Android equivalent of iOS GCD dispatch_group API - android

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.

Related

Why addSnapshotListener is called twice on firestore CollectionReference? [duplicate]

My firestore onSnapshot() function is being called twice.
let user = firebase.firestore().collection('users').doc(userID).onSnapshot
({
next: (documentSnapshot: firebase.firestore.DocumentSnapshot) =>
{
this.userArray.push(documentSnapshot as User);
console.log(documentSnapshot);
//here
},
error: (firestoreError: firebase.firestore.FirestoreError) =>
{
console.log(firestoreError);
//here
}
});
I have also tried subscribing like in https://firebase.google.com/docs/firestore/query-data/listen#detach_a_listener by including user() at the //here comment but to no avail.
How can I modify such that the function only executes one time, i.e. push only one user object per time instead of twice.
I don't know if this is related to your question. If one is using
firebase.firestore.FieldValue.serverTimestamp()
to give a document a timestamp, then onSnaphot will fire twice. This seem to be because when you add a new document to your database onSnapshot will fire, but the serverTimestamp has not run yet. After a few milliseconds serverTimestamp will run and update you document => onSnapshot will fire again.
I would like to add a small delay before onSnapshot fires (say 0,5s or so), but I couldn't find the way to do this.
You can also make a server side function for onCreate event, I believe that would solve your problem. Maybe your userArray.push-action would be more suitable to execute in server side.
Update: To learn more about the behavior of serverTimestamp() and why it triggers the listener twice read this article: The secrets of Firestore’s FieldValue.serverTimestamp() — REVEALED!. Also, the official documentation states:
When you perform a write, your listeners will be notified with the new data before the data is sent to the backend.
In the article there are a couple of suggested solutions, one of which is to use the metadata property of the snapshot to find whether the Boolean value of metadata.hasPendingWrites is true (which tells you that the snapshot you’re looking at hasn’t been written to the server yet) or false.
For example, in your case you can check whether hasPendingWrites is false and then push the object:
if ( !documentSnapshot.metadata.hasPendingWrites ){
// This code will only execute once the data has been written to the server
this.userArray.push(documentSnapshot as User);
console.log(documentSnapshot);
}
In a more generic example, the code will look like this:
firestore.collection("MyCollection")
.onSnapshot( snapshot => {
if ( snapshot.metadata.hasPendingWrites ){
// Local changes have not yet been written to the backend
} else {
// Changes have been written to the backend
}
});
Another useful approach, found in the documentation is the following:
If you just want to know when your write has completed, you can listen to the completion callback rather than using hasPendingWrites. In JavaScript, use the Promise returned from your write operation by attaching a .then() callback.
I hope these resources and the various approaches will help anyone trying to figure out a solution.
REFERENCES:
Events for local changes
The hasPendingWrites metadata property
Snapshot Listen Options
If you need a one time response, use the .get() method for a promise.
firebase.firestore().collection('users').doc(userID).get().then(snap => {
this.userArray = [...this.userArray, snap.doc);
});
However, I suggest using AngularFire (totally biased since I maintain the library). It makes handling common Angular + Firebase tasks much easier.

Is it possible to stop idling for an AsyncTask to complete?

I am trying to set up a test for my project - to test that a progress bar is displayed when my app performs a server request.
The code under test uses an AsyncTask to perform the network call.
I have created a blocking server (MockWebServer) to catch and hold the network call - it receives request but doesn't provide a response until i call ".release()". This allows me to verify before the server response occurs.
My logic flows like this:
// Mock server will catch the next network request
BlockingServer blockingServer = createBlockingServer();
// onResume() activity performs network request and shows Progress Spinner
activityTestRule.launchActivity(null);
// onView() waits on UiController.loopUntilIdle() <- Fails here due to timeout.
onView(withId(progressBar)).check(matches(isDisplayed()));
// Tells the server to respond to the network request
blockingServer.release();
onView(withId(progressBar)).check(matches(not(isDisplayed())));
My problem is that because the Code Under Test uses AsyncTask for the server request, Espresso naturally blocks on the verify call (onView()) in order to wait for the AsyncTask to complete before verifying.
What I need is to temporarily stop Espresso idling while waiting for AsyncTask in order to perform the verify while the server is blocking the app logic flow.
(Changing the Code Under Test is not an option)
Can someone help?
So... this is the answer I've arrived at and some working out behind it:
Espresso (specifically calls to onView(), onData(), injectEvent and Actions) uses UiControllerImpl.loopMainThreadUntilIdle() to wait until all "idle-causing" signals are false. It loops over AsyncTask, CompatAsyncTask and something called dynamicIdle to all be idle.
When this method returns the main flow continues.
loopMainThreadUtilIdle() checks an IdleNotifier to check the idle state of each of those three elements. Obviously if you want to stop espresso waiting for AsyncTask the asyncIdle is of particular interest to you.
The IdleNotifier classes are fed into UiControllerImpl at it's construction - this takes place via dagger so you'll need to look at DaggerBaseLayerComponent which uses Providers to grab the construction arguments and pass them into the UiControllerProvider to construct it.
Everything in all of these classes is locked down very tightly. Method and class visibility is usually protected or package-private and final.
The only way I found was to create my own Espresso.java class (onView() and onData()) which used custom DaggerBaseLayerComponent allowing me to use either: My own Providers or My own UiController.
I found however this doesn't solve the whole problem. There is one more mechanism that needs to be coded around - When you're starting activities they use a waitForIdleSync in the Instrumentation class. Usually this is the Runner which is provided in your gradle file. I created my own AndroidJUnitRunner and provided this in gradle to allow me to return from waitForIdleSync on command.
And finally, in startActivitySync in the Instrumentation base class, it uses an array of ActivityWaiter objects to hold up your launchIntent() calls. I couldn't think of a reasonable way of avoiding this so I cheated and created this method in my Runner:
public void clearActivityWaitQueue() {
Object mSync = Whitebox.getInternalState(this, "mSync");
List mWaitingActivities = Whitebox.getInternalState(this, "mWaitingActivities");
if (mSync != null && mWaitingActivities != null) {
mWaitingActivities.clear();
synchronized (mSync) {
mSync.notifyAll();
}
}
}
It uses PowerMock to give me the convenience Whitebox methods to set internal state of Instrumentation:
// Used to give access to Whitebox
androidTestImplementation 'org.powermock:powermock-reflect:1.6.5'
And that's it! Easy right?
(Please tell me it's easier than this and how!!)

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.

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

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.

Categories

Resources