本文原创:wangtiegangjavascript
Hybrid App,俗称混合应用,即混合了 Native技术 与 Web技术 进行开发的移动应用。兼具" Native App良好用户交互体验的优点 "和" Web App跨平台开发的优点 "。html
Hybrid APP底层依赖于Native提供的容器(UIWebview),上层使用Html&Css&JS作业务开发,底层透明化、上层多多样化,这种场景很是有利于前端介入,很是适合业务快速迭代。前端
Hybrid App,俗称混合应用,即混合了 Native技术 与 Web技术 进行开发的移动应用。如今比较流行的混合方案主要有三种,主要是在UI渲染机制上的不一样:java
经过JSBridge,H5页面能够调用Native的api,Native也可调用H5页面的方法或者通知H5页面回调,从而实现双向通讯,以下图 android
基于 WebView 的机制和开放的 API, 实现这个功能有三种常见的方案:web
class JsObject { @JavascriptInterface public String toString() { return "injectedObject"; } } webview.getSettings().setJavaScriptEnabled(true); webView.addJavascriptInterface(new JsObject(), "injectedObject"); webView.loadData("", "text/html", null); webView.loadUrl("javascript:alert(injectedObject.toString())"); 复制代码
可是此方法使用须要注意:json
WebView 中的 prompt/console/alert 拦截,一般使用 prompt,由于这个方法在前端中使用频率低,比较不会出现冲突;api
WebView URL Scheme 跳转拦截;浏览器
第二三种机制的原理是相似的,都是经过对 WebView 信息冒泡传递的拦截,从而达到通信的,目前多使用后两种方法。缓存
实现过程以下:
经过特定的参数转换方法,将传入的数据,方法名一块儿,拼接成一个url scheme,如
// 基本有用信息就是后面的callbackId,handlerName与data API_Name:callbackId/handlerName?param0=xxx¶m1=yyy 复制代码
经过上面的定义来对应Native层不一样的方法,并传递参数
咱们会使用建立 iframe 发送请求的方式。如
//建立隐藏iframe过程 var messagingIframe = document.createElement('iframe'); messagingIframe.style.display = 'none'; document.documentElement.appendChild(messagingIframe); //触发scheme messagingIframe.src = uri; 复制代码
onJsAlert()
,onJsConfirm()
,onJsPrompt()
,方法回调拦截对话框消息var data = { action:'xxxx', params:'xxxx', callback:'xxxx', }; var jsonData = JSON.stringify([data]); //发起弹框 window.prompt(jsonData); 复制代码
一般考虑到安全性,须要在客户端中设置域名白名单或者限制,避免公司内部业务协议被第三方直接调用。
对应上面消息的发送,消息的拦截方法以下:
客户端能够经过 API 对 WebView 发出的请求进行拦截:
IOS上: shouldStartLoadWithRequest
Android: shouldOverrideUrlLoading
当解析到请求 URL 头为制定的协议时,便不发起对应的资源请求,而是 解析参数,并进行相关功能或者方法的调用,完成协议功能的映射。
以onJsPrompt为例,客户端能够经过 API 对 WebView 发出的请求进行拦截:
IOS上: runJavaScriptTextInputPanelWithPrompt
Android: WebChromeClient.onJsPrompt
处理方式同上
因为 WebView 对 URL 会有长度的限制,所以常规的经过 scheme参数 进行传递的方式便具备一个问题,即当须要传递的参数过长时,可能会致使被截断,例如传递base64或者传递大量数据时。因为 Native 能够直接调用 JS 方法并直接获取函数的返回值,所以咱们只须要对每条协议标记一个惟一标识,并把参数存入参数池中,到时客户端再经过该惟一标识从参数池中获取对应的参数便可。
当消息发送到Native 层后,咱们便须要处理对应的回调,一样须要的处理过程:
因为 Native 能够算做 H5 的宿主,所以拥有更大的权限,上面也提到了 Native 能够经过 WebView API直接执行 Js 代码。
// IOS webview.stringByEvaluatingJavaScriptFromString("alert('NativeCall')") // Android 4.4- webView.loadUrl("javascript:JSBridge.trigger('NativeCall')") // Android 4.4+ mWebView.evaluateJavascript("javascript:JSBridge.trigger('NativeCall')", new ValueCallback<String>() { @Override public void onReceiveValue(String value) { //此处为 js 返回的结果 } }); 复制代码
android系统在低于4.4时,evaluateJavascript 是没法使用的,所以单纯的使用 loadUrl 没法获取 JS 返回值,这时咱们须要使用前面提到的 prompt 的方法进行兼容,让 H5端 经过 prompt 进行数据的发送,客户端进行拦截并获取相应数据
在线获取h5资源总会遇到网络问题,对于一些不会常常改变的资源放到本地缓存,能够提升访问速度和稳定性。
一般来讲,H5资源分为两种,常常更新的业务代码 和 不常常更新的框架、库代码和公用组件代码,为了实现离线资源的共享,在H5打包时能够采用分包的策略,将公用部分单独打包,在本地也是单独存放
为了实现同一资源的线上和离线访问, Native 层须要对 H5 请求的资源进行判断,究竟是从线上获取仍是本地获取,这里咱们暂时称之为 LocalUrlRouter。
接下来就是如何获取离线资源了,咱们上面提到了 LocalUrlRouter,它负责线上资源到本地资源映射,咱们借鉴已有的映射规则:H5开发完成后会扫描H5项目而后生成一份线上资源和本地资源路径的映射表(source-router.json)。每次请求资源时,LocalUrlRouter 会检查映射表,若是本地有离线资源,则会返回本地资源,不然经过http请求获取线上资源。
还能够完善的地方资源包的更新,映射表对资源的缓存也能够动态的增长等