Android notify every time the value on Retrofit call changes - android

As is title I want to be able to notify me every time the value of Retrofit call changes. Is that possible with calling this code only once in onCreate()? The scenario is like this. I build an RPG game. For example want to be notified about amount of gold for specific user at application start (this works). Then user kills a monster and gain some coins (another Retrofit call is made) so amount of gold on server changes and now I want to be also notified (here is where it doesn't work).
ApiInterface:
#GET("/getGold")
Observable<String> getGold(#Query("name") String name);
Activity:
#Override
protected void onCreate(Bundle savedInstanceState) {
(...)
Api.getClient1Or2().getGold("Admin")
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(string -> Log.d("string", string));
}
How to modify this code then?
Thank you in advance!

Even though getGold is Observable, it will run request only once and emit a single item.
You need to either receive Gold from your WebSocket message (preferred), or use WebSocket message as a trigger to refresh Gold by subscribing to getGold again (not preferred because you can send your Gold directly in your WebSocket message).
Of course, you would need your backend to expose a WebSocket endpoint for you with the contract for constantly updating your Gold.
You could also use push notification as a trigger for a refresh, but they have quotas and they are built on WebSockets anyway.

Related

How to implement requests queue?

I have an application which uses methods from third-party SDK (async methods to make HTTP-requests to remote server) and Retrofit2 + OkHttp + Rx to access this server directly. It looks like this:
new SdkRequest("some", "arguments", "here")
.setCompleteListener(this::onGetItemsComplete)
.setErrorListener(this::onGetItemsError)
.getItems(); // Here is can be differents methods (getShops, getUsers, etc)
ApiManager.getInstance()
.getApi()
.addAdmin("some", "another", "arguments", "here") // Methods which not presented in SDK we should call directly
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this::onAddAdminComplete, this::onAddAdminError);
I need that the all requests (from SDK and from Retrofit) with limit - 5 request in 1 second max. When this limit is exceeded request should wait before continue.
How to implement it? The first thing that comes to mind - add Service and BroadcastReceiver. But it not so lazy for me: I should listening this BroadcastReceiver in every activity fragment, and here is no so useful callbacks with this implementation. Is it possible to implement it with Rx (wrap SDK methods also) and use lambdas?
You can do this easily, without extra threads or services, with a semaphore.
Semaphore s = new Semaphore(40);
...
s.acquire();
try {
dispatcher.dispatchRpcRequest(); // Or whatever your remote call looks like
} finally {
s.release();
}

Using RxJava to temporarily save variables during configuration changes

I am using RxJava on an Android project and want to make sure I'm implementing something correctly.
I am using an Observable to login to a server. After the login occurs I may want to save the username on the client side so I'm using the doOnNext() operator to do this. Here's an example of what this looks like:
Observable<Response<Void>> doLogin(Observable<Response<Void>> retainedObservable, Subscriber subscriber, String username, String password, boolean saveUsername) {
if (retainedObservable == null) {
retainedObservable = networkLayer.loginWithcredentials(username, password)
.doOnNext(new Action1<Response<Void>>() {
#Override
public void call(Response<Void> voidResponse) {
if (saveUsername) {
databaseLayer.saveUsername(username).subscribe();
} else {
databaseLayer.saveUsername(null).subscribe();
}
}
})
.cache();
}
mSubscription = retainedObservable.subscribeOn().observeOn().subscribe(subscriber);
return retainedObservable;
}
If the device goes through a configuration change before the login finishes I want to be able to resubscribe to the observable that was running so that I don't have to ask the user to reenter their credentials of press the login button again.
To accomplish this I initially call doLogin(...) and temporarily save the returned observable in a data holder class. If the device goes through a configuration change I can grab the already created observable from the data holder class and resubscribe to it. This works great, my only hang up is that I need to also temporarily save off the username, password and saveUsername flag. This is easy enough to do, however I'm wondering if there's a way I can leverage RxJava to hold these values in the Observable and still have access to them when I need them in doOnNext(...). If I don't do this and just resubscribe to the Observable then when doOnNext(...) runs I won't have values for the saveUsername flag or the username which is obviously bad.
It would be really nice to only have to retain the Observable and somehow have it retain the username, password and saveUsername flag. Is there a way to accomplish this in RxJava or is the way I'm currently doing it the way it needs to be done if I want those variables to be retained during a configuration change?
Essentially, yes. RxJava uses a functional, stateless API, so you're not really able (or supposed) to attach additional data to an observable aside from the values it returns.
As with functional languages, there are generally two ways you can go about this:
You can either capture additional values in a closure, similar to how
you are doing it now. But obviously those values will only be
accessible within the scope of said closure, so you can't return an
Observable from a method and still access variables that were used to
create it later.
If you do need to access that data at a later point, the usual approach is to create some kind of result object that contains all your data. For example a "LoginResult" class, that contains your response as well as the original login data.

How to get collection from Meteor server with Android DDP?

Let me to start explain my problem. There is repository with some explanations, but there are no methods how to get collection or json file from Meteor server(only insert). Also author did not explain properly methods onDataChanged, onDataAdded etc.
public class Login extends Activity implements MeteorCallback{
public static Meteor mMeteor;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
mMeteor = new Meteor(this, "some_socket_it_doesn't_matter");
mMeteor.setCallback(this);
}
}
public class ListOfElements extends ListFragment implements MeteorCallback{
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
String subscriptionId = Login.mMeteor.subscribe("notifications");
Log.d("Log", subscriptionId);
}
}
I didn't understand how i have to use subscription or how to get collection from server. Why there are only insert methods in github repository and no get? I really have no idea how make the code to get collection, use subscribe and so on. There are no any understandable explanations in the network. Please, can you help me with this by explaining how to realize getting, subscribing in this code.
There are two special things about Meteor: It works asynchronously and it has been designed specifically for real-time applications. Thus it has a few different concepts for retrieving data and for some other tasks.
In a synchronous application, you would just call insert(...) and immediately get the method's return value, e.g. a boolean value for success/error or a numeric value for the number of rows that have been inserted.
You would call get(...) and immediately receive a collection of rows as the method's return value.
But in Meteor, everything is asynchronous. This means that you get the results not immediately, but a few (milli)seconds later, in a callback method.
When you call insert(...), this is not so important, as you have noticed. You just call this method and often forget about the result, i.e. you don't wait and check for the result because insertions are usually successful. But this method is still asynchronous and you could (and sometimes should) listen for the result which will arrive a few (milli)seconds later, again.
When you want to call get(...), this would be possible in theory, with the important point again being that it's asynchronous. So you would say "get me all chat messages from the last 5 minutes". There would be no result or return value, as usual, but the result would arrive a short time later, asynchronously, in a callback method that you define. This is what onDataAdded(...), onDataChanged(...) and onDataRemoved(...) are for.
Now it's not clear, yet, why you can't call get(...) and wait for data to arrive in those methods.
The answer to that question is Meteor being designed for real-time applications. This is why you can't say "get me all chat messages from the last 5 minutes". Instead, you have to say "I want to subscribe to all chat messages from the last 5 minutes and always be updated about changes".
So, in Meteor, you subscribe to data sets instead of requesting them via get(...).
All in all, this means the following:
If you want to get some messages, you subscribe to your data set that holds those messages.
When the initial rows are sent (!) and whenever new rows are added to the collection, you receive those in your onDataAdded(...) callback. When rows are modified, you receive those changes in your onDataChanged(...) callback. And, finally, when rows are deleted, you are informed about those deletions in your onDataRemoved(...) callback.
When you don't want to get updates for your data set anymore, you unsubscribe from that set. This is optional.
With the Android-DDP library in your Android application, it translates to the following:
final String subscriptionId = mMeteor.subscribe("chats");
public void onDataAdded(String collection, String docID, String json) { ... }
mMeteor.unsubscribe(subscriptionId);
As you can see, what you have to learn is really Meteor and not the library Android-DDP. Meteor has some new concepts that one has to understand. But when you know how Meteor works, translating those things to Android-DDP is really simple and only a matter of looking up the method names.

Android - sending SeekBar values via TCP

I'm currently developing an android app that communicates with some other device, that acts like a server. Basically to build the application's views, I first have to send a query via a TCP connection to the server to get the info. I (successfully) execute these queries with the help of an async task:
private class TCPQuery extends AsyncTask<String, String, String> {
#Override
protected String doInBackground(String... params) {
//connect the socket send the query and receive feedback
}
#Override
protected void onPostExecute(String result) {
//parse server feedback and build the view
}
}
This approach works fine when it comes to single queries that are made only a couple of times during the application's lifetime. What I have trouble implementing is the following:
a certain view in the application, contains seekbars. So basically, every change of the seekbar value (every time the onProgressChange method fires) must be sent to the server(no feedback this time), so it can keep track of the actual values.
How would you go about implementing this? Of course, no networking in android may be done on the main thread. But here establishing a connection, sending a message and closing the connection every time the value changes is not acceptable. Sliding the bar only a little already results in a dozen such calls in a split second.
I've tried approaching this problem by implementing a service. The service had its own socket to communicate with the server. I would connect the socket to the server and keep it open, so that I would be able to call the service's send method any time a seekbar change has been made. But that seemed to interfere with the other queries I mentioned before (the ones executed with async tasks). I couldn't connect one while the other was active. Now I'm not sure whether my service implementation was just bad, or if I am misunderstanding a crucial networking concept here.
I have thought of only sending the data onStopTrackingTouch, but that is not really what I am after. Any help would be very much appreciated!
Use the system clock to check when the last query has been sent, and don't send another until a certain time has elapsed.
You can change seekbar's value as you want, but the query will be sent only every X milliseconds.
static long sendInterval = 600; //milliseconds
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
long nextSend = 0;
}
#Override
public void onProgressChanged(......) {
if (nextSend < uptimeMillis()) {
...send the query and parse feedback...
nextSend = uptimeMillis() + sendInterval ;
}
Start with nextSend = 0, so the first time the query will be sent immediatly.
Choose sendInterval value according to server's response time. Start with a high value and decrease until you see that all is working well.
If the query itself and the response are small (a few bytes) consider using UDP instead of TCP, it's faster and you can use lower values of sendInterval.
Other way to do it, different and maybe better:
since the response time may vary much depending on network traffic, query complexity and server load, you can use a boolean flag. Set it to False before sending the query, set it to True after parsing the response. Use it in an If statement:
#Override
public void onStartTrackingTouch(SeekBar seekBar) {
boolean readyForQuery = true;
}
#Override
public void onProgressChanged(......) {
if (readyForQuery) {
readyForQuery = false;
<...asyncronous send the query, parse feedback and set readyForQuery=true;...>
}
Consider also the worst case: when the server is down and will not respond at all to the query.
Take care to find a way to set the flag True after a reasonable amount of time and/or when the query code generates an exception, otherwise you won't get further responses even when the server goes up again.

Fetching big amount of data, what is the best way to go?

I have severals URLs I need to get data from, this should happen in order, one by one. The amount of data returned by requesting those URLs is relatively big. I need to be able to reschedule particular downloads which failed.
What is the best way to go? Shall I use IntentService, Loaders or something else?
Additional note: I would need not only to download, but also post process the data (create tables in db, fill it with data, etc). So DownloadManger can't be of help here.
I would use an IntentService.
It has a number of advantages that are suitable for your needs, including being able to download the data without your application running and supporting automatic restart of the service using setIntentRedelivery().
You can set a number of identifiers for the particular job, you need to perform using Intent extras, and you can keep track of the progress using SharedPreferences - that way you can also resume the work if it's been cancelled previously.
The easiest way is probably to use the system DownloadManager http://developer.android.com/reference/android/app/DownloadManager.html
(answering from my phone, so please excuse the lack of formatting)
I would suggest a service for this. Having service resolves many problems
It would allow reporting of progress asynchronously to the application so you can enable or disable a specific gui in application based on the download status of data
It will allow you to continue the download even if the user switches to other application or closes the application.
Will allow you to establish independent communication with server to prioritize downloads without user interaction.
Try a WakefulIntentService for creating a long-running job that uses wakelocks to keep your task alive and running https://github.com/commonsguy/cwac-wakeful .
Also, if your whole app process is getting killed, you may want to look into persisting the task queue to disk, using something like Tape, from Square
I think the way to go is loading urls in an array, then starting an AsyncTask, returning a boolean to onPostExecute indicating if the operation has success or not. then, keeping a global int index, you can run the AsyncTask with the next index if success, or the same index otherwise. Here is a pseudocode
private int index=0;
//this array must be loaded with urls
private ArrayList<String> urlsArray;
new MyDownloaderAsyncTask().execute(urlsArray.get(index));
class MyDownloaderAsyncTask extends AsyncTask<String,String,Boolean>{
#Override
doInBackground(String... input){
//downlaod my data is the function which download data and return a boolean
return downloadMyData();
}
#Override
onPostExecute(Boolean result){
if(result)
new MyDownloaderAsyncTask().execute(urlsArray.get(++index));
else
new MyDownloaderAsyncTask().execute(urlsArray.get(index));
}
}
hope this help
I have just completed an open source library that can do exactly what you need. Using droidQuery, you can do something like this:
$.ajax(new AjaxOptions().url("http://www.example.com")
.type("GET")
.dataType("JSON")
.context(this)
.success(new Function() {
#Override
public void invoke($ droidQuery, Object... params) {
//since dataType is JSON, params[0] is a JSONObject
JSONObject obj = (JSONObject) params[0];
//TODO handle data
//TODO start the next ajax task
}
})
.error(new Function() {
#Override
public void invoke($ droidQuery, Object... params) {
AjaxError error = params[0];
//TODO adjust error.options before retry:
$.ajax(error.request, error.options);
}
}));
You can specify other data types, which will return different object types, such as JSONObject, String, Document, etc.
Similar to #Murtuza Kabul I'd say use a service, but it's a little complicated than that. We have a similar situation related to constant internet access and updates, although ours places greater focus on keeping the service running. I'll try to highlight the main features without drowning you in too much detail (and code is owned by the company ;) )
android.permission.RECEIVE_BOOT_COMPLETED permission and a BroadcastReceiver listening for android.intent.action.BOOT_COMPLETED to poke the service awake.
Don't link the service to the Activity, you want it running all the time. eg we call context.startService(new Intent(context.getApplicationContext(), OurService.class))
The service class is just a simple class which registers and calls an OurServiceHandler (as in our case we fire off repeated checks and the Handler manages the 'ticks')
We have an OurServiceRunnable which is a singleton which is checked and called by the Handler for each test. It protects against overlapping updates. It delegates to an OurServiceWorker to do the actual lifting.
Sounds heavy handed, but you want to ensure that the service is always running, always ticking (via the Handler) but only running a single check at a time. You're also going to run into database issue if you use the standard SqlLite DbHelper paradigm, as you can't open the DB on multiple threads and you definitely want the internet access off the main thread. Our hack was a java.util.concurrent.locks.ReentrantLock protecting access to the DB, but you could probably keep DB access on the UI thread and pass DB operations via the Handler.
Beyond this it's just a matter of keeping the downloads atomic in terms of "get task, download task, complete task" or enabling it to pick up from a failed state eg downloaded OK, attempt to complete.
You should take a look at the volley library :
http://www.javacodegeeks.com/2013/06/android-volley-library-example.html
There is also an interesting video of the author that took place at google io 2013 :
http://www.youtube.com/watch?v=yhv8l9F44qo
Mainly because it eases the process of managing a lot of these fastidious tasks that are connection checking, connection interruption, queue management, retry, resume, etc.
Quoting from the javacodegeeks "Advantages of using Volley :
Volley automatically schedule all network requests. It means that Volley will be taking care of all the network requests your app executes for fetching response or image from web.
Volley provides transparent disk and memory caching.
Volley provides powerful cancellation request API. It means that you can cancel a single request or you can set blocks or scopes of requests to cancel.
Volley provides powerful customization abilities.
Volley provides Debugging and tracing tools"
Update from dennisdrew :
For large file, better use a variant of volley which authorize using another http client implementation. This link gives more details :
The volley article about this modification :
http://ogrelab.ikratko.com/android-volley-examples-samples-and-demos/
The github file detail :
https://github.com/ogrebgr/android_volley_examples/blob/master/src/com/github/volley_examples/toolbox/ExtHttpClientStack.java
public class FetchDataFromDBThread implements Runnable {
/*
* Defines the code to run for this task.
*/
#Override
public void run() {
// Moves the current Thread into the background
android.os.Process
.setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND);
FetchDataFromDB();
}
}

Categories

Resources