Native JsBridge源码解析 深入理解JsBridge

Posted Vicent_9920

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Native JsBridge源码解析 深入理解JsBridge相关的知识,希望对你有一定的参考价值。

最近项目中使用了 HyBrid 框架,但是在使用过程中遇到了不少问题,因此花时间来研究了一下其中原理! 在平时开发过程中,不管是可复用性非常高,可以跨平台开发的 HyBrid ,还是半 Native 半 web 浅尝辄止的 HyBrid ,对 android 而言,陌生的就是其中的通信——Android 与 html 的互相通信。这里就不掉书包,直接阐明其中的使用方法。

  • 引入JsBridge库
//at your porject gradle
repositories 
    // ...
    maven  url "https://jitpack.io" 


//at you app gradle
dependencies 
    compile 'com.github.lzyzsd:jsbridge:1.0.4'

  • Android端收发消息

    1. 使用控件
    2. 向Html发送消息
    3. 接收Html发送的消息
//布局
 <com.github.lzyzsd.jsbridge.BridgeWebView
        android:id="@+id/webView"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
     </com.github.lzyzsd.jsbridge.BridgeWebView>

//控件
webView = (BridgeWebView) findViewById(R.id.webView);

/**
 * 发送消息给html
 * @param value 字符串
 * @param data  数据
 * @param function  回调方法
 */
webView.callHandler("value", "data", new CallBackFunction() 
            @Override
            public void onCallBack(String response) 

            
        );

 /**
 * 发送消息给html
 * @param data  字符串
 */   
 webView.send("hello");

/**
 * register handler,so that javascript can call it
 * 注册handler,以便javascript可以调用它
 * @param handlerName
 * @param handler
 */
webView.registerHandler("value", new BridgeHandler() 

            @Override
            public void handler(String data, CallBackFunction function) 

            

        );
  • Html端收发消息
//注册 WebViewJavascriptBridgeReady
function connectWebViewJavascriptBridge(callback) 
    if (window.WebViewJavascriptBridge) 
        callback(WebViewJavascriptBridge)
     else 
        document.addEventListener(
            'WebViewJavascriptBridgeReady'
            , function() 
                callback(WebViewJavascriptBridge)
            ,
            false
        );
    

connectWebViewJavascriptBridge(function(bridge) 
    bridge.init(function(message, responseCallback) 
        console.log('JS got a message', message);
        //默认返回值
        var data = 
            'Javascript Responds': '测试中文!'
        ;
        console.log('JS responding with', data);
        responseCallback(data);
    );
    /**
     * 接收消息
     * "functionInJs"   字符串标签
     * data 收到数据
     * responseCallback 回调接口
     */
     bridge.registerHandler("functionInJs", function(data, responseCallback) 
          document.getElementById("show").innerHTML = ("data from Java: = " + data);
          var responseData = "Javascript Says Right back aka!";
          responseCallback(responseData);
      );
  )

通过库文件demo可以仔细看到这些方法,还是比较容易理解的。但是具体是怎么一个原理呢?跟踪代码看看,其实非常简单:

webView.send("hello");

/**
 * 发送消息给html
 * @param value 字符串
 * @param data  数据
 * @param function  回调方法
 */
private void sendMessage(String value, String data, CallBackFunction function) 
        webView.callHandler(value, data, function);
    

接下来看看BridgeWebView的源码:


@Override
    public void send(String data) 
        send(data, null);
    

    @Override
    public void send(String data, CallBackFunction responseCallback) 
        doSend(null, data, responseCallback);
    

/**
     * call javascript registered handler
     *
     * @param handlerName
     * @param data
     * @param callBack
     */
    public void callHandler(String handlerName, String data, CallBackFunction callBack) 
        doSend(handlerName, data, callBack);
    

接下来仔细研究一下doSend这个方法,源码如下:

private void doSend(String handlerName, String data, CallBackFunction responseCallback) 
        Message m = new Message();
        if (!TextUtils.isEmpty(data)) 
            m.setData(data);
        
        if (responseCallback != null) 
            String callbackStr = String.format(BridgeUtil.CALLBACK_ID_FORMAT, ++uniqueId + (BridgeUtil.UNDERLINE_STR + SystemClock.currentThreadTimeMillis()));
            responseCallbacks.put(callbackStr, responseCallback);
            m.setCallbackId(callbackStr);
        
        if (!TextUtils.isEmpty(handlerName)) 
            m.setHandlerName(handlerName);
        
        queueMessage(m);
    

上面的代码其实就是封装了一个 Message,然后将 Message 添加到添加到队列里,这个结构类似于 Handler,接下来看看队列消息里面怎么将消息发送到 Js?

private void queueMessage(Message m) 
        if (startupMessage != null) 
            startupMessage.add(m);
         else 
            dispatchMessage(m);
        
    

    void dispatchMessage(Message m) 
        String messageJson = m.toJson();
        //escape special characters for json string
        messageJson = messageJson.replaceAll("(\\\\\\\\)([^utrn])", "\\\\\\\\\\\\\\\\$1$2");
        messageJson = messageJson.replaceAll("(?<=[^\\\\\\\\])(\\")", "\\\\\\\\\\"");
        String javascriptCommand = String.format(BridgeUtil.JS_HANDLE_MESSAGE_FROM_JAVA, messageJson);
        if (Thread.currentThread() == Looper.getMainLooper().getThread()) 
            LUtil.e(javascriptCommand);
            this.loadUrl(javascriptCommand);
        
    

startupMessage 其实就是一个消息队列,这个对象的初始值肯定不为 null,但是这里的 startupMessage 确实是被置空了,至于在什么地方置空的,稍候再研究,我们可以看到在分发消息的时候,直接将 Message 转成了 Json 字符串,然后对字符串经过一系列处理便加载了这个方法,通过日志,我们可以看到这时候输出的内容如下:

问题是我们注册的时候,并没有这个方法,那么这个方法在什么地方被声明的呢?通过查看 BridgeWebView 的相关方法,最后发现在 BridgeWebViewClient 的 onPageFinished 方法里,这里将 assets 文件夹下的 WebViewJavascriptBridge.js 读取出来发送到了 html,而 WebViewJavascriptBridge.js 内容是什么呢?看代码:

//发送消息内容:
view.loadUrl("javascript:" + jsContent);
//消息内容如下
//notation: js file can only use this kind of comments
//since comments will cause error when use in webview.loadurl,
//comments will be remove by java use regexp
(function() 
    if (window.WebViewJavascriptBridge) 
        return;
    

    var messagingIframe;
    var sendMessageQueue = [];
    var receiveMessageQueue = [];
    var messageHandlers = ;

    var CUSTOM_PROTOCOL_SCHEME = 'yy';
    var QUEUE_HAS_MESSAGE = '__QUEUE_MESSAGE__/';

    var responseCallbacks = ;
    var uniqueId = 1;

    function _createQueueReadyIframe(doc) 
        messagingIframe = doc.createElement('iframe');
        messagingIframe.style.display = 'none';
        doc.documentElement.appendChild(messagingIframe);
    

    //set default messageHandler
    function init(messageHandler) 
        if (WebViewJavascriptBridge._messageHandler) 
            throw new Error('WebViewJavascriptBridge.init called twice');
        
        WebViewJavascriptBridge._messageHandler = messageHandler;
        var receivedMessages = receiveMessageQueue;
        receiveMessageQueue = null;
        for (var i = 0; i < receivedMessages.length; i++) 
            _dispatchMessageFromNative(receivedMessages[i]);
        
    

    function send(data, responseCallback) 
        _doSend(
            data: data
        , responseCallback);
    

    function registerHandler(handlerName, handler) 
        messageHandlers[handlerName] = handler;
    

    function callHandler(handlerName, data, responseCallback) 
        _doSend(
            handlerName: handlerName,
            data: data
        , responseCallback);
    

    //sendMessage add message, 触发native处理 sendMessage
    function _doSend(message, responseCallback) 
        if (responseCallback) 
            var callbackId = 'cb_' + (uniqueId++) + '_' + new Date().getTime();
            responseCallbacks[callbackId] = responseCallback;
            message.callbackId = callbackId;
        

        sendMessageQueue.push(message);
        messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE;
    

    // 提供给native调用,该函数作用:获取sendMessageQueue返回给native,由于android不能直接获取返回的内容,所以使用url shouldOverrideUrlLoading 的方式返回内容
    function _fetchQueue() 
        var messageQueueString = JSON.stringify(sendMessageQueue);
        sendMessageQueue = [];
        //android can't read directly the return data, so we can reload iframe src to communicate with java
        messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://return/_fetchQueue/' + encodeURIComponent(messageQueueString);
    

    //提供给native使用,
    function _dispatchMessageFromNative(messageJSON) 
        setTimeout(function() 
            var message = JSON.parse(messageJSON);
            var responseCallback;
            //java call finished, now need to call js callback function
            if (message.responseId) 
                responseCallback = responseCallbacks[message.responseId];
                if (!responseCallback) 
                    return;
                
                responseCallback(message.responseData);
                delete responseCallbacks[message.responseId];
             else 
                //直接发送
                if (message.callbackId) 
                    var callbackResponseId = message.callbackId;
                    responseCallback = function(responseData) 
                        _doSend(
                            responseId: callbackResponseId,
                            responseData: responseData
                        );
                    ;
                

                var handler = WebViewJavascriptBridge._messageHandler;
                if (message.handlerName) 
                    handler = messageHandlers[message.handlerName];
                
                //查找指定handler
                try 
                    handler(message.data, responseCallback);
                 catch (exception) 
                    if (typeof console != 'undefined') 
                        console.log("WebViewJavascriptBridge: WARNING: javascript handler threw.", message, exception);
                    
                
            
        );
    

    //提供给native调用,receiveMessageQueue 在会在页面加载完后赋值为null,所以
    function _handleMessageFromNative(messageJSON) 
        console.log(messageJSON);
        if (receiveMessageQueue && receiveMessageQueue.length > 0) 
            receiveMessageQueue.push(messageJSON);
         else 
            _dispatchMessageFromNative(messageJSON);
        
    

    var WebViewJavascriptBridge = window.WebViewJavascriptBridge = 
        init: init,
        send: send,
        registerHandler: registerHandler,
        callHandler: callHandler,
        _fetchQueue: _fetchQueue,
        _handleMessageFromNative: _handleMessageFromNative
    ;

    var doc = document;
    _createQueueReadyIframe(doc);
    var readyEvent = doc.createEvent('Events');
    readyEvent.initEvent('WebViewJavascriptBridgeReady');
    readyEvent.bridge = WebViewJavascriptBridge;
    doc.dispatchEvent(readyEvent);
)();

前面讲的 startupMessage 置空,就是在上面的 onPageFinished 中被置空的。

//置空startupMessage
if (webView.getStartupMessage() != null) 
    for (Message m : webView.getStartupMessage()) 
        webView.dispatchMessage(m);
    
    webView.setStartupMessage(null);

接下来分析,BridgeWebView 端发送的消息是怎么在 JS 被执行的呢?先是通过 _handleMessageFromNative 方法调用 _dispatchMessageFromNative 方法,最后执行如下代码:

try 
   handler(message.data, responseCallback);
 catch (exception) 
    if (typeof console != 'undefined') 
        console.log("WebViewJavascriptBridge: WARNING: javascript handler threw.", message, exception);
    

handler(message.data, responseCallback) 方法具体实现是在下面实现的( js 不大熟悉,整理代码发现逻辑大致如下):

/**
             * 接收消息
             * "functionInJs"   字符串标签
             * data 收到数据
             * responseCallback 回调接口
             */
            bridge.registerHandler("functionInJs", function(data, responseCallback) 
                document.getElementById("show").innerHTML = ("data from Java: = " + data);
                var responseData = "Javascript Says Right back aka!";
                responseCallback(responseData);
            );

接着我们看看 BridgeWebView 端发送消息的回调接口,是怎么实现的?通过代码我们发现 _dispatchMessageFromNative 方法下回调方法 _doSend 方法:

 //sendMessage add message, 触发native处理 sendMessage
    function _doSend(message, responseCallback) 
        if (responseCallback) 
            var callbackId = 'cb_' + (uniqueId++) + '_' + new Date().getTime();
            responseCallbacks[callbackId] = responseCallback;
            message.callbackId = callbackId;
        

        sendMessageQueue.push(message);
        messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE;
    

_doSend 方法请求 url:yy://QUEUE_MESSAGE/
当 BridgeWebView 的 shouldOverrideUrlLoading 方法收到 messagingIframe 请求,继续检查这个方法:

 @Override
 public boolean shouldOverrideUrlLoading(WebView view, String url) 
      try 
          url = URLDecoder.decode(url, "UTF-8");
          LUtil.e("shouldOverrideUrlLoading:"+url);
       catch (UnsupportedEncodingException e) 
          e.printStackTrace();
      

      if (url.startsWith(BridgeUtil.YY_RETURN_DATA))  
          webView.handlerReturnData(url);
          return true;
       else if (url.startsWith(BridgeUtil.YY_OVERRIDE_SCHEMA))  //
          webView.flushMessageQueue();
          return true;
       else 
          return super.shouldOverrideUrlLoading(view, url);
      
  

这次我们查看 BridgeWebView 的 flushMessageQueue 方法,直接看代码:

        /**
     * 刷新消息队列
     */
    void flushMessageQueue() 
        if (Thread.currentThread() == Looper.getMainLooper().getThread()) 
            loadUrl(BridgeUtil.JS_FETCH_QUEUE_FROM_JAVA, new CallBackFunction() 
                ***
            );
        
    

public void loadUrl(String jsUrl, CallBackFunction returnCallback) 
        this.loadUrl(jsUrl);
        responseCallbacks.put(BridgeUtil.parseFunctionName(jsUrl), returnCallback);
    

其实这里很清楚了,就是当 JS 需要返回数据的时候,先将需要返回的数据保存下来,然后发送请求到 BridgeWebView ,告诉 BridgeWebView 我要发送回调数据了,然后 BridgeWebView 将请求和接口以键值对的形式保存下来,并请求 js 的方法—— javascript:WebViewJavascriptBridge._fetchQueue();JS 收到消息后就发送请求,然后 shouldOverrideUrlLoading 收到请求判断消息头以后调用 BridgeWebView 的方法,代码如下:

//返回消息头 yy://return/_fetchQueue/+返回数据
void handlerReturnData(String url) 
        String functionName = BridgeUtil.getFunctionFromReturnUrl(url);
        CallBackFunction f = responseCallbacks.get(functionName);
        String data = BridgeUtil.getDataFromReturnUrl(url);
        if (f != null) 
            f.onCallBack(data);
            responseCallbacks.remove(functionName);
            return;
        
    

最后将这个过程整理如下:

  • 安卓发送消息给 Js 过程
  1. 当 WebView 发送数据给 Js 时: WebView 请求 js 方法—— javascript:WebViewJavascriptBridge._handleMessageFromNative( gson 字符串);
  2. js 执行相关方法: _handleMessageFromNative——_dispatchMessageFromNative——handler(message.data, responseCallback);,
  • Js 通过接口返回数据
  1. js 执行 _doSend 方法;
  2. BridgeWebView 拦截请求并判断消息头,如果消息头标签是—— yy://,则将 _fetchQueue 和接口再次以键值对的形式保存下来,并通过 BridgeWebView 请求 js 的 _fetchQueue() 方法;
  3. js 的 _fetchQueue() 方法将值发送给 BridgeWebView;
  4. BridgeWebView 再次判断请求的消息头,如果消息头标签是—— yy://return/ ,则用 _fetchQueue 取出的接口对象;
  5. 在接口对象中,根据返回的接口对象 Id —— responseId 取出 BridgeWebView 存储的待处理数据的接口对象,并将请求里面包含的数据取出来,执行待处理数据的接口。

接下来 Js 发送消息给 BridgeWebView 的过程又如何呢?首先咱们得与 Js 约定一个 Handler,然后将这个 Handler 保存下来,代码如下:

Map<String, BridgeHandler> messageHandlers = new HashMap<String, BridgeHandler>();
/**
 * register handler,so that javascript can call it
 * 
 * @param handlerName
 * @param handler
 */
public void registerHandler(String handlerName, BridgeHandler handler) 
        if (handler != null) 
            messageHandlers.put(handlerName, handler);
        
    

然后咱们继续看 Js 是如何发送消息给 BridgeWebView,还是直接看源码:


 //call native method
 window.WebViewJavascriptBridge.callHandler(
     'submitFromWeb'
     , 'param': '中文测试'
     , function(responseData) 
         document.getElementById("show").innerHTML = "send get responseData from java, data = " + responseData
     
 );

来来来,咱们理一下这个过程,首先 js 执行 callHandler 方法—— _doSend 方法,在这个方法 _doSend 方法中还是一样:将 Message 信息保存下来,并发送请求给 BridgeWebView,告诉他,我要给你发消息了,准备好!
然后过程就和上面类似了,过程如下:

  1. js 执行 _doSend 方法,保存消息并发送请求—— yy://QUEUE_MESSAGE/;
  2. BridgeWebView 拦截请求并判断消息头,如果消息头标签是—— yy://,则将 _fetchQueue 和接口以键值对的形式保存下来,并通过 BridgeWebView 请求 js 的 _fetchQueue() 方法;
  3. js 执行 _fetchQueue() 方法并发送请求——yy://return/_fetchQueue/+”消息”;
  4. BridgeWebView 在 shouldOverrideUrlLoading 方法拦截请求,如果消息头标签是—— yy://return/_fetchQueue/,则取出第2点保存的接口,并将请求包含的数据发送给接口;
  5. 在接口当中做两件事,第一件事是根据请求信息生成 Message 对象并保存下来(BridgeWebView的queueMessage方法),另一件事就是根据请求内容将 js 与 BridgeWebView 约定 BridgeHandler ,并执行 BridgeHandler 的 handler 方法;
  6. BridgeWebViewClient 在 onPageFinished 方法中分发消息(dispatchMessage方法)—— BridgeWebView 请求 js 的 _handleMessageFromNative 方法;
  7. Js 执行 _handleMessageFromNative 方法—— _dispatchMessageFromNative 方法,在方法中根据 responseId 找出接口,并执行该接口,完毕。

上述已经描述了 BridgeWebView 发送消息并回调、Js 发送消息并回调,整个过程我们已经很清楚了,那么这些个过程有没有需要优化的地方呢?比如 BridgeWebView 与 js 优化的地方有很多,那么能不能把接口改一下呢?咱们试一试!由于整个 JsBridge 代码文件不多,我们可以直接下载下来,这样方便我们修改,因此我们可以直接修改 BridgeHandler.java ,然后修改部分代码,说干就干:

public interface BridgeHandler 

    void handler(String name, String data, CallBackFunction function);


//修改DefaultHandler
public class DefaultHandler implements BridgeHandler 


    @Override
    public void handler(String name,String data, CallBackFunction function) 
        if(function != null)
            function.onCallBack("DefaultHandler response data");
        
    



//通过Js信息查找Handler
JsBridgeHandler handler;
                            if (!TextUtils.isEmpty(m.getHandlerName())) 
                                handler = messageHandlers.get(m.getHandlerName());
                             else 
                                handler = defaultHandler;
                            
                            if (handler != null)
                                handler.handler(m.getHandlerName(),m.getData(), responseFunction);
                            

//修改注册方法
@Override
  public void handler(String name, String data, final CallBackFunction function) 

      switch (name)
          case "open"://打开后台维护界面

              break;

          case "token"://获取token

              break;

          case "clientId"://获取clientId

              break;

          case "out"://退出账号

              break;

      
  

以上已经分析完毕。JsBridge 的原理大家都知道了,接下来需要做的就是WebView 的优化,因为我们知道原生的 WebView 加载页面的时候渲染比较慢,一些诸如腾讯 X5、VasSonic 等据说渲染效果比 WebView 好很多,那么兼容通信和渲染效果良好的的 JsBridge 框架,应该怎么搭建呢?好好想一想?

  • 腾讯 X5 WebView 接入

    X5 WebView 比较简单,就是一个 jar 包,然后将原本的 android.webkit 包下的内容转为 com.tencent.smtt 下的相关类,并实现相关权限(运行时权限需要自己处理),具体如下:

系统内核SDK内核
android.webkit.ConsoleMessagecom.tencent.smtt.export.external.interfaces.ConsoleMessage
android.webkit.CacheManagercom.tencent.smtt.sdk.CacheManager(deprecated)
android.webkit.CookieManagercom.tencent.smtt.sdk.CookieManager
android.webkit.CookieSyncManagercom.tencent.smtt.sdk.CookieSyncManager
android.webkit.CustomViewCallbackcom.tencent.smtt.export.external.interfaces.IX5WebChromeClient.CustomViewCallback
android.webkit.DownloadListenercom.tencent.smtt.sdk.DownloadListener
android.webkit.GeolocationPermissionscom.tencent.smtt.export.external.interfaces.GeolocationPermissionsCallback
android.webkit.HttpAuthHandlercom.tencent.smtt.export.external.interfaces.HttpAuthHandler
android.webkit.JsPromptResultcom.tencent.smtt.export.external.interfaces.JsPromptResult
android.webkit.JsResultcom.tencent.smtt.export.external.interfaces.JsResult
android.webkit.SslErrorHandlercom.tencent.smtt.export.external.interfaces.SslErrorHandler
android.webkit.ValueCallbackcom.tencent.smtt.sdk.ValueCallback
android.webkit.WebBackForwardListcom.tencent.smtt.sdk.WebBackForwardList
android.webkit.WebChromeClientcom.tencent.smtt.sdk.WebChromeClient
android.webkit.WebHistoryItemcom.tencent.smtt.sdk.WebHistoryItem
android.webkit.WebIconDatabasecom.tencent.smtt.sdk.WebIconDatabase
android.webkit.WebResourceResponsecom.tencent.smtt.export.external.interfaces.WebResourceResponse
android.webkit.WebSettingscom.tencent.smtt.sdk.WebSettings
android.webkit.WebSettings.LayoutAlgorithmcom.tencent.smtt.sdk.WebSettings.LayoutAlgorithm
android.webkit.WebStoragecom.tencent.smtt.sdk.WebStorage
android.webkit.WebViewcom.tencent.smtt.sdk.WebView
android.webkit.WebViewClientcom.tencent.smtt.sdk.WebViewClient
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
  • VasSonic-Android 接入
    VasSonic接入比较简单,但是使用就比较复杂,接入如下:
//Add VasSonic gradle plugin as a dependency in your module's build.gradle

compile 'com.tencent.sonic:sdk:1.0.0'

使用前需要实现 SonicRuntime 和 SonicSessionClient 接口,具体参考 VasSonic


腾讯 X5 WebView 、VasSonic 、原生 WebView 对比

通过对onPageStarted方法、onPageFinished方法查看各个 WebView 加载页面所需时间,发现 X5 、VasSonic 均比原生 WebView 快了近一倍,所以 JsBridge 有必要将第三方WebView 加载到项目中,但是加载 X5 还是 VasSonic 呢?通过查看文档发现 VasSonic 更新需要和后台服务器交互判断是否更新数据?也就是说 VasSonic 需要和后台配合才能发挥最大效果。我倾向于选择前者,因为使用起来简单得多,而后者的库也配置好了,地址如下:https://github.com/Vicent9920/JsBridge


JsBridge 注意事项:
  • App 首次就可以加载 x5 内核

    App 在启动后(例如在 Application 的 onCreate 中)立刻调用 QbSdk 的预加载接口 initX5Environment ,可参考接入示例,第一个参数传入 context,第二个参数传入 callback,不需要 callback 的可以传入 null,initX5Environment 内部会创建一个线程向后台查询当前可用内核版本号,这个函数内是异步执行所以不会阻塞 App 主线程,这个函数内是轻量级执行所以对 App 启动性能没有影响,当 App 后续创建 webview 时就可以首次加载 x5 内核了。(也可以像LitePal那样在Application初始化,但是需要文件清单配置或者继承自该Application,或者在该类传入一个静态方法初始化 JsBridge )

  • 获取系统内核的WebView或者 x5内核的WebView的宽高

com.tencent.smtt.sdk.WebView webView = new com.tencent.smtt.sdk.WebView(this);
int width = webView.getView().getWidth();
  • 避免输入法界面弹出后遮挡输入光标的问题
//方法一:在AndroidManifest.xml中设置

android:windowSoftInputMode="stateHidden|adjustResize"
//方法二:在代码中动态设置:

getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE | WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN);
  • 兼容视频播放

1)享受页面视频的完整播放体验需要做如下声明:
页面的Activity需要声明
android:configChanges="orientation|screenSize|keyboardHidden"
2)视频为了避免闪屏和透明问题,需要如下设置
a)网页中的视频,上屏幕的时候,可能出现闪烁的情况,需要如下设置:Activity在onCreate时需要设置:
getWindow().setFormat(PixelFormat.TRANSLUCENT);(这个对宿主没什么影响,建议声明)
在非硬绘手机和声明需要controller的网页上,视频切换全屏和全屏切换回页面内会出现视频窗口透明问题,需要如下设置
声明当前<item name="android:windowIsTranslucent">false为不透明。
特别说明:这个视各app情况所需,不强制需求,如果声明了,对体验更有利
c)以下接口禁止(直接或反射)调用,避免视频画面无法显示:

webview.setLayerType()
webview.setDrawingCacheEnabled(true);

其它参考文档:X5 接入文档


WebView相关文章推荐:

腾讯浏览服务X5内核集成
Android中WebView的JavaScript代码和本地代码交互的三种方式
WebView详解与简单实现Android与H5互调
WebView写入数据到 localStorage总结

以上是关于Native JsBridge源码解析 深入理解JsBridge的主要内容,如果未能解决你的问题,请参考以下文章

深入java并发包源码简介

深入理解Stream之foreach源码解析

Android EventBus源码解析, 带你深入理解EventBus

深入理解Android Handler机制(深入至native层)

深入理解Android Handler机制(深入至native层)

为自己搭建一个鹊桥 -- Native Page与Web View之间的JSBridge实现方式