AlexioVay
AlexioVay

Reputation: 4527

Best practice implementing JavascriptInterface using WebView in Android?

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");
}

Upvotes: 1

Views: 3181

Answers (1)

CommonsWare
CommonsWare

Reputation: 1007296

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.

Upvotes: 1

Related Questions