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();
}
});
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.
}
}
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;
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With