I know that to interact from Javascript to Java you have to inject a Java object using the addjavascriptInterface method in webview.
Here is the problem I am facing.
-
I register a java object using
addJavascriptInterfacemethod to be available in my JS. -
I inject few JS in the webview using
webview.loadURL("javascript:XXX"); -
I send a JS event when I am done with injecting the JS.
The problem is that if immediately after step 1, if I execute the following Javascript:
mWebView.loadUrl("javascript:if(window.myobject) console.log('myobject found---------'); else {console.log('myobject not found----');}");
I get “myobject not found” in my console’s log.
I want to know that if there is some time before I can access my object and if so, how do I get to know how much time should I wait to call my object?
Yes, I think there is a delay, because
WebView.addJavascriptInterfacewill run in the WebView’s internal worker thread. Perhaps you’ve thought about this, and realized that WebView has to maintain at least one worker thread to do asynchronous network IO. Maybe you also noticed these threads in DDMS while using a WebView.It turns out that it also uses a thread to do work for a number of other public methods. I really wish the docs from Google made this clearer! But I hope I can help and show you how I tried to confirm this for myself.
Follow me as I take a look at the source for WebView. It’s reasonably readable, even if you can’t follow exactly what’s going on, it’s possible to trace through answer some questions with respect to threads.
You can download the Android framework source through the SDK manager tool, but it’s also mirrored on Github, so that’s what I’ve linked to here. I guessed and picked a tag that’s close to some version of ICS. It’s not hard to find
WebView.addJavascriptInterface. I just Googled “WebView.java site:github.com/android”.The method
WebView.addJavascriptInterfacesends a message to an instance ofWebViewCore:In
WebViewCore.javathere are a bunch of overloaded methods calledsendMessage, but we don’t really need to know which exactly is being called, since they do pretty much the same thing. There’s even a nice comment to give us a hint that we’re in the right place! All of them are delegating to an instance ofEventHubwhich is some inner class. This method turns out to be synchronized, and is sending a message to an instance ofHandler, which is a good indication that this is probably running in another thread, but for completeness sake, let’s find out!That
Handleris instantiated inEventHub.transferMessageswhich is called fromWebViewCore.initialize. There are a few more hops here, but eventually I found out that this is called fromruninWebCoreThread(subclass ofRunnable), which is instantiated along with a newThreadright here.What an adventure! So, even though I really can’t say for sure what’s going on with all these moving parts, I am pretty confident to say that this method is not synchronous, and sends a message to the WebView’s worker thread. I hope that makes sense!
Unfortunately, I don’t know the answer to this. I was researching this exact issue and found this question on StackOverflow in the course of my Googling. I think you have the following options, some of which are nicer or easier than others:
1) Just
Thread.sleepfor 100 ms or something betweenaddJavascriptInterfaceandloadUrl("javascript:..."). Blech, I don’t like this, but it is potentially the easiest.2) Another possibility is that you could call
WebView.loadUrlwith a snippet of JavaScript that specifically tests if the interface is set, and catches the ReferenceError that is thrown if it’s not set yet. However, as you might have guessed, this kind of involves adding a JavaScript interface to the WebView!3) Call
WebView.setWebChromeClientinstead, and catch JavaScript’salert()orconsole.loginstead. From my experiments, this method is synchronous, so there is no delay. (I have confirmed this in source, but I’ll leave details as an exercise for the reader) You should probably come up with some special string to callalertwith and check for it insideonJsAlert, so you aren’t just catching allalert()s.Sorry for the length of this answer, I hope that helps. Good luck!