3

I am using WebView and a lot of JavaScript in my native application. If JS side wants to call native functions it's rather smooth using JavaScriptInterface. However, if native wants to call JS function it's not so easy. I've tried two solutions so far:

  • calling loadUrl method with javascript:something() URL - not acceptable since when user is typing something on keyboard WebView is hiding it after function execution
  • asking native side for functions every x seconds (presented by String object) and call eval() on a result - sometimes after setting interval for 1 seconds I was getting those requests after 50-60 seconds!

I am wondering if there's any other way to accomplish this communication model. Let's say create local Socket/HTTP/something-else server inside app and give access for it to WebView. I am looking for any tips how (if it's possible) do that.

Piotr
  • 1,668
  • 1
  • 22
  • 38

2 Answers2

3

I have the following code which uses java reflection to call JavaScript from Java. It avoids the following loadUrl bugs: 1. loadUrl may hide keyboard when your focus in a input. 2. loadUrl cannot be called too often.

Hopefully this helps:

public class AdvancedWebView extends WebView {
    private static final int EXECUTE_JS = 194;
    Method sendMessageMethod;
    Object webViewCore;
    boolean initFailed = false;;
    InputMethodManager imm;


    public AdvancedWebView(Context context, AttributeSet attrs) {
        super(context, attrs);
        imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
        this.getSettings().setJavaScriptEnabled(true);
        this.initReflection();
    }

    @SuppressWarnings("rawtypes")
    private void initReflection() {
        Object webViewObject = this;
        Class webViewClass = WebView.class;
        try {
            Field f = webViewClass.getDeclaredField("mProvider");
            f.setAccessible(true);
            webViewObject = f.get(this);
            webViewClass = webViewObject.getClass();
        } catch (Throwable e) {
            // mProvider is only required on newer Android releases.
        }       

        try {
            Field f = webViewClass.getDeclaredField("mWebViewCore");
            f.setAccessible(true);
            webViewCore = f.get(webViewObject);
            if (webViewCore != null) {
                sendMessageMethod = webViewCore.getClass().getDeclaredMethod("sendMessage", Message.class);
                sendMessageMethod.setAccessible(true);      
                System.out.println("sendMessageMethod-----"+sendMessageMethod);
            }
            hasIntercepted = true;
        } catch (Throwable e) {
            hasIntercepted = false;
            //Log.e(LOG_TAG, "PrivateApiBridgeMode failed to find the expected APIs.", e);
        }finally{
            if(sendMessageMethod == null)
            {
                hasIntercepted = false;
            }
        }
    }

    private void loadJs(String url) {
        if (sendMessageMethod == null && !initFailed) {
            initReflection();
        }
        // webViewCore is lazily initialized, and so may not be available right away.
        if (sendMessageMethod != null) {
            //String js = popAndEncodeAsJs();
            Message execJsMessage = Message.obtain(null, EXECUTE_JS, url);
            try {
                sendMessageMethod.invoke(webViewCore, execJsMessage);
            } catch (Throwable e) {
                //Log.e(LOG_TAG, "Reflection message bridge failed.", e);
            }
        }
    }
}
Jacinda
  • 4,424
  • 2
  • 24
  • 37
Jonny Chen
  • 84
  • 3
  • should change private void loadJs -> public void loadJs; then use webview.loadJs instead of webview.loadUrl when I want to call javascript. – o0omycomputero0o May 12 '16 at 07:56
1

I don't know how to get an alternate communication model working, but I think I've found a workaround for the loadUrl-hides-the-keyboard issue: https://stackoverflow.com/a/18776064/513038. Does that help any?

Community
  • 1
  • 1
Erhannis
  • 3,613
  • 2
  • 24
  • 38