JSBridge——如何与原生打交道

1,822 阅读6分钟

导语

随着移动端领域的快速发展,业务场景愈发复杂,为减少开发量和投入成本,混合应用(Hybrid App)占据了不少市场。混合应用承载H5页面的容器就是webview,前端人员在开发过程中或多或少都需要与原生(Native App)之间交互,这个交互的桥梁就叫做JSBridge。同样也有一套通用的调试方法来确保JSB通信顺利。

什么是WebView

WebView是原生应用用来展示网页的view组件,本质上就是一款内置了webkit内核的无头浏览器(headless browser),提供了例如页面前进后退、放大缩小、音频控制等一般浏览器具备的功能。

可以将WebView想象成html里面的iframe标签,h5页面与原生应用的通信过程就像是iframe父子页面之间的通信,只不过iframe使用postMessage和message事件处理,而WebView使用的是JSBridge。

什么是JSBridge

JSBridge是支持原生应用与H5应用双向通信的“桥梁”。

不支持在 Docs 外粘贴 block

  • 原生->h5:向h5应用通知原生应用的相关状态,触发h5页面的内容更新、消息发送、音频播放等
  • h5->原生:向原生应用通知h5应用需要使用的功能,比如使用摄像头、使用gps、唤起app等 因为原生应用与h5应用各自运行在独立的环境中,他们通信的方式就像前端JSONP跨域请求一样,通过原生暴露的一些特性让双端进行联系。

不支持在 Docs 外粘贴 block

Android WebView

Android WebView以4.4为分界点,采用了不同的内核:

  • Android 4.4前:基于Webkit内核,H5的很多新特性不支持,且存在适配成本高、不安全、不稳定、耗流量、速度慢、视频播放差、文件能力差等问题。
  • Android 4.4后:基于Chromium内核(google在webkit上的fork出来的项目),很多新的规范被支持,例如WebGL,Canvas2D,CSS3以及其他很多的HTML5特性,同时大大提升了WebView组件的性能。

js调用java

Android Webview共提供过三种JS调用java的接口:

  • JavascriptInterface
  • WebViewClient.shouldOverrideUrlLoading() 【官方已废弃
  • WebChromeClient.onXXX()

JavascriptInterface

这是 Android 提供的 JS 与 Native 通信的官方解决方案。

  • 首先Android RD需要实现一个类给 JavaScript 调用。
public class WebAppInterface {
    @JavascriptInterface
    public void foo(String str) {
        // doing something
    }
}
  • 将这个WebAppInterface类添加到 WebView 的 JavaScriptInterface 中。
WebView webView = (WebView) findViewById(R.id.webview); 
// 这里的Android会被当做一个变量,注入到页面的window中。
webView.addJavascriptInterface(new WebAppInterface(this), "Android"); 
  • 最后就可以在 JS 中调用 Native 的方法了。
function callFoo(str: string) {
    Android.foo(str);
}

WebViewClient.shouldOverrideUrlLoading()

拦截webview内的url变更,例如iframe.src或location.href,若设置WebViewClient且该方法返回true,则可由代码自定义逻辑。

一般采用URL Scheme的方式,它一种类似于URL的链接,原本用于唤起各类app而设计,主要特征是protocol和host一般是自定义的。

所以可以事先约定好特殊格式的 URL Scheme,触发shouldOverrideUrlLoading拦截后,判断其格式是否符合,然后 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);
    }
}

WebChromeClient.onXXX()

WebChromeClient支持监听浏览器的很多方法,比如:

  • Alert -> WebChromeClient.onJsAlert
  • Confirm -> WebChromeClient.onJsConfirm
  • Prompt -> WebChromeClient.onJsPrompt
  • console.xxx -> WebChromeClient.onConsoleMessage
/**
 * 当网页调用alert()来弹出alert弹出框前回调,用以拦截alert()函数
 */
public boolean onJsAlert(WebView view, String url, String message,JsResult result)
/**
 * 当网页调用confirm()来弹出confirm弹出框前回调,用以拦截confirm()函数
 */
public boolean onJsConfirm(WebView view, String url, String message,JsResult result)
/**
 * 当网页调用prompt()来弹出prompt弹出框前回调,用以拦截prompt()函数
 */
 public boolean onJsPrompt(WebView view, String url, String message,String defaultValue, JsPromptResult result) 
 /**
 * 打印 console 信息
 */
 public boolean onConsoleMessage(ConsoleMessage consoleMessage)

以alert为例:

  • H5页面调用alert方法
window.alert(message); 
  • 触发执行WebChromeClient.onJsAlert(),alert方法的入参message就是onJsAlert中的message参数
public class CustomWebChromeClient extends WebChromeClient {
  @Override
  public boolean onJsAlert(
    WebView view,
    String url,
    String message,
    JsResult result
    ) {
        // doing something
        // 返回值true,表示拦截成功,false表示拦截失败,继续弹窗
        return true;
  }
}

Java 调用 js

根据android版本,有2种方式:

  • loadUrl:全部支持
  • evaluateJavascript:安卓4.4版本之后

loadUrl(String)

webView.loadUrl("javascript:" + javaScriptString); 

WebView.evaluateJavascript(String, IValueCallback)

webView.evaluateJavascript("javascript:" + javaScriptString, new ValueCallback<String>() {
    @Override
    public void onReceiveValue(String value) {
      // 执行完成后的回调
    }
});

IOS WebView

IOS8 以前使用的是UIWebview,但是它存在不少问题:加载速度慢、内存泄漏、内存占用高、超出内存限制会被kill掉或者强制触发gc。感兴趣可阅读《IOS混合应用切换app闪屏bug总结》

所以在WWDC 2014 大会上,推出了WKWebView。它代替了 UIKit 中的 UIWebView 和 AppKit 中的 WebView,提供了统一的跨双平台 API。拥有 60fps 滚动刷新率、内置手势、高效的 app 和 web 信息交换通道、和 Safari 相同的 JavaScript 引擎。

使用messageHandlers 调用swift

  • 设置messageHandler
[self.webView.configuration.userContentController addScriptMessageHandler:self name:@"showAlert"];

// 为了避免循环引用,导致控制器无法被释放,还需要移除
[self.webView.configuration.userContentController removeScriptMessageHandlerForName:@"showAlert"];
  • webview会向window全局对象上注入webkit,调用messageHandlers即可实现调用swift
window.webkit.messageHandlers.showAlert.postMessage()

使用UserScript来注入 JavaScript

WKUserScript 允许在正文加载之前或之后注入到页面中,它以 JavaScript 源码形式初始化,初始化时还可以传入是在加载之前还是结束时注入,以及脚本影响的是这个布局还是仅主要布局。举一个简单例子:

let source = "document.body.innerHTML = 'helloWorld'";
// 注入脚本 在文档加载完成后执行
let userScript = WKUserScript()
let userScript = WKUserScript(source: source, injectionTime: WKUserScriptInjectionTimeAtDocumentStart, forMainFrameOnly: true)
let userContentController = WKUserContentController()
userContentController.addUserScript(userScript)

let configuration = WKWebViewConfiguration()
configuration.userContentController = userContentController
self.webView =WKWebView(frame: self.view.bounds, configuration: configuration)
    // url scheme
    this.sendMessageQueue.push(msgJSON);

    this._dispatchUrlMsg("" + this.bridgeScheme + this.dispatchMsgPath);
  }
} catch (e) {
  console.error(e);
}

Webview以及JSBridge调试

安卓

安装chrome

Google Chrome 网络浏览器

启动开发者选项

  1. 谷歌手机开启「开发者选项」的方式,以小米为例是多次点击miui版本号开启
  2. 在「开发者选项中」打开「USB调试」

打开chrome的检查器

  1. 数据线连接至mac上
  2. 打开Chrome, 地址栏输入chrome://inspect
  3. 在Devices选项, 打开发现USB设备选择

在这里插入图片描述

  1. 打开待调试webview页面,即可看到出现在设备列表中
  2. 点击其下方的inspect按钮,即可进入「控制台」调试

ios

下载安装Safari Technology Preview

直接在官网下载安装即可,Safari Technology Preview - Safari - Apple Developer

手机或pad开启web检查器

  1. 连接数据线到mac上。
  2. 在「设置」当中找到「Safari浏览器」,点击进入。

在这里插入图片描述

  1. 下滑找到「高级」,开启「web检查器」

在这里插入图片描述

(写文档的时候没有真机,用模拟器截图,理解意思就行~~~)

使用Safari开发工具调试

  1. 打开Safari Technology Preview,点击菜单中的「开发」,找到自己连接的机型

在这里插入图片描述

  1. 「信任」设备连接 真机设备会弹出对话框,选择「信任」。再进行第一步的操作。
  2. 进入「控制台」愉快的调试

JSBridge调试

在控制台「console」中,通过JSBridge.trigger(event, params)触发jsb,可以判断jsb是否通畅。

注意事项

  • 老旧设备可能无法这样调试,可以使用vconsole
  • 确保连接线是数据线,而不是充电线
  • 安卓可能需要安装「ADB」拓展程序
  • 如果还不行,很大概率是你没有科学上网

相关文档