狭义Hybridjavascript
也是如今你们广泛认知的,Hybrid就是一种给 WebView 增长一些js通讯能够调用原生API的方式前端
广义Hybridjava
我可否认为,只要是前端的开发思路与客户端原生的开发思路相结合,就认为他是一种 Hybrid?git
我可否认为,经过原生的配合,把本来js or 前端开发作不到的事情作到了,用原生的方式加强了本来的前端技术能力,是否就是一种 Hybrid?github
我可否认为,不管是 WebView+Bridge 也好,RN相似的原生渲染框架也好,小程序也好,某种意义上讲,他们都算 Hybrid?web
由于Hybrid本是一个面向业务服务的东西,若是业务的野心足够大,WebView 容器的想象空间应该是在能力上与RN/小程序看齐的,没错,WebView 在 Hybrid 的支持下,不单纯是设计几个 Bridge 调用几个原生 API 的事,我其实在很多聊天群里深度聊过这个话题,泛前端动态化这个方向上,各类技术轮子都是一脉相承通着的,因此你看RN or 小程序是一个大厂新作的重型轮子?在我看来他们都是一回事,彻底能够拆解RN中的每一个环节,把RN号称比 WebView 好的原生渲染/原生组件拆解融入 WebView,我也能够学习小程序保持 Html/CSS/JS 的开发方式(固然我知道小程序是WXML/WXSS),而非RN那样统一用JSX开发。我甚至还能把RN与小程序都没有的动态bridge融入到Hybrid容器中去,What’s more? 还有更多能够开放的脑洞。数据库
这种拆解不是说能够作到把全部框架优势塞在一个大而全的框架里就完事的,各类优化方案的选择背后必定带来的是一些取舍。谁来决定取舍,业务决定,若是本身能深度把握这里面的设计思想,就不用在意什么新的轮子新的框架,取其设计优势(优势必定带来取舍,若是选择这个有点意味着也要选择他的取舍),融入本身的业务之中。json
一个标准的WebView容器要具有哪些基础的功能需求,来知足常规的 hybrid-webview 开发呢?小程序
Android 调用 JS 有两种方式,都是经过
WebView
的方法:
bash
webview.loadUrl()
webview.evaluateJavascript()
两者区别:
loadUrl()
会刷新页面,evaluateJavascript()
则不会使页面刷新,因此evaluateJavascript()
的效率更高
loadUrl()
得不到 js 的返回值,evaluateJavascript()
能够获取返回值
evaluateJavascript()
在 Android 4.4 以后才可使用在
methods
中定义一个供 Android 调用的方法callJsFunction(str)
, 并可接收一个参数str
,而后改变页面中的文字。若是只是在
methods
中定义方法,原生调用会找不到这个方法。因此要在页面加载的时候将方法挂载在window
上,这样WebView
就能够拿到此方法了。注意,这步很重要必定要写!loadUrl() 实现:tbsWebView.loadUrl("javascript:callJsFunction('soloname')");
evaluateJavascript() 实现:
tbsWebView.loadUrl("javascript:callJsFunction('soloname')");
最基础功能,WebView 各类想要调用原生能力都经过这个设计来通知原生,不管是打开新页面新路由,仍是弹个 Tips 框,仍是执行 IAP 购买,仍是打开摄像头等等。
对于JS调用Android代码的方法有3种:
WebView
的 addJavascriptInterface()
进行对象映射WebViewClient
的 shouldOverrideUrlLoading()
方法回调拦截 urlWebChromeClient
的onJsAlert()
、onJsConfirm()
、onJsPrompt()
方法回调拦截JS对话框alert()
、confirm()
、prompt()
消息新建类 JsJavaBridge
public class JsJavaBridge {
private Activity activity;
private WebView webView;
public JsJavaBridge(Activity activity, WebView webView) {
this.activity = activity;
this.webView = webView;
}
@JavascriptInterface
public void onFinishActivity() {
activity.finish();
}
@JavascriptInterface
public void showToast(String msg) {
ToastUtils.show(msg);
}
}
复制代码
而后经过 WebView
设置 Android
类与 JS 代码的映射
tbsWebView.addJavascriptInterface(new JsJavaBridge(this, tbsWebView), "$App");
复制代码
这里将类 JsJavaBridge
在 JS 中映射为了 $App
,因此在 Vue 中能够这样调用 $App.showToast("哈哈,我是js调用的")
。
仍是在基础功能之上,若是 WebView 是想要获取一些只有原生才有的数据,好比读原生数据库,查看原生设备网络/磁盘等硬件情况,须要在上面的功能下还额外回调给 WebView。
对JS消息体进行改造,增长用于处理回调相关的数据字段 callbackId 与 callbackFunction
对 callbackId 与 callbackFunction 进行赋值,callbackId 是一个保证每次通讯都惟一的一个id值 getNextCallbackID ,大概思路能够是用时间戳+必定程度的随机小数来进行生成,思路不深刻展开了。 callbackFunction 这里咱们先写 window.callbackDispatcher
后面会提到这个入口是怎么操做的。
这里有一步最最重要的操做就是,this.msgCallbackMap[callbackid] = callback;
会把 JS 业务的回调函数,保存在一个全局可处理的回调字典之中,而 Key 就是这个惟一ID callbackId,这样当 客户端发起回调的时候,你才能找到对应的 JS Function
咱们会按着下面这种方式去拼接 JS 而后用 evaluateJavaScript:
来注入调用JS,在客户端拼接出一段 JS 代码,但若是 params 这个数据中存在必定特殊字符好比 \r \n \f
等等,这些特殊字符会破坏 JS 的代码结构,打破本来的 JS 语法,这块要很是当心,尤为是你要传递大型嵌套字典数据的时候,简单的测试数据这个问题是没法暴露出来的,若是 JS 代码结构被破坏,那么全部通讯 JS 的方法就失效了。Native会把 callbackId callbackFuction ResultString 拼接成以下 JS 代码,注入回 WebView
window.callbackDispatcher('12345callbackid','{\'result\':\'result\'}');复制代码
那么前端要作的就是准备好对应的函数,在window的对象上,挂上 callbackDispatcher 这个函数,这就是为啥我一开始说 callbackFunction 写死 window.callbackDispatcher
的缘由,客户端用这个字符串,拼出了 JS 代码,这个 JS 代码执行的时候,就恰好window下有这么一个函数接着
window.callbackDispatcher: function (callbackId, resultjson) {
var handler = this.msgCallbackMap[callbackId];
if (handler && typeof (handler) === 'function') {
console.log(resultjson);
var resultObj = resultjson ? JSON.parse(resultjson) : {};
handler(resultObj);
}}复制代码
当Native已经成功回调到 JS 了,那么就用 callbackId 在刚才保存的回调字典里找到要回调的方法,而后把传过来的 resultjson 用 JS 的 JSON.parse 反序列化成字典,而后用找到的回调方法把数据传递进去。@