美文网首页IT干货程序员代码改变世界
Android WebView呼叫Javascript填坑记

Android WebView呼叫Javascript填坑记

作者: LostAbaddon | 来源:发表于2015-01-20 17:17 被阅读1954次

    作为誓死效忠大安卓帝国的程序狗,我一般不写技术类文章。
      你们翻翻我的文章就会发现,我还真不像技术流的。
      不过最近搞一个安卓的坑,搞得非常蛋疼,于是打算纪录下来。

    坑是这样的:我有一个WebView,里面有一个写字区域,然后我要在写字的同时呼叫里面的JS。
      在iOS上这事挺容易整的,比如这样(obj-C为例):

    NSString *hasRange =
        [self stringByEvaluatingJavaScriptFromString:@"MobileWriter.hasRange()"];
    

    但在大安卓帝国,这事就有点蛋疼了。

    在KitKat上,WebView有个接口名叫evaluateJavascript,从而事情是这样的:

    evaluateJavascript("MobileWriter.hasRange();", resultCallback);
    

    这货看上去和iOS上差不多,但已经有点讨厌了:它是Callback机制的,不像iOS上直接拿结果。
      但,这还算好的,如果不是KitKat,也就是4.4之前的Android,你连这个接口都没有,于是只能这样:

    loadUrl("javascript:MobileWriter.hasRange();");
    

    这个就很蛋疼了,因为没有callback,你必须在JS运行结束后,让JS去调用一个指定的对象,从而通过这个对象来获得回调,比如下面这样:

    (In Java)
    addJavascriptInterface(new JavascriptDelegate () {
        @JavascriptInterface
        public void jsCallback (String result) {
            Log.i("Editor", "Blablabla...");
        }
    }, "AndroidHost");
    
    (In Javascript)
    MobileWriter.hasRange = function () {
        "Blablabla..."
        if (AndroidHost && AndroidHost.jsCallback) {
            AndroidHost.jsCallback('Mission Complete.');
        }
    };
    

    看着是不是就很蛋疼?
      但,这根本不算事,挺Easy的,只要J-J两端协议定好,这都不叫事儿。
      麻烦的是下面这个问题:
      每次你在Java端使用loadUrl的时候,在4.4以下的安卓上都会引发WebView的页面重新载入事件(而且这个你还没法通过重载WebViewClient的shouldOverrideUrlLoading方法来阻止),从而引发系统的clearHelpers,这货则会调用clearTextEntry并最终调用到hideSoftKeyboard。
      这个貌似看上去没什么,但实际上却很糟糕,因为这会导致两个问题:

    1. SoftKeyboard会自动消失(hideSoftKeyboard);
    2. Contextual Menu和相关选区会自动消失(clearTextEntry)。

    也就是说,如果你在输入的时候就要调用JS的话,那么只要你调用了JS,输入状态就自动消失,键盘没了,选区没了,你得重新开始选择。
      这事就很蛋疼了。
      4.4为什么通过调用loadUrl来调用JS不会有这个问题?因为如果你调用的是javascript协议从而也就是调用js函数的话,其实4.4下面走的是上面提到的evaluateJavascrpt,当然安全了。

    解决这个问题的方法,一个是用反射调用android.webkit.BrowserFrame.stringByEvaluatingJavaScriptFromString(),这个比较蛋疼。
      另一个,则是设法通知JS我Java端有事件了,然后让JS调用JavascriptDelegate插入的Delegate对象,并从这个对象获取当前要做的事件,并执行。

    第一个方法比较霸气,直接用反射,相当犀利,但我不确定能否通过安检(不过国内App反正也没啥检查,应该不慌。GoogleAppStore是否允许我这么玩我就不知道了)。
      第二个方法比较温和,没这么霸气,但缺点是你得加一个同步锁,避免操作不同步导致问题——WebView中的JS是跑在另一根线程上的,这种频繁的线程间相互调用回调的方法安全性是个问题。
      至于说通知JS应该要召唤Delegate的方法嘛,当然不能傻傻地用loadUrl了。你可以小小地微调一下WebView的尺寸,引发JS端的window.onresize事件,然后就可以让JS端去调用Java端了。
      或者另一个比较蛋疼的方法是JS端开一个Timeout甚至Interval,这个有点网站开发早期的轮询了,但个人不建议这么做,毕竟是手机端,毕竟是轮询,还是要考虑资源消耗的。
      至于说有没有别的通知手段,这个暂时没想到。。。

    相关文章

      网友评论

      本文标题:Android WebView呼叫Javascript填坑记

      本文链接:https://www.haomeiwen.com/subject/dfdhxttx.html