完整解决方案:H5与Native交互之JSBridge技术

优采云 发布时间: 2022-09-24 17:10

  完整解决方案:H5与Native交互之JSBridge技术

  许多做过混合开发的人都知道 Ionic 和 PhoneGap 等框架。这些框架在web的基础上包裹了一层Native,然后利用Bridge技术让js调用视频、位置、音频等功能。本文就是介绍Bridge这一层的交互原理。通过阅读本文,可以了解js、ios和android的底层通信原理,以及JSBridge的封装技术和调试方法。

  一、原则

  下面介绍IOS与Android和Javascript的底层交互原理

  IOS

  在解释原理之前,我们先来了解一下iOS的UIWebView组件。我们来看看苹果官方的介绍:

  您可以使用 UIWebView 类在您的应用程序中嵌入 Web 内容。为此,您只需创建一个 UIWebView 对象,将其附加到窗口,并向其发送加载 Web 内容的请求。您还可以使用该类在网页历史记录中前后移动,甚至可以通过编程方式设置一些网页内容属性。

  上面的意思是UIWebView是一个可以加载网页的对象,它具有浏览记录功能,加载的网页内容是可编程的。说白了,UIWebView 有一个类似浏览器的功能。我们可以用它来打开页面,做一些自定义的功能,比如让js调用某个方法来获取手机的GPS信息。

  但需要注意的是,Safari浏览器使用的浏览器控件和UIwebView组件并不相同,两者在性能上存在较大差距。幸运的是,苹果在发布 iOS8 时,添加了一个 WKWebView 组件。如果你的APP只考虑支持iOS8及以上,那么你可以使用这个新的浏览器控件。

  原生 UIWebView 类提供以下属性和方法

  属性:

  方法:

  Native(Objective-C 或 Swift)调用 Javascript 方法

  Native通过UIWebView组件的stringByEvaluatingJavaScriptFromString方法调用Javascript语言,返回js脚本的执行结果。

  // Swift

webview.stringByEvaluatingJavaScriptFromString("Math.random()")

// OC

[webView stringByEvaluatingJavaScriptFromString:@"Math.random();"];

  从上面的代码可以看出,它实际上是调用了窗口下的一个对象。如果我们想让native调用我们js写的方法,那么这个方法必须是window下可以访问的。但是从全局来看,我们只需要暴露一个JSBridge这样的对象就可以调用native,所以这里我们可以对native代码做一个简单的封装:

  //下面为伪代码

webview.setDataToJs(somedata);

webview.setDataToJs = function(data) {

webview.stringByEvaluatingJavaScriptFromString("JSBridge.trigger(event, data)")

}

  Javascript 调用本机(Objective-C 或 Swift)方法

  反过来,Javascript调用Native,没有现成的API可以直接使用,需要通过一些方法间接实现。 UIWebView有一个特点:所有在UIWebView发起的网络请求都可以通过delegate函数在Native层得到通知。这样,我们就可以在UIWebView中发起一个自定义的网络请求,通常是这样的格式:jsbridge://methodName?param1=value1&param2=value2

  所以在UIWebView的delegate函数中,只要找到以jsbridge://开头的地址,就不会加载内容,而是执行相应的调用逻辑。

  有两种方式发起这样的网络请求:1.通过localtion.href; 2. 通过 iframe;

  location.href有一个问题,就是如果我们连续多次修改window.location.href的值,Native层只能接收到最后一个请求,而之前的请求会被忽略。

  使用iframe方法,以唤起Native APP的分享组件为例,简单的闭包如下:

  var url = 'jsbridge://doAction?title=分享标题&desc=分享描述&link=http%3A%2F%2Fwww.baidu.com';

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();

}, 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

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

}

}

  安卓

  在android中,native和js的通信方式与ios类似,android中也支持ios中的schema方式。

  javascript 调用原生方法

  目前android中调用native的方式有3种:

  1.使用shouldOverrideUrlLoading方法通过schema方法解析url协议。这个js的调用方式和ios一样,都是用iframe调用原生代码。

  2.通过将原生js代码直接注入webview页面,使用addJavascriptInterface方法实现。

  

  在android中的实现如下:

  class JSInterface {

@JavascriptInterface //注意这个代码一定要加上

public String getUserData() {

return "UserData";

}

}

webView.addJavascriptInterface(new JSInterface(), "AndroidJS");

  以上代码将AndroidJS对象注入到页面的window对象中。可以直接在js中调用

  alert(AndroidJS.getUserData()) //UserDate

  3.使用 prompt、console.log 和 alert 方法。这三个方法是js原生的,可以在android webview层重写。一般我们用prompt,因为这个在js里用的不多,和native交流的副作用也少。

  class YouzanWebChromeClient extends WebChromeClient {

@Override

public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {

// 这里就可以对js的prompt进行处理,通过result返回结果

}

@Override

public boolean onConsoleMessage(ConsoleMessage consoleMessage) {

}

@Override

public boolean onJsAlert(WebView view, String url, String message, JsResult result) {

}

}

  Native 调用 javascript 方法

  在android中,使用webview的loadUrl调用,如:

  // 调用js中的JSBridge.trigger方法

webView.loadUrl("javascript:JSBridge.trigger('webviewReady')");

  二、库的封装js调用原生封装

  上面我们了解了js和native通信的底层原理,所以我们可以封装一个基本的通信方法doCall来屏蔽android和ios的区别。

  YouzanJsBridge = {

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) {

if ($.isPlainObject(value) || $.isArray(value)) {

data[key] = JSON.stringify(value);

}

});

var url = Args.addParameter('youzanjs://' + 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();

}, 100);

} else if (UA.isAndroid()) {

window.androidJS && window.androidJS[functionName] && window.androidJS[functionName](JSON.stringify(data));

} else {

console.error('未获取platform信息,调取api失败');

}

}

}

  在android端,我们使用addJavascriptInterface方法注入了一个AndroidJS对象。

  项目常用方法抽象

  在项目的实践中,我们逐渐抽象出一些通用的方法,基本可以满足项目的需要。如下:

  1.getData(datatype, callback, extra) H5从Native APP获取数据

  使用场景:当H5需要从Native APP获取一些数据时,可以调用该方法。

  参数类型是否必填示例值说明

  数据类型

  字符串

  是的

  

  用户信息

  数据类型

  回调

  功能

  是的

  回调函数

  额外的

  对象

  没有

  传递给 Native APP 的数据对象

  示例代码:

  JSBridge.getData('userInfo',function(data) {

console.log(data);

});

  2.putData(datatype, data) H5告诉Native APP一些数据

  使用场景:H5告诉Native APP一些数据,可以调用这个方法。

  参数类型是否必填示例值说明

  数据类型

  字符串

  是的

  用户信息

  数据类型

  数据

  对象

  是的

  { 用户名:'zhangsan',年龄:20 }

  传递给 Native APP 的数据对象

  示例代码:

  JSBridge.putData('userInfo', {

username: 'zhangsan',

age: 20

});

  3.gotoWebview(url, page, data) 原生APP打开一个新的Webview窗口并打开对应的网页参数类型必填示例值说明

  网址

  字符串

  是的

  网页链接地址,一般只要传URL参数就可以了

  页面

  字符串

  没有

  网络

  网页类型,默认为web

  数据

  对象

  没有

  额外的参数对象

  示例代码:

  // 示例1:打开一个网页

JSBridge.gotoWebview('http://www.youzan.com');

// 示例2:打开一个网页,并且传递额外的参数给Native APP

JSBridge.gotoWebview('http://www.youzan.com', 'goodsDetail', {

goods_id: 10000,

title: '这是商品的标题',

desc: '这是商品的描述'

});

  4.gotoNative(page, data) 从H5页面跳转到Native APP类型的native接口参数必填示例值说明

  页面

  字符串

  是的

  登录页面

  本机页面标识符,例如 loginPage

  数据

  对象

  没有

  { 用户名:'zhangsan',年龄:20 }

  额外的参数对象

  示例代码:

  // 示例1:打开Native APP登录页面

JSBridge.gotoNative('loginPage');

// 示例2:打开Native APP登录页面,并且传递用户名给Native APP

JSBridge.gotoNative('loginPage', {

username: '张三'

});

  5.doAction(action, data) 函数上的一些动作参数 Type 是否必填 示例值 说明

  动作

  字符串

  是的

  复制

  核心方法:PHP如何使用curl实现数据抓取

  PHP如何使用curl实现数据抓取介绍如何使用curl实现数据抓取?以下是为您提供的实现代码。您可以参考代码以获取更多详细信息。代理服务器代理服务器地址httpwwwcnproxycomproxy1htmlHongKongChina的速度比较好 curl_setoptchCURLOPT_PROXYcu

  

  rl_setoptchCURLOPT_URLurlcurl_setoptchCURLOPT_RETURNTRANSFER1returndontprintcurl_setoptchCURLOPT_TIMEOUT30设置超时时间curl_setoptchCURLOPT_USERAGENTMozilla40compatibleMSIE501WindowsNT50curl_setoptchCURLOPT_FOLLOWLOCATION1302redirectcurl_setoptchCURLOPT_MAXREDIRS7HTTp定向级别curl_multi_add_handlemhch把curlresource放进multicurlhandler里handle[i]ch执行domrccurl_multi_execmhrunningifwait_usec0每个connect要间隔多久usleepwa

  

  it_usec250000025secwhilemrcCURLM_CALL_MULTI_PERFORMwhilerunningmrcCURLM_OKifcurl_multi_selectmh-1domrccurl_multi_execmhrunningwhilemrcCURLM_CALL_MULTI_PERFORM读取资料foreachhandleasichcontentcurl_multi_getcontentchdata[i]curl_errnoch0contentfalse移除handleforeachhandleaschcurl_multi_remove_handlemhchcurl_multi_closemhreturndataurlsarrayhttpmapbaiducomreasync_get_urlurlsechore[0]

0 个评论

要回复文章请先登录注册


官方客服QQ群

微信人工客服

QQ人工客服


线