Cordova插件实现原理概论

这段时间一直在和Cordova打交道,而且在打交道的过程中,一个疑惑越来越重,那就是Cordova的实现机理到底是怎么样的呢,于是就想探究一二,虽然现在仍然无法从细节上去把握原理,但是已经可以从大致实现流程方面做一个知识的梳理了。至于为什么无法把握细节,一方面是因为公司项目时间紧,没有太多时间研究细节,另一方面则是本人JS基础较差,待以后时间充足还是可以根据大致实现流程然后再从代码去逐一细细研究的。

我在使用Cordova框架过程中主要由三个疑惑,而本文也是主要从原理上来解决这三个疑惑,先把三个以后列出来吧(以MathPlugin插件为例来说明)。

疑惑1:cordvoa.plugin.math.plus如何就调用到了MathPlugin.js插件中plus方法;

疑惑2:MathPlugin.js中的plus如何就调用到了MathPlugin.java中的plus方法;

疑惑3:MathPlugin.java中的CallbackContext对象又是如何调用到了JS代码;

解决这三个疑惑的过程就是研究Cordova实现原理的过程,而在研究Cordova实现原理之前,先讲讲不用第三方框架怎么实现JS和Native的交互吧,因为实际上Cordova也是通过Android自带的交互实现来进行扩展的。

1、不用第三方框架实现JS和Native交互

据我所知,Native调用JS方式有

方式1: webView.evaluateJavascript(js, callback);

方式2: webView.loadUrl("javascript:" + js);

而这两种方式的实现,实际上都可以在Cordova框架的SystemWebView.java文件中找到。

而JS调用Native的方式有

方式1:通过addJavascriptInterface方法结合@JavascriptInterface注释来实现;

方式2:通过WebChromeClient对象,中的三个方法(onJsAlert,onJsConfirm,onJsPrompt)来实现;

WebChromeClient中的三个方法如下图所示

这里写图片描述

当JS调用window对象的对应的方法,即window.alert,window.confirm,window.prompt,WebChromeClient对象中的三个方法对应的就会被触发, 于是可以利用这个机制实现JS调用Native方法。

2、插件模块初始化,并由clobber映射到对应的plugin.js

这个小节题目可能说的有点模糊,以MathPlugin的实现为例来解释下吧,,其plugin.xml有一段配置为

<js-module name="mymath" src="www/MathPlugin.js">
    <clobbers target="cordova.plugins.math"/>
</js-module>

同时我们定义了一个MathPlugin.js文件来调用原生代码

var exec = require('cordova/exec');
var myMathFunc = function(){};  

myMathFunc.prototype.plus = function(success, error, arg0) {
    exec(success, error, "MathPlugin", "plus", arg0);
};

myMathFunc.prototype.minus = function(success, error, arg0) {
    // yjing's test
    exec(success, error, "MathPlugin", "minus", arg0);
};

var MYMATHFUNC = new myMathFunc();
module.exports = MYMATHFUNC;

并且当时说在js中直接通过cordova.plugins.math.plus便可调用到MathPlugin.js文件中plus扩展方法,那么这中间便存在一个clobber和plugin.js之间的映射过程,而这个过程便是本小节要解决的问题,先看下面时序图吧。
这里写图片描述

这个时序图中主要看中间的cordova.js在App启动时,首先从cordova_plugin.js中加载插件,并存放在本地变量中,接着在channel.join方法中创建cordova相关所有对象模块,其中也包括了插件所对相应的对象模块(这里指MathPlugin.js),接着再通过后面几个方法来处理映射关系,经过一系列的操作后,会将cordova_plugin.js文件中的clobber:cordova.plugin.math和MathPlugin.js中的myMathFunc行程一一对应关系,之后就可以通过cordova.plugin.math.plus调用myMathFunc模块中的plus方法了。

到这里就解决额疑惑1。

3、插件JS代码如何调用到了Native代码

第一小节提到了Android实际上是提供了两种JS代码调用Native代码的方式的,而Corodva插件实现JS代码调用Native代码的方法正是通过这两种方式实现的,不过在用着两种方式之前经历了一系列的其它操作,废话不说,同样看时序图。

这里写图片描述

从图中可以看出当调用cordova.plugin.math.plus方法时,会调用到exec.js模块的androidExec方法,并在该方法中会生成一个callbackId参数,该参数格式是由plugin名称+变化的数字(每次加一)组成,比如callbackId = MathPlugin123487,这个参数是唯一的,在js调用native时会传到naive代码,当native代码返回时,又会把这个参数返回js,这样js就能够判断返回参数对应的插件模块了,再根据success和error方法便能够输出native端返回js端的数据。

之后会调用nativeApiProvidet.get()方法,而返回的nativeApi实际上是

var nativeApi = this._cordovaNative || require('cordova/android/promptbasednativeapi');

这里有个this._cordovaNative表示webView是否有设置

webView.addJavascriptInterface(exposedJsApi, "_cordovaNative");

如果设置了,则执行nativeApiProvider.get().exec方法后就会执行exposedJsApi.java里的相应方法; 如果没有设置,那么就调用 platform_www/cordova-js-src/android/promptbasednativeapi.js里exec方法,该方法是执行 js的prompt方法(这个是一个常用的jsbridge里通信方法),之后就会执行WebChromClient的onJsPrompt方法。

到这里解决掉疑惑2。

3、CallBackContext对象调用JS代码

CallBackContext调用JS代码比较简单,直接跟着代码调试就可以发现调用流程,先放图
这里写图片描述

这里的流程图就不讲了,感兴趣的同学跟着调试就知道咋回事了。这里看看CordovaWebViewImpl中的sendPluginResult代码

@Override
public void sendPluginResult(PluginResult cr, String callbackId) {
    nativeToJsMessageQueue.addPluginResult(cr, callbackId);
}

贴出这个代码是为了强调下这里的callbackId,然后会将PluginResult数据存放在nativeToJsMessageQueue队列中。

所有的Native到JS的通信在Cordova中都是通过BrideMode来完成的,而BridgeMode在NativeToJsMessageQueue类中又三种实现方式

/** Uses webView.loadUrl("javascript:") to execute messages. */
方式1: public static class LoadUrlBridgeMode extends BridgeMode

/** Uses online/offline events to tell the JS when to poll for messages. */
方式2:public static class OnlineEventsBridgeMode extends BridgeMode

/** Uses webView.evaluateJavascript to execute messages. */
方式3:public static class EvalBridgeMode extends BridgeMode

其中LoadUrlBridgeMode对应

engine.loadUrl("javascript:" + js, false);

这种方式来跟js交互。

EvalBridgeMode 对应

engine.evaluateJavascript(js, null);

这种方式同js交互。

而OnlineEventsBridgeMode 这种模式对应

delegate.setNetworkAvailable(true);

这就尴尬了,不知道对应啥,不过有兴趣的同学可以调试进去看看,我相信最后还是会成为上面那两种方式和js进行交互。

到这算是解决了疑问3。

文章写的不太好,有错误的或者疑问的望各位同学更够指正。

5、参考文献

本文内容基本都是结合参考文献,自己看源码总结而来,如本文描述不够详细的地方,也可以参考一下文献学些。

1、Cordova原理一:
http://www.cnblogs.com/StephenWu/p/6580362.html

2、Cordova原理二:
http://www.cnblogs.com/StephenWu/p/6580632.html

3、Android JSBridge的原理与实现 (prompt和javascript)
http://blog.csdn.net/u012971339/article/details/50770854
相关文章
相关标签/搜索