How to implement requests queue? - android

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();
}

Related

How to get WorkManager Status Synchronously

I am working with WorkManager Alpha 05.
I'm developing a Service that enqueues task on demand of other applications.
It has two methods:
createTask (Create a new task, given a name and a set of data, it returns and ID)
checkTaskStatus (The application asks the services given a ID, the status of the task)
The communication is done via bound services using messages. That means both client and services has the correct implementations to communicate information.
Method 1 is working fine.
I have problems with method 2.
WorkManager.getInstance().getStatusById(taskID)
.observe(LifecycleOwner, Observer {
status -> if (status !=null){
val myResult = status.state.toString()
statusString = myResult
Log.d("Task Status",myResult)
}
})
The observer is logging the status correctly, but I can't send back that message to the client. Is there a way to check the status in a sync way?
I don't really need to have the task attached to a LiveData.
Seems like SynchronousWorkManager was removed on October 11:
Removed WorkManager.synchronous() and WorkContinuation.synchronous() and all related methods. Added ListenableFuture as the return type of many methods in the API. This is a breaking API change.
How to use ListenableFuture:
You can now synchronously get and observe by using ListenableFutures. For example, WorkManager.enqueue() used to return void; it now returns a ListenableFuture. You can call ListenableFuture.addListener(Runnable, Executor) or ListenableFuture.get() to run code once the operation is complete.
More info can be found here.
The WorkManager instance has a synchronous method which returns the SynchronousWorkManager, This will give you a set of methods to perform synchronous operations. Take into account that this is meant to be used in a background thread.

Test UI while idling resource is busy

I am new at android testing and I'm running into a problem. I am using RxJava and to test the UI I am using an IdlingResource. While idling resource is busy i cannot test UI.
For example: I have a button. onClick I'm doing a request. While requesting the button disables. After request the button is in enabled state. I want to test the following 3 steps:
Button is enabled before request
Button is disabled while requesting (onCLick)
Button is enabled when requesting ends and response message returns
I would be very very happy if you can help me in this issue...
If you need more information about my issue let me know it. I will edit my post
As I understood, you're trying to test your UI. If so, please, make sure, that you do it in right way:
1). You don't do REAL request.
Please, understand, that your test must always have same behaviour in similar situations. In other words, it must give same result, you're passing same input parameters.
Your input parameters for now:
1.1). Button is enabled before request
1.2). Button disabled during the request
1.3). Buttons enabled after request
As you can see from this list, you don't need to do a real request. It doesn't matter for you, what server will return you (error or success). You even don't need a server for this. All what you need, is just "something", that behaves like a real server. In other words, you have to mock your API client.
I suppose that you're using retrofit. If no, you have to create the interface wrapper for your client. If you're using retrofit, you just need to mock your interface.
Let's suppose, you have next interface:
public interface ApiClient{
#GET("/items")
Observable<MyResponse> doSomeRequest();
}
How do you usually create your API client:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.build();
ApiClient service = retrofit.create(ApiClient.class);
How you should do it in tests:
import static org.mockito.Mockito.*;
and in test method:
ApiClient apiMock = mock(ApiClient.class);
when(apiMock.doSomeRequest())
.thenReturn(Observable.just(fakeResponse));
or
ApiClient apiMock = mock(ApiClient.class);
when(apiMock.doSomeRequest())
.thenReturn(Observable.defer(new Func0<Observable<MyResponse>>() {
#Override
public Observable<MyResponse> call() {
try{
Thread.sleep(2 * 1000) //2 seconds
}catch(Exception e){
return Observable.error(e);
}
return Observable.just(fakeResponse);
}
}));
P.S. Retrofit adds .subscribeOn(Schedulers.io()) to all Observable's by default. This mocked object doesn't do it. So, please, don't forget to add .subscribeOn(Schedulers.io()) in your code, or apply it to the result of Observable.defer(...)
In code above it will look like:
when(apiMock.doSomeRequest())
.thenReturn(Observable.defer(...).subscribeOn(Schedulers.io()));
And you should pass apiMock to Activity / Fragment which you try to test.
How to do it? See #2.
2). Use DI (dependency injection)
I will not write a lot about it.
I just recomend you to read the documentation on http://google.github.io/dagger/
And especially, how to organise project in way, when you can use real implementaions for production, and mock implementations for testing:
http://google.github.io/dagger/testing.html
In other words, when you're going to build app for usage, you provide real dependencies(in your case it will be real implementation of ApiClient), and when you're going to test some UI or business logic, you pass mock dependencies, which have behaviour specified before the test by you.
This is all, what I wanted to tell you. Hope this helped, and let me know if you have any other questions.
Small addition to Alexander's answer. I would use a Subject for "mocked" api. This allows you to control execution.
//setup your test
Subject<Response,Response> stubResponse = AsyncSubject.create();
ApiClient apiMock = mock(ApiClient.class);
when(apiMock.doSomeRequest()).thenReturn(stubResponse.asObservable());
//check first condition that button is enabled before executing action
//click on button
//test your second condition that button is disabled while waiting for response
stubResponse.onNext(fakeResponse); //return fake response
stubResponse.onCompleted();
//test your third condition that button is enabled when you get response back
Remark. Never use sleep in your test. It will slow down your tests and add flakiness.

Android equivalent of iOS GCD dispatch_group API

I come from an iOS background and I'm new to Android.
Is there an efficient and fast way to make the same network API call but with different parameters each time where the parameters are stored in an array. I would only want to return when all the network API calls have completed, but I don't want any of the api calls in the loop to block other api calls in the loop.
I basically want the equivalent of this Swift code. Basically the function below won't return until all network calls getData has either succeeded or failed. How would I accomplish the same thing below in Android?
func getDataForParameters(array: NSArray) {
let group = dispatch_group_create()
for (var i = 0; i < array!.count(); i++) {
let param = array![i]
dispatch_group_enter(group)
getData(param, success: {
() in
dispatch_group_leave(group)
}, failure: {
() in
dispatch_group_leave(group)
})
}
dispatch_group_wait(group, DISPATCH_TIME_FOREVER)
}
You have many ways to achieve this.
You can use Thread.join() in case you are using threads
you can use 3rd party libraries like RxJava.
you can write your own event dispatcher here is an ugly example
This answer also covers your question Callable and Future
If the network calls in the loop shouldn't block other network call then you should make the network calls asynchronously.
You can use google's volley network library to make the network calls and they execute asynchronously. Follow the below link for volley
https://developer.android.com/training/volley/index.html.
if you can implement a counter which increments on either success or failure call back you can use that variable to determine whento return back to your calling method.
Since the network calls are being made asynchronously you need to write a callback interface which should be triggered once your counter condition is met so that it will send a callback to the called method. you can find lot of examples on how use callback mechanism in Android. Callback functions are like Delegate functions in IOS.
I Hope this helps.

How to test Android UI using IdlingResource when using Retrofit network requests

I am writing integration tests that perform actions in the UI which start network calls using Retrofit.
I know I need to implement a CountingIdlingResource, but I want to do it the correct way (and not reinvent the wheel if it has already been done).
Has anyone implemented an IdlingResource in their app's Espresso test suite to wait while network requests execute?
More info here.
The most straightforward solution for this: is to basically swap out Retrofit's Thread-pool executor with an AsyncTask one (as recommended by the very helpful Nick from that linked Google group discussion). I do this like so:
new RestAdapter.Builder()
.setEndpoint(LOCLSET_SERVER_URL)
.setExecutors(AsyncTask.THREAD_POOL_EXECUTOR,
new MainThreadExecutor())
.build();
I'm not sure if this is the most appropriate solution, but it's the quickest most sane one that I could get working. Bare in mind the caveat, that this works only for ICS+.
If you're using RxJava Observables with Retrofit 2.0 then you can use .subscribeOn(Schedulers.from(AsyncTask.THREAD_POOL_EXECUTOR)) instead of .subscribeOn(Schedulers.io()) and everything works fine!
OR alternatively you can override RxJavaSchedulersHook, allowing you to just make the change in one location. For example:
public MySuperCoolClient() {
if (BuildConfig.DEBUG) {
configureIoSchedulerToUseAsyncTaskThreadPool();
}
this.restApi = new Retrofit.Builder()
.baseUrl(Parameters.endpoint)
.addConverterFactory(GsonConverterFactory.create(gsonBuilder()))
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build()
.create(RestApi.class);
}
private void configureIoSchedulerToUseAsyncTaskThreadPool() {
RxJavaPlugins.getInstance().registerSchedulersHook(new RxJavaSchedulersHook() {
#Override
public Scheduler getIOScheduler() {
return Schedulers.from(AsyncTask.THREAD_POOL_EXECUTOR);
}
});
}
note answer below is based on Retrofit 1.6.1 - will update for newest version. Retrofit 1.9.0 does not allow you to set the HttpExecutor via the RestAdapter.Builder any longer
The accepted answer is a step in the right direction but it makes me feel uncomfortable. In practise you would either need to set the AsyncTask.THREAD_POOL_EXECUTOR for live & tests builds OR test builds only.
Setting for both would mean all your network IO pooling will depend on the aysnc queue implementation, which became serial by default for apps with target versions ICS+
Setting for tests only would mean that your test build is different from your live build, which imho is not a great place to start testing from. Also you may encounter test problems on older devices due to async pool changes.
It is rightly mentioned above that Espresso hooks into AsyncTask.THREAD_POOL_EXECUTOR already. Lets poke around...
How does it obtain this?
ThreadPoolExecutorExtractor
Who/what uses this?
BaseLayerModule has provideCompatAsyncTaskMonitor(ThreadPoolExecutorExtractor extractor) which returns an AsyncTaskPoolMonitor
How does that work? Have a look!
AsyncTaskPoolMonitor
Where is it used?
UiControllerImpl has method loopMainThreadUntilIdle() which manually calls asyncTaskMonitor.isIdleNow() before checking any user registered idlingResources with idlingResourceRegistry.allResourcesAreIdle()
Im guessing with Retrofit we can use the RestAdapter.Builder.setExecutors(...) method and pass in our own instance (or version) of the AsyncTaskPoolMonitor using the same http Executor that Retrofit is init on Android with
#Override Executor defaultHttpExecutor() {
return Executors.newCachedThreadPool(new ThreadFactory() {
#Override public Thread newThread(final Runnable r) {
return new Thread(new Runnable() {
#Override public void run() {
Process.setThreadPriority(THREAD_PRIORITY_BACKGROUND);
r.run();
}
}, RestAdapter.IDLE_THREAD_NAME);
}
});
}
(from here)
And wrap this in the IdlingResource interface to use in our tests!!
The only question in that as Retrofit makes the callback using a separate Executor on the mainThread that relies on the main Looper, this may result in problems but Im assuming for the moment that Espresso is tied into this as well. Need to look into this one.
Retrofit 2 uses okhttp3, which, in turn uses a dispatcher. Jake Wharton created this library that monitors the dispatcher for idleness. You would create the IdlingResource like this:
IdlingResource resource = OkHttp3IdlingResource.create("OkHttp", okHttpClient);
Be aware that this might not suffice to be used for successful Espresso tests (I've tried) because the IdlingResource might say it's idle just before or after the http call, and your Espresso test would execute and fail instead of waiting.
My recommendation for these cases is to use a thread pool to launch any background tasks and make an IdlingResource wrapping this thread pool. See this article for more info: https://medium.com/#yair.kukielka/idlingresource-dagger-and-junit-rules-198e3ae791ff
If you're using Asynctasks, you don't need to do anything because Espresso already knows how to wait for them: it uses AsyncTaskPoolMonitor which is a wrapper around the Asynctask thread pool.
If you're using you're own thread pool (that was my case), you could use this class that will wrap your executor so that Espresso can know when it's idle.
This great post explains how it works. I tried in my project and it's great! Using dagger, I get a hold of my thread pool and wrapped it in an IdlingResource in a junit #rule.

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