1

I have a crash report of the same error like in this question: WebView methods on same thread error

There it is suggested to create a Runnable().

I don't understand why exactly this solves the problem. The error says "Webview methods on same Thread", but the answer suggests to create the method on the UI-Thread (Main Thread). But isn't the UI-Thread the one and only thread? Could someone explain this whole process in detail (considering I create a new Webview in every activity in the constructor)?

My code to implement Javascript functions/methods looks like this:

public class JS_Bind {
    private static final String TAG = "JS_Bind";
    private Context context;
    private AdvancedWebView mWebView;

    public JS_Bind(Context c, AdvancedWebView mWebView) {
        context = c;
        this.mWebView = mWebView;
    }
    @JavascriptInterface
    public void openActivity(String activityName) {
        try {
            Class activityClass = Class.forName(PACKAGE_NAME + "." + activityName);
            context.startActivity(new Intent(MainActivity.this, activityClass));
        } catch (ClassNotFoundException e) {
            Toast.makeText(context, "Invalid activity name: " + activityName, Toast.LENGTH_SHORT).show();
            e.printStackTrace();
        }
    }
    @JavascriptInterface
    public void makeToast(String toast) {
        Toast mToast = Toast.makeText(context, toast, Toast.LENGTH_SHORT);
        mToast.setGravity(Gravity.CENTER, 0, 0);
        mToast.show();
    }
    @JavascriptInterface
    public void external(String url) {
        mTracker.send(new HitBuilders.EventBuilder().setCategory("Action").setAction("External Link: " + url).build());
        Uri uri = Uri.parse(url);
        Intent intent = new Intent(Intent.ACTION_VIEW, uri);
        startActivity(intent);
    }
    @JavascriptInterface
    public String showToken() {
        return gcmToken;
    }
    @JavascriptInterface
    public int showUid() {
        SharedPreferences pref = getSharedPreferences("Pref", Activity.MODE_PRIVATE);
        int uid = pref.getInt("uid", 0);
        return uid;
    }
    @JavascriptInterface
    public void buyPremium() {
        bp.purchase(MainActivity.this, PRODUCT_ID);
    }
}

Do I have to change EVERY function to this code (first answer in the question I refered to):

@JavascriptInterface
    mWebView.post(new Runnable() {
        @Override
        public void makeToast() {
          // ...
        }
    });

?

By the way, this is how I create the webview in the constructor activies onCreate method:

mWebView = (AdvancedWebView) findViewById(R.id.webView);
mWebView.setListener(this, this);
mWebView.addJavascriptInterface(new JS_Bind(this, mWebView), "Android");
mWebView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);

if (!DetectConnection.checkInternetConnection(this)) {
    mWebView.loadUrl("file:///android_asset/offline.html");
}
else {
        mWebView.loadUrl("http://example.com/tw3/index.php?s=home");
}
Community
  • 1
  • 1
AlexioVay
  • 4,338
  • 2
  • 31
  • 49

1 Answers1

1

But isn't the UI-Thread the one and only thread?

No. WebView has its own pool of threads. There can be many other threads in an Android application.

Do I have to change EVERY function to this code

Not necessarily.

First, I do not see how you are getting that error (A WebView method was called on thread 'JavaBridge'. All WebView methods must be called on the same thread.) from your @JavascriptInterface methods shown above. That error is when you call a method on the WebView itself, and you are not doing that.

You will need to use runOnUiThread() (or equivalent techniques) if:

  • Your @JavascriptInterface methods refer to the WebView itself, or

  • Your @JavascriptInterface try to do something else that has to be done on the main application thread (which will yield a different error message, as it will not be tied specifically to WebView)

Your startActivity() call might need to be called on the main application thread — I forget if that can be called on a background thread or not. Similarly with your Toast work — while I think that can be done on a background thread, I am not certain of it. It has been ages since I tried doing either of those things from a background thread.


Also, please only use your code if you control every single byte of what is being displayed in the WebView. Exposing startActivity() to arbitrary Web content has significant security implications.

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • Sorry, I forgot to mention that I didn't list every JavascriptInterface function, so I only posted 2 to demonstrate. I'm not quite sure which of my functions caused the error. I update my question in 1 min to show you the rest. I also thought about using webview in an AsyncTask, would you recommend that? About the security issue: Should I create multiple hardcoded methods for every single activity then? – AlexioVay Jun 24 '16 at 21:48
  • @Vaia: "I also thought about using webview in an AsyncTask, would you recommend that?" -- no. "Should I create multiple hardcoded methods for every single activity then?" -- that would be an improvement. Or, just validate the input, rather than blindly opening any activity based on the passed-in string. – CommonsWare Jun 24 '16 at 21:49
  • @Vaia: You are not referencing `mWebView` anywhere. So, I do not see how you can be getting an error from it, but I also do not see why you have it around in the first place. – CommonsWare Jun 24 '16 at 21:50
  • I initialized my webview with `private AdvancedWebView mWebview;` and pasted the code how I created mWebView in my constructor at the end of my question here. Sorry, maybe it's because English isn't my mother language, but what exactly do you mean by why I have it around in the first place? Thank you for your time, I appreciate it. – AlexioVay Jun 24 '16 at 21:55
  • @Vaia: "I initialized my webview..." -- yes, but then you never refer to it anywhere. Delete that field. Or, paste in the methods where you *are* referring to it. – CommonsWare Jun 24 '16 at 21:56
  • I updated my question at the end of it. That is what I wrote in my `onCreate` method. Is this what you mean? – AlexioVay Jun 24 '16 at 22:12
  • @Vaia: No. I am referring to `private AdvancedWebView mWebView;` in `JS_Bind`. This would appear to be a useless field. You assign a value to it, but then **you never use that field** anywhere else in the `JS_Bind` code in your question. It would appear that you can delete that field. – CommonsWare Jun 24 '16 at 22:16
  • Ohh, I see. Thank you! I also found out why the error occured. It's an activity that a user calls via a webview link on an earlier version of my app where the activity didn't exist in that specific app version. However, thank you very much! – AlexioVay Jun 24 '16 at 22:25