Web 与原生应用的常见通信方式
前言
在移动web开发过程中,一定会遇到web端与APP端交互的情况。下面就来分析下,web端到底是如何与APP端实现交互的。
在APP端,iOS和Andriod的实现方式各不一样。
大家都知道,我们的javascript大部分是运行在浏览器上的,这时浏览器的环境就是宿主环境(host environment)则给我们的javascript提供了window,navigator等宿主对象。
Andriod
在Android上,实现的方式就和上面这种方式类似,就是在webview上注册一个全局变量,然后我们再js直接调用即可。下面即表示在全局环境下定义了一个对象androidEnv :
webview.addJavascriptInterface(object, "androidEnv");web调用Andriod
Andriod在webview这个类里面定义的方法,我们在js都可以直接调用,这样就实现了web到Andriod的单向交互了,例如:
AndriodEnv.sayHi(); //注意:sayHi是Andriod定义的方法Andriod调用web
然后,如果我们要实现Andriod到web的单向交互呢,道理也一样。即是js定义一个全局的函数,然后Andriod就可以直接调用了:
function globalFunc(){
return 'hello Andriod';
}iOS
相反在iOS上,则没这么方便了,需要用到一个叫WebViewJavascriptBridge的桥接中间件。
在js上我们需要判断这个bridge是否已经注册,如果有就直接拿来使用;否则就监听一下brideg的变化,然后再使用。因此我们先封装好一个方法:
function connectWebViewJavascriptBridge(callback) {
if (window.WebViewJavascriptBridge) {
callback(WebViewJavascriptBridge);
}
else {
document.addEventListener('WebViewJavascriptBridgeReady', function() {
callback(WebViewJavascriptBridge);
}, false);
};
}之后我们用一个callback来处理这个bridge:
connectWebViewJavascriptBridge(function(bridge) {
bridge.init(function(data, responseCallback) {});
});到这个时候,基础工作就做完了,接下来就是web与iOS之间的交互了。
web调用iOS
bridge.callHandler('ObjC Echo', {'key':'value'}, function responseCallback(responseData) {
console.log("JS received response:", responseData)
})其中,ObjC Echo是iOS上的对应方法(其实并不是函数,只是一个token),{'key':'value'}则是web传到iOS的参数,responseData则是iOS回传的数据。
这里还有另外一种方法,就是我们可以直接利用bridge的send方法,直接将所有东西都send过去给iOS即可,例如:
var param = {
"functionName" : "share",
"params":{
"title" : opt.title,
"desc" : opt.desc
}
};
connectWebViewJavascriptBridge(function(bridge) {
bridge.send(param);
});send可以接受两个参数,第二个参数就是回调函数了:
connectWebViewJavascriptBridge(function(bridge) {
bridge.send(param, function(data){
callback(data);
});
});iOS调用web
bridge.registerHandler('JS Echo', function(data, responseCallback) {
console.log("JS Echo called with:", data)
responseCallback(data)
})这里的JS Echo则是JS上注册的一个方法名,当iOS执行玩这个方法,我们就可以马上监听到并接受到一些数据data,之后我们还可以执行responseCallback回调iOS告诉它我们的处理情况。
说说js代码
知道如何实现了之后,就涉及到软件工程的问题了。要如何编写代码结构,最优化实现我们的需求才是重中之重。首先先贴上一份代码,大家可以先考虑如何优化再往下看。
jAPP = {
getUserName: function(){
if(inAPP) {
if(iOS){
//...
}
if(Andriod){
//...
}
}
else {
//...
console.log('请在APP内打开')
}
},
getUserId: function(){
if(inAPP) {
if(iOS){
//...
}
if(Andriod){
//...
}
}
else {
//...
console.log('请在APP内打开')
}
}
}我认为的缺点:
重复判断设备信息,因为设备信息可以理解成
常量,一经获取就不会更改,所以我们可以用初始化分支来优化。重复判断是否在APP内。同样可以采用
初始化分支来优化,也就是说不在APP内的话,调用的方法都不需要初始化了。console.log没有意义,只有在测试环境下能有,上生产应该去除。
优化
var ua = navigator.userAgent().toLowerCase();
var inIOS = !!( ua.indexOf('iphone') > -1 || ua.indexOf('ipad') > -1 );
var inAndriod = !!( ua.indexOf('andriod') > -1 );
var inAPP = !!( ua.indexOf('your APP token') > -1 );
var jAPP = {};
if(inAPP) {
if(inIOS) {
jAPP.getUserId = function(){
//..
};
jAPP.getUserName = function(){
//..
};
}
else if(inAndriod) {
jAPP.getUserId = function(){
//..
};
jAPP.getUserName = function(){
//..
};
}
}初始化分支的意思就是说在js脚本开始执行的的时候就确定好分支,而不是每次执行的时候才去确定分支。好处在于不用重复判断分支。
如果你有更好的做法,希望你可以在下方给大家分享一下。谢谢。