JSBridge框架解决通讯问题实现移动端跨平台开发

【宜信技术沙龙002期】数据中台:宜信敏捷数据中台建设实践|宜信技术沙龙 将于5月23日晚8点线上直播,点击报名javascript

1、跨平台开发是趋势

目前主流的移动端平台主要是Android和iOS,为了尽量复用代码和节省开发成本,各大巨头都开发了本身的跨平台框架,好比Facebook的React-Native、阿里的Weex、Cordova,以及今年Google开发者大会上介绍的Flutter框架。这些框架各有优缺点,可是到目前为止都没有大规模地推广开来,在我看来主要有如下几个缘由:html

一、开发者生态圈还不够成熟vue

RN是三大跨平台框架中关注人最多、生态最活跃的框架,可是到目前为止也没有到1.0版本(最新的release是0.57.8),更别说做为后来者的Weex和Flutter了。生态不成熟,意味着开发文档少,可使用的开源控件少,好比在RN上想作一个最基本的下拉刷新和上拉加载更多的listview都比较费劲。Weex已经贡献给Apache,好久没有更新release了。Flutter如今还在beta版本,其发展还有待观察。java

二、性能问题android

虽然这几大框架都对渲染性能作了优化,可是相比原生仍是差一些,RN和weex都本身实现了一个浏览器内核(JSCore),所以多了一层js解析,渲染较慢。好比RN的listview,若是数据量太大就会出现卡顿。Flutter虽然自带绘制引擎,可是跟原生比起来仍是有一些距离。git

三、兼容问题github

虽然这三大平台的初衷都是为了跨平台(Write/Learn once, run everywhere),但在实际应用中仍是须要耗费不少的精力去兼容和适配,好比RN在Android低端机器上表现就不尽如人意,连曾经RN的坚实拥护者Airbnb都宣布放弃使用RN了。web

四、开发集成成本shell

三个框架都须要学习新的语言React/vue/dart,weex的最大优点就是入门简单,可是版本迭代慢,RN上手门槛高,开发调试难度大,集成RN和weex框架还会加入不少so文件,增长安装包的大小(至少在10M左右),这还不包括第三方的library。Flutter由于刚出来,应用的人还很少,其效果还有待观察。浏览器

总结:虽然上述自主研发的跨平台框架都或多或少地存在问题,可是移动开发的跨平台是大势所趋,能够节省开发成本,提升开发效率,迅速响应业务变化,如今主流的应用仍是使用H5和原生的通讯来实现跨平台的开发。Android和iOS平台都有本身内置的浏览器内核webkit框架,跨平台的本质就是用H5/JS编写的代码可以分别运行在Android和IOS的WebView中,从而实现一套代码两个平台都能运行的目的。

2、安卓跨平台开发实践

在Android平台上要实现Native和JS的通讯主要经过WebViewClient和WebChromeClient两个类来实现。

  • WebViewClient的做用是帮助WebView处理各类通知、事件请求,其主要的方法有:onLoadResource、onPageStart、onPageFinished、onReceiveError、shouldOverrideUrlLoading等;

  • WebChromeClient处理JS页面的事件响应,好比网页中的对话框、网页图标、网站标题、网页的加载进度等事件,对应的响应方法有onJsAlert、onJsConfirm、onJsConsole、onProgressChanged、onReceiveIcon、onReceiveTitle等。

要实现Java和JS通讯就要:

  • 解决Java调JS;

  • JS调Java。

Java调用JS经过loadUrl和evaluateJavaScript两个方法。

经过webview.loadUrl(“javascript:alert(‘hello world’)”),能够在android平台将js代码注入到html页面,loadUrl方法能够直接调用js中定义的函数,也能够把android本地的assets目录下的js文件以字符串的形式注入到html页面中,可是这个注入时机必定要等到html页面加载完毕才能作,即在WebViewClient.onPageFinished的回调函数中调用,这样就至关于在html页面中直接引用了js资源文件。对于客户端来讲,java调用js本质上是一个拼接js字符串的过程,可是调用loadUrl不能直接获取js函数的返回值,而要实现Java调用js函数后。

获取js函数的返回值可使用webview.evaluateJavaScript方法,可是该方法只有在android4.4及以上的版本才可使用。其余用法和loadUrl一致。

JS调用Java能够分为三种:对象映射、URL拦截、JS方法拦截。

对象映射是经过webview.addJavascriptInterface(new JSObject(), “javaObject”),这样能够js代码中能够直接调用javaObject对象,从而实现JS调用Java的功能,可是这个方法在android4.2如下会有安全漏洞,利用反射机制调用Android API getRuntime执行shell命令进行攻击,好比遍历sdcard、发送短信、安装木马APK等。

URL拦截是指在html页面经过iframe.src、window.open、documention.location或者href,这四种方法均可以在html页面中打开一个链接,从而会触发Java中的WebViewClient.shouldOverrideUrlLoading方法。例如在js中执行

在shouldOverrrideUrlLoading中能够根据约定的协议格式(Scheme)和协议名(Authority)获取从JS中传输过来的数据(Data)。

在JS中调用alert、console、prompt、confirm等方法就会触发WebChromeClient的onJsAlert、onConsoleMessage、onJsPrompt、onJsConfirm方法的回调。好比在js中能够调用

在onJsPrompt的message中能够获取prompt的内容,而后根据约定的协议格式能够获取数据。

3、JSBridge框架

为了解决JS和Native的通讯问题,须要使用一个JSBridge框架(https://github.com/lzyzsd/JsBridge)用来负责H5和Java之间的通讯,此时须要解决如下两个问题:

1)JS互相Java调用后如何回调,将responseData传递回去;

2)JS调用Java有三种方法,若是选择哪种方法比较合适。

针对问题1,能够在java端和js端定义一个数据结构: Message={callbackId:xxx, handleName:xxx,responseData:xxx,responseId:xxx}。将回调函数保存在callbackId中,当JS或者Java处理完数据回调的时候再将保存在callbackId的回调函数存放在responseId,相应的回调的数据存放在responseData中,这样就能响应JS或者Java调用后的回调消息。

Js调用Java的方法虽然有三种,可是addJavaScriptInterface存在安全性问题通常不建议使用,JS中的alert、console方法都会在Html页面比较经常使用,confirm和prompt虽然不经常使用可是某些手机系统版本上会有对话框弹出,不通用,因此比较好的选择是url拦截,能够经过iframe.src触发shouldOverrideUrlLoading。

JsBridge框架的使用主要分为:

  • 在H5页面加载完毕注入一个本地的js文件;

  • Java代码中注册BridgeHandler,用来处理JS发送过来的消息;

  • 在本地注入的js文件中定义_handleMessageFromNative,用来接收java传递过来的消息;

  • 由于客户端注入js是异步的,因此须要在js文件中注册Event监听器,成功后通知H5。

Native调用JS,例如经过

webview.loadUrl(
"javascript:WebViewJavascriptBridge._handleMessageFromNative('{
\"callbackId\":\"JAVA_CB_2_559\",\"data\":\"just data from java\"
}')");

这样就能够调用JS的handleMessageFromNative方法,传递的数据格式是Message,callBackId响应js的回调,发送前会存储到HashMap中,js回调的时候根据JAVA_CB_2_559找到对应的的回调函数处理js的响应数据,具体流程以下:

Js调用Java,经过sendMessageQueue将传递的信息转换成

Message= {data: {…}, callbackId: "cb_1_1234"}

其中callbackId是js的回调函数。而后经过

iframe.src=’yy://return/_fetchQueue/[{"data":"xxxx","callbackId":"cb_1_4321"}]’,

触发shouldOverrideUrlLoading方法,java处理完js传递过来的data后,将回调函数cb_1_4321设置到

Message={responseId: cb_1_4321, responseData:XXX},

这样在js中就能处理回调函数。具体的流程图以下:

JsBridge框架提供两种Handler方法,registerHandler方法须要传入handler的名字,这样须要iOS、Android、H5三方约定这个名字,由于H5开发的的业务需求变化比较快,并且H5调用原生的方法也是随机的,因此每次都用registerHandler约定名字须要使用全局变量存放这些handler方法名不便于扩展,因此实际开发中使用defaultHanlder,H5调用原生方法的的时候约定一个cmd,即约定data={cmd:xxx,time:data:{…}},只要在原生代码中对cmd命令有对应的功能,那么H5页面就能够随时调用原生的方法。

JSBridge的改进建议,因为webview调用js方法的时候必须在主线程才能生效,因此偶然会出现java调用js失败。另外,Js调用Java偶尔也会失败,由于iframe机制不能保证每次都能触发shouldOverrideUrlLoading回调。

目前JSBridge采用的是url scheme的方式,若是不考虑Android4.2如下,iOS7如下,能够采用的交互,好比直接使用addJavaScriptInterface在JS页面注入一个Native对象,将以前触发u步骤变为使用这个Native对象向Native发送消息。这种方法只是一个可行的方案,实际使用过程当中目前的JSBridge方案基本上知足业务需求了。

做者:周智

来源:宜信技术学院

相关文章
相关标签/搜索