Android Question WebView + Javascript injection launching on different threads

Charles Bib

Member
Licensed User
Running into a problem in that my code works in debug, but not in release. b4a980

I have an activity that uses webview extras to inject javascript and get the source HTML. It works great in debug. However, the routine webpagehtml crashes in release mode with the error below. So I have two questions. Why the difference between debug and release? Second, how can I restructure the code to make this work. I did research and discovered the problem can occur in pure Java, and their solution is to launch the webview.loadurl on the UI thread. Can this be done in B4A?

Help!


Pure Java solution webview.post:
private void test(final String s) {
        webView.post(new Runnable() {
            public void run() {
                webView.loadUrl("javascript:" + s + ";");
            }
        });
        System.out.println("javscript done..");
    }




java.lang.RuntimeException: java.lang.Throwable: A WebView method was called on thread 'JavaBridge'. All WebView methods must be called on the same thread. (Expected Looper Looper (main, tid 1) {fd95b0d} called on Looper (JavaBridge, tid 3268) {997c056}, FYI main Looper is Looper (main, tid 1) {fd95b0d})




Activity Code:
Sub WebView1_PageFinished (Url As String)

    ' Call the javascript injection routine to get the HTML
    WebView1.JavaScriptEnabled = True
    ExtractHTMLViaJavascript

End Sub


Sub ExtractHTMLViaJavascript
    
    ' Create an outerHTML directive and send it to webpagehtml()

    Dim Javascript As String
    Javascript = "B4A.CallSub('webpagehtml', false, document.documentElement.outerHTML)"
    WebViewExtras1.executeJavascript(Javascript)
    
    ' Extracted value ends up in webpagehtml() via the callback
    
End Sub



Sub webpagehtml (strhtml As String)

    ' Capture the web page HTML as a return value from the injected Javascript
    
    If CheckForNoVehiclesFound (strhtml) = True Then

        WebView1.Visible = False
        
    Else

        WebView1.Visible = True
    
    End If
    
End Sub





Sub ExtractHTMLViaJavascript
  
    ' Create an outerHTML directive and send it to webpagehtml()

    Dim Javascript As String
    Javascript = "B4A.CallSub('webpagehtml', false, document.documentElement.outerHTML)"
    WebViewExtras1.executeJavascript(Javascript)
  
    ' Extracted value ends up in webpagehtml() via the callback
  
End Sub
 

JohnC

Expert
Licensed User
Please include a copy of the log when it crashes.
 
Upvote 0

Charles Bib

Member
Licensed User
Keep in mind this is a larger app, but the code posed above crashes the minute I try to reference the webview, yet doesn't in Debug mode.



Log:
** Activity (vehiclestatusshawgmc) Resume **
vehiclestatusshawgmc_webpagehtml (java line: 714)
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
    at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:7542)
    at android.view.ViewRootImpl.recomputeViewAttributes(ViewRootImpl.java:3665)
    at android.view.ViewGroup.recomputeViewAttributes(ViewGroup.java:1348)
    at android.view.ViewGroup.recomputeViewAttributes(ViewGroup.java:1348)
    at android.view.ViewGroup.recomputeViewAttributes(ViewGroup.java:1348)
    at android.view.ViewGroup.recomputeViewAttributes(ViewGroup.java:1348)
    at android.view.View.needGlobalAttributesUpdate(View.java:10615)
    at android.view.View.setFlags(View.java:11575)
    at android.view.View.setVisibility(View.java:8100)
    at anywheresoftware.b4a.objects.ViewWrapper.setVisible(ViewWrapper.java:271)
    at b4a.example.vehiclestatusshawgmc._webpagehtml(vehiclestatusshawgmc.java:714)
    at java.lang.reflect.Method.invoke(Native Method)
    at anywheresoftware.b4a.BA.raiseEvent2(BA.java:196)
    at anywheresoftware.b4a.BA.raiseEvent2(BA.java:180)
    at anywheresoftware.b4a.BA.raiseEvent(BA.java:176)
    at uk.co.martinpearman.b4a.webkit.DefaultJavascriptInterface.CallSub(DefaultJavascriptInterface.java:39)
    at android.os.MessageQueue.nativePollOnce(Native Method)
    at android.os.MessageQueue.next(MessageQueue.java:328)
    at android.os.Looper.loop(Looper.java:151)
    at android.os.HandlerThread.run(HandlerThread.java:61)
vehiclestatusshawgmc_webpagehtml (java line: 714)
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
    at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:7542)
    at android.view.ViewRootImpl.recomputeViewAttributes(ViewRootImpl.java:3665)
    at android.view.ViewGroup.recomputeViewAttributes(ViewGroup.java:1348)
    at android.view.ViewGroup.recomputeViewAttributes(ViewGroup.java:1348)
    at android.view.ViewGroup.recomputeViewAttributes(ViewGroup.java:1348)
    at android.view.ViewGroup.recomputeViewAttributes(ViewGroup.java:1348)
    at android.view.View.needGlobalAttributesUpdate(View.java:10615)
    at android.view.View.setFlags(View.java:11575)
    at android.view.View.setVisibility(View.java:8100)
    at anywheresoftware.b4a.objects.ViewWrapper.setVisible(ViewWrapper.java:271)
    at b4a.example.vehiclestatusshawgmc._webpagehtml(vehiclestatusshawgmc.java:714)
    at java.lang.reflect.Method.invoke(Native Method)
    at anywheresoftware.b4a.BA.raiseEvent2(BA.java:196)
    at anywheresoftware.b4a.BA.raiseEvent2(BA.java:180)
    at anywheresoftware.b4a.BA.raiseEvent(BA.java:176)
    at uk.co.martinpearman.b4a.webkit.DefaultJavascriptInterface.CallSub(DefaultJavascriptInterface.java:39)
    at android.os.MessageQueue.nativePollOnce(Native Method)
    at android.os.MessageQueue.next(MessageQueue.java:328)
    at android.os.Looper.loop(Looper.java:151)
    at android.os.HandlerThread.run(HandlerThread.java:61)
** Activity (vehiclestatusshawgmc) Pause, UserClosed = true **
Sleep not resumed (context is paused): b4a.example.circularprogressbar$ResumableSub_AnimateValueTo
** Activity (main) Resume **
sending message to waiting queue (sleep)
 
Last edited:
Upvote 0

Charles Bib

Member
Licensed User
Ok.. Well I managed to restructure my code so that WebView1_PageFinished (Url As String) now calls ExtractHTMLViaJavascript() as a resumable sub.
The call back Sub webpagehtml (strhtml As String) doesn't do anything except set a boolean flag. Once the resumable part resumes, I change my webview visibility, etc, based on the boolean flags.

I'd still like to know why the difference between debug and release. This was quite an unexpected debug when I thought I had a working prototype of something. So I have it working, but it really feels like a hack somehow.


Revised with resumable sub:
sub WebView1_PageFinished (Url As String)

    ' Call the javascript injection routine to get the HTML
    WebView1.JavaScriptEnabled = True
   
   
    Dim EJ As ResumableSub = ExtractHTMLViaJavascript
    wait for (EJ) Complete (Result As Boolean)
        ' Wait Items
        If NO_RESULTS = True Then
           
            StopProgressBar
            WebView1.Visible = False
            MsgboxAsync ("Check that VIN was scanned correctly." & CRLF & CRLF & "Vehicle may have been removed from inventory.", "NO RESULTS FOUND")
               
        End If
        If NO_RESULTS = False Then
            StopProgressBarDelayed (5)
            WebView1.Visible = True
        End If
       
End Sub



Sub ExtractHTMLViaJavascript () As ResumableSub
   
    ' Create an outerHTML directive and send it to webpagehtml()

    Dim Javascript As String
    Javascript = "B4A.CallSub('webpagehtml', false, document.documentElement.outerHTML)"
    WebViewExtras1.executeJavascript(Javascript)
   
    ' Extracted value ends up in webpagehtml() via the callback
   
    Return (True)
End Sub


Sub webpagehtml (strhtml As String)

        If CheckForNoVehiclesFound (strhtml) = True Then

        'WebView1.Visible = False
        NO_RESULTS = True
       
    Else
        NO_RESULTS = False
        'WebView1.Visible = True
   
    End If
   
   
End Sub
 
Upvote 0

JohnC

Expert
Licensed User
The error you posted looks like the crashing was due to some threading issue - and threading could definitely act differently in debug vs release mode.

But, why are you even using threads anyway because the code seems pretty straightforward?
 
Upvote 0

Charles Bib

Member
Licensed User
Well honestly, webpagehtml(), which is effectively a callback from the javascript wasnt apparent to me as running in a different thread. Im not sure why it is, since many examples on the forum dealing with html extraction from a webview dont even mention threads.

It would be cool if the compiler would colorize the sub () to draw attention to the fact it will execute in the non ui thread. Either that, or make it consistent, and have it crash in debug mode like it does in release.

Right now it looks like there is some kind of bug at play.
 
Upvote 0

JohnC

Expert
Licensed User
Instead of injecting/running the HTML extraction code in a separate routine, why not try running it in the _PageFinished event like this:

B4X:
Sub WebView1_PageFinished (Url As String)

    ' Create an outerHTML directive and send it to webpagehtml()
    Dim Javascript As String
    Javascript = "B4A.CallSub('webpagehtml', false, document.documentElement.outerHTML)"
    WebViewExtras1.executeJavascript(Javascript)

End Sub
 
Upvote 0
Top