app native和 h5的通信方式-- jsbridge
什么是移动端通信?
有哪些是需要了解的开发背景知识?
怎么样开始一个移动端项目开发和调试呢?
看这篇文档记录,够入门 go--->
1 IOS 和Android底层和js的交互原理
ios提供UIWebView 组件,是一个可加载网页的对象,提供类似浏览器的功能,可以通过js代码调用一些原生的功能,比如:获取GPS信息。
Safari浏览器的控件和原生 UIWebView不兼容,在ios8版本之后提供了WKWebView对象,提供了4个属性、8个api供js调用:
loading:是否处于加载中,
canGoBack:只读,是否可以接受后退
canGoForward: 只读,是否可接受向前
request: url 请求
loadData: 设置主页类型,MIME类型,文档编码,base url 。
loadRequest: 加载网络资源
loadHTMLString: 加载本地 html 资源
stopLoading: 停止加载
goBack: 后退
goForward: 前进
reload: 重新加载
stringByEvaluatingJavaScriptFromString: 执行一段 js 脚本,并返回执行结果。
2 IOS 系统
Native 调用 JavaScript的方法 (object-c swift)
是获取 stringByEvaluatingJavaScriptFromString 方法执行的放回结果。
// Swift webview.stringByEvaluatingJavaScriptFromString("Math.random()") // OC [webView stringByEvaluatingJavaScriptFromString:@"Math.random();"];
以上例子的函数都是相当于在 window 下的方法,所以如果js的方法放在 window 下是可以被 webview的接口调用的。
类似:
"Math.random()"
JS 调用 Native 方法
native没有现成的api给js调用,可以通过发送网络请求的方式,在 native 层得到通知。
UIWebView 层内发起网络请求,格式是这样的,例如 object-c 和 swift
jsbridge://methodName?param1=value1¶m2=value2 // js 调用原生的一般传参格式。
所以,在UIWebView层如果看到这样的schema就进行逻辑处理,不进行网络请求。
发起这种网络请求的方式,h5可以用 location.href 和 iframe 的方式。
location.href 会有点问题 连续发送多个请求时,在 Native 层只能接收到最后一次的调用。
所以使用 iframe发送请求。
var url = 'jsbridge://doAction?title=标题'; // 定义请求的url,设置好对终端有效的schema。方便拦截。 var iframe = document.createElement('iframe'); iframe.style.width = '1px'; iframe.style.height = '1px'; iframe.style.display = 'none'; iframe.src = url; document.body.appendChild(iframe); // 将iframe添加到document,发起请求 setTimeout(function() { iframe.remove(); // 只需要一次请求,所以需要把 iframe 标签从HTML文档中移除。 }, 100);
WebView 可以拦截这个请求并获取对应的参数,参考如下代码
func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool { print("shouldStartLoadWithRequest") // 开始处理这个请求 let url = request.URL let scheme = url?.scheme let method = url?.host // 获取host值,判断执行的动作,交给 Object-c 处理。 let query = url?.query // 获取执行的传参 if url != nil && scheme == "jsbridge" { print("scheme == \(scheme)") print("method == \(method)") print("query == \(query)") switch method! { case "getData": self.getData() case "putData": self.putData() case "gotoWebview": self.gotoWebview() case "gotoNative": self.gotoNative() case "doAction": self.doAction() case "configNative": self.configNative() default: print("default") } return false; } else { return true; } }
3 Android 系统
js 调用 native 的方式
在 Android 系统中有 2 种方式可以实现,js调用 native方法。好像还有第3中,重写console.log方法不明觉厉中。。
方法一:
通过schema方式,使用shouldOverrideUrlLoading
方法对url协议进行解析。这种js的调用方式与ios的一样,使用iframe来调用native代码。
方法二:
使用原生webview提供的接口,addJavaScriptInterface 方法来实现。
class JSInterface { @JavascriptInterface //注意这个代码一定要加上,Java 代码对类型方法的修饰。 public String getUserData() { return "---- native 定义的方法"; } }
webView.addJavascriptInterface(new JSInterface(), "AndroidJS");复制代码上面的代码就是在页面的window对象里注入了AndroidJS对象。在js里可以直接调用
console.log(AndroidJS.getUserData()) // ---- native 定义的方法 JSInterface 接口的方法。 对应在 Android WebView 方法中定义的被修饰后的方法。
native 调用 js 的方式
webView.loadUrl("javascript:Bridge.doSomething('hello.')");
调用 JavaScript 中的 Bridge.doSomething() 方法。
4 库的封装
js 调用 native 的封装
基于之前对 IOS 和Android 和js通信的了解,再封装一层,保证js代码操作系统兼容性。 https://juejin.im/post/599a58f6f265da247b4e756b 这段代码来自掘金的文章。
我再整理下逻辑并注释。^_^
HaveJsBridge = { doCall: function(functionName, data, callback) { var _this = this; // 这里其实是一个节流,不让你点太快或响应太快 if (this.lastCallTime && (Date.now() - this.lastCallTime) < 100) { setTimeout(function() { _this.doCall(functionName, data, callback); }, 100); return; } this.lastCallTime = Date.now(); data = data || {}; if (callback) { $.extend(data, { callback: callback }); } if (UA.isIOS()) { $.each(data, function(key, value) { // 每个 data key value 进行序列化 if ($.isPlainObject(value) || $.isArray(value)) { data[key] = JSON.stringify(value); } }); var url = Args.addParameter('jsbridge://' + functionName, data); var iframe = document.createElement('iframe'); iframe.style.width = '1px'; iframe.style.height = '1px'; iframe.style.display = 'none'; iframe.src = url; document.body.appendChild(iframe); setTimeout(function() { iframe.remove(); // 和上面的代码执行一样,清除dom }, 100); } else if (UA.isAndroid()) {
// 此处对接上面👆代码中的 webView.addJavascriptInterface 接口方法 window.androidJS && window.androidJS[functionName] && window.androidJS[functionName](JSON.stringify(data)); } else { console.error('未获取platform信息,调取api失败'); } } }
好,兼容性准备工作就绪,现在开始封装几个 常用的 js可以调用 native 的方法。
!!这几个方法都对应上面的 func webView 函数 👆!!
1 getData(datatype, callback, extra) H5从Native APP获取数据
JSBridge.getData('userInfo',function(data) { console.log(data); });
2 putData(datatype, data) H5告诉Native APP一些数据
JSBridge.putData('userInfo', { username: 'zhangsan', age: 20 });
3 gotoWebview(url, page, data) 打开相应网页传参,打开新的webview 窗口,可以再 webView函数中调用
JSBridge.gotoWebview('http://www.youzan.com', 'goodsDetail', { goods_id: 10000, title: '这是商品的标题', desc: '这是商品的描述' });
4 打开某个原生 Native APP 的页面
JSBridge.gotoNative('loginPage', { username: '张三' });
5 做一些操作
// 封装一个复制函数,比如点击复制 JSBridge.doAction('copy', { content: '这是要复制的内容' }); // 封装一个分享函数,比如点击分享到 哪哪哪。 JSBridge.doAction('share', { title: '分享标题', desc: '分享描述', link: 'http://www.youzan.com', imgs_url: 'http://wap.koudaitong.com/v2/common/url/create?type=homepage&index%2Findex=&kdt_id=63077&alias=63077' }); // 注意,这些代码还只是方法的伪代码实现不要当真。
5 Safari 上的调试
第一步: 首先需要打开Safari的调试模式,在Safari的菜单中,选择“Safari”→“Preference”→“Advanced”,勾选上“Show Develop menu in menu bar”选项
第二步: 打开iPhone模拟器的调试模式,在真机或iPhone模拟器中打开设置界面,选择“Safari”→“高级”→“Web检查器”,选择开启
第三步: 将真机通过USB连上电脑,或者开启模拟器,Safari的“Develop”菜单下便会多出相应的菜单项
第四步: Safari连接上UIWebView之后,我们就可以直接在Safari中直接修改HTML、CSS,以及调试Javascript
什么是 UIWebView 启动呢?
这个是Android开发需要Java环境,起一个Android的项目,在手机或模拟器上安装这个项目,就跑起来了。
6 参考文档
UIWebView https://developer.apple.com/documentation/uikit/uiwebview
WKWebView
Github JSBridge https://github.com/marcuswestin/WebViewJavascriptBridge
搭建简单webview调试 https://www.jianshu.com/p/4f783cd34ab1