JSBridge(Android和IOS平台)的设计和实现

前言

对于商务类的app,随着app注册使用人数递增,app的运营者们就会逐渐考虑在应用中开展一些推广活动。大多数活动具备时效性强、运营时间短的特征,一般产品们和运营者们都是通过wap页面快速投放到产品的活动模块。Wap页面可以声文并茂地介绍活动,但活动的最终目标是通过获取特权、跳转进入本地功能模块,最后达成交易。如何建立wap页面和本地Native页面的深度交互,这就需要用到本文介绍的JSBridge。

此外一些平台类的产品,如大家每天都在使用的微信、支付宝、手机qq等,无一例外都在使用集成JSBridge的webContainer完成众多业务组件功能,大大减少了客户端Native开发的工作量,不仅节约了大量人力开发成本,还能避开产品上线更新的版本审核周期限制(特别是IOS平台)。当然这些超级APP有强大的技术力量支撑,通过JSBridge有计划的进行API规范接口,不断向前端Wap开发人员开放,并在版本上向下兼容。但对于我们刚起步运营的中小级app来说暂时还没有必要如此大张旗鼓,相反前面提到的wap活动推广则是我们的主要需求。

为了满足这个需求,本文通过提炼JSBridge的核心部分改造成JSService方式供各个不同的产品零修改方式使用。各个不同的产品只需要按照插件的方式提供Native扩展接口,并在各自封装的webContainer中调用JSService对Wap调用进行拦截处理。

具体产品应用

目前该框架同时覆盖了Android和IOS平台,在我司的几个电商类产品中都得到了很好的使用,并趋于稳定。

本文的Demo工程运行效果如下:

关于JSAPI的接口封装

JSAPI的封装包括核心JS和对外开放接口JS两个部分。 核心JS部分通过拦截某Q的wap请求页面获取,获取的JS进行编码混淆处理,已经通过调试进行了注释,其主要过程就是对参数和回调进行封装,并构建一个url链接通过创建一个隐藏的iframe进行发送。核心JS代码阅读

对参数和回调进行封装部分的代码如下:

//invoke

//mapp.invoke("device", "getDeviceInfo", e);

//@param e 类 必须

//@param n 类方法 必须

//@param i 同步回调的js方法

//@param s

function k(e, n, i, s) {

if (!e || !n) return null;

var o, u;

i = r.call(arguments, 2), //相当于调用Array.prototype.slice(arguments) == arguments.slice(2),获取argument数组2以后的元素

//令s等于回调函数

s = i.length && i[i.length - 1],

s && typeof s == "function" ? i.pop() : typeof s == "undefined" ? i.pop() : s = null,

//u为当前存储回调函数的index;

u = b(s);

//如果当前版本支持Bridge

if (C(e, n)) {

//将传进来的所有参数生成一个url字符串;

o = "ldjsbridge:" + "/" + "/" + encodeURIComponent(e) + "/" + encodeURIComponent(n),

i.forEach(function(e, t) {

typeof e == "object" && (e = JSON.stringify(e)),

t === 0 ? o += "?p=": o += "&p" + t + "=",

o += encodeURIComponent(String(e))

}),

(o += "#" + u); //带上存储回调的数组index;

//执行生成的url, 有些函数是同步执行完毕,直接调用回调函数;而有些函数的调用要通过异步调用执行,需要通过

//全局调用去完成;

var f = N(o);

if (t.iOS) {

f = f ? f.result: null;

if (!s) return f; //如果无回调函数,直接返回结果;

}

}else {

console.log("mappapi: the version don‘t support mapp." + e + "." + n);

}

}

创建iframe发送JSBridge调用请求:

//创建一个iframe,执行src,供拦截

function N(n, r) {

console.log("logOpenURL:>>" + n);

var i = document.createElement("iframe");

i.style.cssText = "display:none;width:0px;height:0px;";

var s = function() {

//通过全局执行函数执行回调函数;监听iframe是否加载完毕

E(r, {

r: -201,

result: "error"

})

};

//ios平台,令iframe的src为url,onload函数为全局回调函数

//并将iframe插入到body或者html的子节点中;

t.iOS && (i.onload = s, i.src = n);

var o = document.body || document.documentElement;

o.appendChild(i),

t.android && (i.onload = s, i.src = n);

//

var u = t.__RETURN_VALUE;

//当iframe执行完成之后,最后执行settimeout 0语句

return t.__RETURN_VALUE = e,

setTimeout(function() {

i.parentNode.removeChild(i)

},

0),

u

}

对外开放接口的封装:(使用者只需要对该部分进行接口扩展即可)

mapp.build("mapp.device.getDeviceInfo", {

iOS: function(e) {

return mapp.invoke("device", "getDeviceInfo", e);

},

android: function(e) {

var t = e;

e = function(e) {

try {

e = JSON.parse(e)

} catch(n) {}

t && t(e)

},

mapp.invoke("device", "getDeviceInfo", e)

},

support: {

iOS: "1.0",

android: "1.0"

}

}),

核心JS代码调用说明

 

mapp.version: mappAPI自身版本号

mapp.iOS: 如果在ios app中,值为true

mapp.android: 如果在android app中,值为true

mapp.support: 检查当前app环境是否支持该接口,支持返回true

mapp.support("mqq.device.getClientInfo")

mapp.callback: 用于生成回调名字,跟着invoke参数传给客户端,供客户端回调

var callbackName = mapp.callback(function(type, index){

console.log("type: " + type + ", index: " + index);

});

mapp.invoke 方法:

mapp核心方法,用于调用客户端接口。

@param {String} namespace 命名空间

@param {String} method 接口名字

@param {Object/String} params 可选,API调用的参数

@param {Function} callback 可选,API调用的回调

* 调用普通的无参数接口:

mapp.invoke("ns", "method");

* 调用有异步回调函数的接口:

mapp.invoke("ns", "method", function(data){

console.log(data);

});

mapp.invoke("ns", "method", {

"params" : params   //参数通过json封装

"callback" : mapp.callback(handler), //生成回调名字

});

* 如果有多个参数调用:

mapp.invoke("ns", "method", param1, param2 /*,...*/,callback);

JSService的具体实现-插件运行机制

JSService部分是基于Phonegap的Cordova引擎的基础上简化而来,其基本原理参照Cordova的引擎原理如图所示:

一般app中都有自己定制的Webcontainer,为了更好的跟已有项目相融合,在Cordova的基础上我们进行了简化,通过JSAPIService服务的方式进行插件扩展开发如图所示:

本JSBridge是基于Phonegap的Cordova引擎的基础上简化而来, Android平台Webview和JS的交互方式共有三种:

  1. ExposedJsApi:js直接调用java对象的方法;(同步)
  2. 重载chromeClient的prompt 截获方案;(异步)
  3. url截获+webview.loadUrl回调的方案;(异步)

为了和IOS保持一致的JSAPI,只能选用第三套方案;

基于JSService的插件开发、配置和使用

IOS平台

git地址:https://github.com/Lede-Inc/LDJSBridge_IOS.git

在Native部分,定义一个模块插件对应于创建一个插件类, 模块中的每个插件接口对应插件类中某个方法。

集成LDJSBridge_IOS框架之后,只需要继承框架中的插件基类LDJSPlugin,如下所示:

  • 插件接口定义

#import "LDJSPlugin.h"

@interface LDPDevice : LDJSPlugin

{}

//@func 获取设备信息

- (void)getDeviceInfo:(LDJSInvokedUrlCommand*)command;

@end

  • 自定义插件接口实现

@implementation LDPDevice

/**

*@func 获取设备信息

*/

- (void)getDeviceInfo:(LDJSInvokedUrlCommand*)command{

//读取设备信息

NSMutableDictionary* deviceProperties = [NSMutableDictionary dictionaryWithCapacity:4];

UIDevice* device = [UIDevice currentDevice];

[deviceProperties setObject:[device systemName] forKey:@"systemName"];

[deviceProperties setObject:[device systemVersion] forKey:@"systemVersion"];

[deviceProperties setObject:[device model] forKey:@"model"];

[deviceProperties setObject:[device modelVersion] forKey:@"modelVersion"];

[deviceProperties setObject:[self uniqueAppInstanceIdentifier] forKey:@"identifier"];

LDJSPluginResult* pluginResult = [LDJSPluginResult resultWithStatus:LDJSCommandStatus_OK messageAsDictionary:[NSDictionary dictionaryWithDictionary:deviceProperties]];

[self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];

}

@end

  • 在plugin.json文件中对plugin插件的统一配置

{

"update": "",

"module": "mapp",

"plugins": [

{

"pluginname": "device",

"pluginclass": "LDPDevice",

"exports": [

{

"showmethod": "getDeviceInfo",

"realmethod": "getDeviceInfo"

}

]

}

]

}

  • 在webContainer中对JSService初始化, 当初始化完成之后,向前端页面发送一个ReadyEvent,前端即可开始调用JSAPI接口;

//注册插件Service

if(_bridgeService == nil){

_bridgeService = [[LDJSService alloc] initBridgeServiceWithConfig:@"PluginConfig.json"];

}

[_bridgeService connect:_webview Controller:self];

/**

Called when the webview finishes loading.  This stops the activity view.

*/

- (void)webViewDidFinishLoad:(UIWebView*)theWebView{

NSLog(@"Finished load of: %@", theWebView.request.URL);

//当webview finish load之后,发event事件通知前端JSBridgeService已经就绪

//监听事件由各个产品自行决定

[_bridgeService readyWithEvent:@"LDJSBridgeServiceReady"];

}

Android平台

git地址:https://github.com/Lede-Inc/LDJSBridge_Android.git

  • 插件接口定义

public class LDPDevice extends LDJSPlugin {

public static final String TAG = "Device";

/**

* Constructor.

*/

public LDPDevice() {

}

}

  • LDJSPlugin 属性方法说明

/**

* Plugins must extend this class and override one of the execute methods.

*/

public class LDJSPlugin {

public String id;

//在插件初始化的时候,会初始化当前插件所属的webview和controller

//供插件方法接口 返回处理结果

public WebView webView;

public LDJSActivityInterface activityInterface;

//所有自定义插件需要重载此方法

public boolean execute(String action, LDJSParams args, LDJSCallbackContext callbackContext) throws JSONException {

return false;

}

}

  • 自定义插件接口实现

@Override

public boolean execute(String action, LDJSParams args, LDJSCallbackContext callbackContext) throws JSONException {

if (action.equals("getDeviceInfo")) {

JSONObject r = new JSONObject();

r.put("uuid", LDPDevice.uuid);

r.put("version", this.getOSVersion());

r.put("platform", this.getPlatform());

r.put("model", this.getModel());

callbackContext.success(r);

}

else {

return false;

}

return true;

}

  • 在封装的webContainer中注册服务并调用:

 

 

时间: 2024-08-09 10:43:33

JSBridge(Android和IOS平台)的设计和实现的相关文章

学习笔记:APP切图那点事儿–详细介绍android和ios平台

学习笔记:APP切图那点事儿–详细介绍android和ios平台 转载自:http://www.woofeng.cn/articles/168.html   版权归原作者所有 作者:亚茹有李 原文地址:http://blog.boocss.com/app-android-ios/ 一.android版 在做android版本设计的时候,尺寸有很多种,这时我们要以一种尺寸为基准,那这个基准尺寸是480px*800px,设计图完成之后就开始切图了 需要注意的: A:android主要有3种屏,即:

cocos2d-x打开网页android与ios平台

cocos2d-x打开一个网页,特别简单 转载请注明地址白白:http://blog.csdn.net/u010229677 iOS中 <span style="font-size:18px;">void FileOperation::linkToFull() { [[UIApplication sharedApplication]openURL:[NSURL RLWithString:@"http://www.baidu.com"]]; }</s

基于Android、iOS平台的移动端车牌识别技术及实现车牌识别过程

近年来,随着移动行业的爆发式发展,手机配置不断提高,基于手机平台的信息采集.图像处理.数据传输等方面的研究也成为了热点,这使得基于手机平台上的车牌识别成为可能.传统的车牌识别系统一般都基于固定的桌面平台.图像采集不灵活,特别是对于交通管理部门来说,对违章车辆车牌的自动登记非常不便,因此基于Android.iOS平台的移动端车牌识别技术出现了. 那么如何实现车牌识别的呢,下面简单说说: 首先对现存的车牌识别算法进行了研究,在诸多算法中寻找到一种适合在Android.iOS平台上运行的算法.先通过智

一种支持本地离线扫描识别的移动端车牌识别技术,基于Android、iOS平台

随着社会的发展,城市中的汽车越来越多.城市由于汽车的增加造成的拥挤给人们的生活带来了极大的不便,这种不便迫使人们去寻找高技术有效手段去解决这种不便.很多的大型停车场收费系统管理存在着排队时间长.管理成本高.劳动强度大等各种弊端,顺应时代发展的一些占路停车场和小型露天停车场也应运而生,然而这些停车场收费透明度低.资金流失和车辆失窃也给车主和管理者造成了较大的困扰,因此需要一些较为快捷有效的管理系统去解决这些问题.此时,一种基于移动端车牌识别的停车收费方法应运而生,车辆通过停车场出入口时,停车场端系

经典好文:android和iOS平台的崩溃捕获和收集

通过崩溃捕获和收集,可以收集到已发布应用(游戏)的异常,以便开发人员发现和修改bug,对于提高软件质量有着极大的帮助.本文介绍了iOS和android平台下崩溃捕获和收集的原理及步骤,不过如果是个人开发应用或者没有特殊限制的话,就不用往下看了,直接把友盟sdk(一个统计分析sdk)加入到工程中就万事大吉了,其中的错误日志功能完全能够满足需求,而且不需要额外准备接收服务器. 但是如果你对其原理更感兴趣,或者像我一样必须要兼容公司现有的bug收集系统,那么下面的东西就值得一看了. 要实现崩溃捕获和收

JS对于Android和IOS平台的点击响应的适配

综述 最近做项目的时候发现了一个非常奇怪的问题,就是对于click事件的响应.经过测试发现,对于IOS平台,直接监听click事件可能是没有响应的,而在Android和PC上则完全没有问题.所以通过获取设备信息实现了不同平台的不同监听. IOS监听 对于IOS设备,只监听click方法可能是没有响应的.解决方法就是监听 “touchend click”事件. 而对于Android和PC,则只监听click事件即可. 平台检测 我们利用userAgent来检测平台 1 2 3 4 5 6 7 8

教你pomeloclient包libpomelo增加cocos2d-x 3.0工程(Windows、Android、IOS平台)

Windows平台 操作系统:Windows7(64-bit) VS版本号:2013 Cocos2d-x版本号:3.0 project路径:E:\cocos2d-prj\ 1.从github下载libpomelo代码 E:\cocos2d-prj\cocos2d\external> git clone https://github.com/NetEase/libpomelo.git 2.创建libpomelo的VSproject E:\cocos2d-prj\cocos2d\external>

android和iOS平台的崩溃捕获和收集

转自:http://www.cnblogs.com/lancidie/archive/2013/04/13/3019349.html 通过崩溃捕获和收集,可以收集到已发布应用(游戏)的异常,以便开发人员发现和修改bug,对于提高软件质量有着极大的帮助.本文介绍了iOS和android平台下崩溃捕获和收集的原理及步骤,不过如果是个人开发应用或者没有特殊限制的话,就不用往下看了,直接把友盟sdk(一个统计分析sdk)加入到工程中就万事大吉了,其中的错误日志功能完全能够满足需求,而且不需要额外准备接收

一种基于Android、iOS平台的移动端名片识别SDK,商务得力助手

移动端名片识别应用背景: 这些年,随着移动互联的发展,APP应用成爆发式的增长,在很多APP中都涉及到对名片信息的录入,如移动CRM.移动端OA,移动访客系统等:如果手动输入名片信息,速度慢,易出错,用户体验非常差.为了提高在移动终端上输入名片信息的速度和准确性,移动端名片识别技术出现了,以满足各个行业对名片信息自动录入的需求,只需在APP中集成移动端名片识别,用户便可通过手机拍照,自动录入识别名片信息.名片识别是指名片经过手机拍照识别,导入手机通讯录后,利用软件SyncML标准同步至云端,便可