android 混编JsBridge的原理和实现

首先我们先要知道

js调用Android的方法有以下四种:
  1. WebView的JavascriptInterface
  1. WebViewClient.shouldOverrideUrlLoading()
  1. WebChromeClient.onConsoleMessage()
  1. WebChromeClient.onJsPrompt()


1. JavascriptInterface
JavascriptInterface是Android官方提供的工js和Native通信方案。其实现如下:
  1. 实现一个java类,供js调用
    public class JavascriptInterface {@JavascriptInterface
     public void showToast(String toast) {             
        Toast.makeText(MainActivity.this, toast, Toast.LENGTH_SHORT).show();
     }
    }
    
  1. 在webView中注册这个类:
webView.addJavascriptInterface(new JavascriptInterface(), "javascriptInterface");
  1. 在js中直接调用这个接口:
function showToast(text){
 window.javascriptInterface.showToast(text);
}
但是webView有个安全漏洞: WebView中接口隐患与手机挂马利用。 在Android4.2后,这个漏洞被修复;但是考虑到兼容性的问题,这个方案基本不被采用。


2. WebViewClient.shouldOverrideUrlLoading()
这个方法是拦截所有webView的跳转,页面可以构造一个特殊格式的Url跳转,shouldOverrideUrlLoading拦截Url后判断其格式,然后Native就能执行自身的逻辑了。
 public class CustomWebViewClient extends WebViewClient {@Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
      if (isJsBridgeUrl(url)) {
        // JSbridge的处理逻辑
        return true;
      }
      return super.shouldOverrideUrlLoading(view, url);
    }
  }

3. WebChromeClient.onConsoleMessage()
在js中执行 console.log(), 会进入Android的WebChromeClient.consoleMessage()回调。
  public class CustomWebChromeClient extends WebChromeClient {
@Override
    public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
      super.onConsoleMessage(consoleMessage);
      String msg = consoleMessage.message();//Javascript输入的Log内容
    }
  }
4. WebChromeClient.onJsPrompt()
和3类似,在js中执行 alert、confirm和prompt这三个方法时,会进入Android的 onJsAlert、onJsConfirm和onJsPrompt回调。 由于prompt的使用频率较低,因此通常采用prompt实现js和Native的通信。


Android调用js
Android调用js接口的方法只有一个:WebView.loadUrl():
 webView.loadUrl('javascript: ' + ...);
Android中的JSBridge是H5与Native通信的桥梁,至于demo在http://blog.csdn.net/roshen_android/article/details/73825781已经详细说明了,大家可以下载demo去看看

当你下载完,看完demo后你会发现嵌完所有的类后,只需简单的调用BridgeWebview的registerHandler(js调用Native),callHandler(Native调用js)这2个类就能实现H5与Native通信。


先来看看registerHandler


/**
 * 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调用native的请求添加到一个

Map<String, BridgeHandler> messageHandlers = new HashMap<String, BridgeHandler>();

中去,然后你会发现messageHandlers最终还是去到

BridgeHandler handler;
if (!TextUtils.isEmpty(m.getHandlerName())) {
   handler = messageHandlers.get(m.getHandlerName());
} else {
   handler = defaultHandler;
}
if (handler != null){
   handler.handler(m.getData(), responseFunction);
}

这就是js调用Native回调回来的,至于这个回调可以看到


public BridgeWebViewClient(BridgeWebView webView) {
    this.webView = webView;
}

@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
    try {
        url = URLDecoder.decode(url, "UTF-8");
    } 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);
    }
}


,下面来看看Native调用js,该调用主要是callHandler

/**
	 * call javascript registered handler
	 *
     * @param handlerName
	 * @param data
	 * @param callBack
	 */
	public void callHandler(String handlerName, String data, CallBackFunction callBack) {
        	doSend(handlerName, data, callBack);
	}
一步一步点下去你会发现最终去到

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()) {
           this.loadUrl(javascriptCommand);
       }
   }



看到红色的字体没,就是Native唯一调用js的方法。


-----------------------------------------------------------------------------------------------------------------------------------------------

总结了下,JsBridger无非都是为我们搭建了更好的桥梁,让我们更好的优化hybrid的架构。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值