Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

latch(used for awaiting async response) freezes the WebView (and the UI)

I have an app that displays a webview on the whole layout. Sometimes I need to call an async method, that async operation is done by a 3-party sdk, and then I wait for it for a while until I get the response to a designated listener. I have solved it with a latch - once the response is received in the listener, I countDown the latch and then the initiating method can continue with this response. Unfortunately, when I do this, the WebView is stuck. I imagined the native UI would be stuck , but I didn't expect the webview to get frozen as well. How can that be overcome?

To make it clearer, here is an example. I need the ajaxFunc to wait until MyAsyncListener gets a certain response, and then return this exact response.

part of JS I inject to the webview :

var response = jsHandler.ajaxFunc(request.data);

I have a variable global variable called response.

public class JavaScriptInterface
{
     @JavascriptInterface
     public String ajaxFunc(String data)
     {
         return MyUtils.handleData( data );
     }
}

the handleData method :

 public String handleData( String data )
 {
     SomeClass.startAsyncRequest(); // starts the async request, response is to come at the listener's callbacks .

     latch = new CountDownLatch(1);
     try {
         latch.await(30, TimeUnit.SECONDS);
     }
     catch (InterruptedException e) {
         e.printStackTrace();
     }

     return response;
   }

now, once the handleData function calls a function, that does something asynchronously, the async function then returns some answer inside some Listener :

myAsyncListener = new MyAsyncListener() {
    @Override
        public Response handleEvent(CustomEvent event) {
            //Now I need to return this 'event' back to the handData/ajaxFunc function <-- 

            response = event.getName();
            latch.countDown();

        }
    });
like image 320
BVtp Avatar asked Mar 29 '17 10:03

BVtp


2 Answers

I need the ajaxFunc to wait until MyAsyncListener gets a certain response, and then return this exact response.

Since you are dispatching an async request, you shouldn't expect the response to arrive in the same method call you made to start the async code. If you do this, the caller thread will block until the async response arrives, that is, it won't be async at all.

You can redesign your code to something like this:

public class JavaScriptInterface {
    @JavascriptInterface
    public void ajaxFunc(String data) {
        // draw a waiting animation or something
        MyUtils.handleData(data);
    }
}
public void handleData(String data) {
    SomeClass.startAsyncRequest();
}
myAsyncListener = new MyAsyncListener() {
    @Override
    public void handleEvent(CustomEvent event) {
        // Do what you need to do
        // (update UI, call javascript etc.).
        //
        // Mind the thread that will execute this callback
        // to prevent multithreading issues.
    }
}
like image 124
nandsito Avatar answered Oct 22 '22 22:10

nandsito


You have put CountDownLatch inside your code which will cause blocking of your main thread. Thats the reason of lag on UI.

For better you should show some ProgressDialog before doing main work of Async task which generally written in doInBackground() method of Async task and hide it after completing your task, Async provide onPostExecute() method for that purpose.

If you need to put some timeout like your CountDownLatch is doing, you can write timer in doInBackground() of Async. That timer will stop your current Async will allotted time is elapsed.

Update, you could use EventBus for your purpose -

EventBus works on Publish/Subscribe pattern. You can subscribe in Activity and publish result from JavaScriptInterface.

Add it in gradle using -

compile 'org.greenrobot:eventbus:3.0.0'

In your Activity start subscribing, generally subscriptions done in onCreate method of Activity -

EventBus.getDefault().register(this);

Also remove subscriptions in onDestroy method of Activity or if your work is done.

EventBus.getDefault().unregister(this);

To listen subscription use below code in your Activity -

@Subscribe(threadMode = ThreadMode.MAIN)  
     public void onMessageEvent(MyMessageEvent event) {
     // Do your work after getting result from ajaxFunc method
 };

You just have to post message notification from your method now and remove CountDown latch from your code if it is not needed somewhere else.

myAsyncListener = new MyAsyncListener() {
    @Override
        public Response handleEvent(CustomEvent event){ 
        response = event.getName();
        // Create object of message and post it
        final MyMessageEvent msgEvent = new MessageEvent(response);
        EventBus.getDefault().post(msgEvent);
    }
});

MyMessageEvent.class

import android.support.annotation.NonNull;

public class MyMessageEvent {

    private String mMessage;

    public MyMessageEvent(@NonNull final String message) {
        mMessage = message;
    }

    public String getMessage() {
        return mMessage;
    }

}
like image 34
Rahul Avatar answered Oct 22 '22 20:10

Rahul