cordova源码解析

cordova是很多公司用来做hybrid方案的框架,当然会根据自己的业务需求加入一些自己的改动,现在公司也要用,于是把cordova安卓端的代码看了一遍。

架构图:


图片.png

在看之前提出几个问题:

  • 启动流程,初始化流程
  • 配置的加载流程,plugin的启动、初始化流程
  • 如何通讯,js到native,native到js

看总体的类图:

图片.png

有一些线没画出来,比如SystemWebViewEngin和SystemWebview都是含有一个parent(对前一个对象的)引用的。比如SystemWebViewEngin的parent是CordovaWebView,SystemWebview的parent是SystemWebViewEngin。SystemWebView和SystemWebViewEngin以及CordovaWebViewImpl都含有一个CordovaInterface的引用,以及pluginManager(SystemWebView没有plguinManager的引用)。

主要的类介绍:

  • CordovaInterface:主要是plugin以及webview中操作UI的接口,可以直接通过
    CordovaInterface获取到Activity,比如权限请求,startActivity等等。在plugin中也有这个对象的引用,所以可以在对于的plugin中调用UI相关的东西。
  • PluginManager: 主要是统一管理和调用对应的插件,他对外提供了一个exec方法。这个方法可以统一调用对应的plugin。还可以通过pluginManager控制插件的生命周期,比如onPause,onStop等等。
  • CordovaWebViewImpl: 实现了CordovaWebView的接口, 主要是对外提供这个组件的接口,内部集成和管理其他的部分,比如webviewEngin等等。他对外提供了比如loadUrl等等接口。
  • SystemWebViewEngine: 这个类是用来解耦真实Webview和他的调用方,这样有两个好处。可以对外提供更加灵活的API,分离真实的WebView这样可以缩小对外开放的api。
  • SystemWebView:这个类是真正的Webview,继承自WebView。这里做一些webview的初始化工作,比如setWebViewClient,setWebChromeClient等等。
  • NativeToJsMessageQueue: 这个类用来管理发送给webview的消息的。主要用来向webview发送消息。他有几种模式,比如通过loadUrl来执行js的,通过evaluateJavascript来执行url的等等。这些模式可以动态设置。

js如何与native通讯

先来复习一下js与native通讯的方式:

对于Android调用JS代码的方法有2种:

  1. 通过WebView的loadUrl()
  2. 通过WebView的evaluateJavascript()

对于JS调用Android代码的方法有3种:

  1. 通过WebView的addJavascriptInterface()进行对象映射 。这样js就可以直接通过映射的对象调用native的代码了。(但是这个方法在一些安卓版本里有任意执行漏洞)
  2. 通过 WebViewClient 的shouldOverrideUrlLoading ()方法回调拦截 url 。(主要是拦截对应的url做一些操作,比如拦截到一些特殊的url或者url带了一些特殊的参数,从而调用native的代码,也就是相当于js调用了native的代码。)
  3. 通过 WebChromeClient 的onJsAlert()、onJsConfirm()、onJsPrompt()方法回调拦截JS对话框alert()、confirm()、prompt() 消息。也可以实现js调用native的代码和逻辑。

native如何调用js。

native调用js的时候,native的调用会被封装成一个message,通过NativeToJsMessageQueue来发送和调用。比如有一个方法addJavaScript可以执行js代码。但是addJavaScript这个方法已经不推荐使用,推荐使用addPluginResult这个方法。这个方法封装了需要传递一个PluginResult,和一个callbackId,作为发送到js那边的消息。因为大多数时候native需要调用js都是作为js调用native的callback来调用从而返回一些native这边的结果的。

在通过addPluginResult这个方法往将需要发送的消息封装成一个JsMessage然后调用enqueueMessage这个方法,这个方法底部会通过onNativeToJsMessageAvailable方法通知有消息来了,然后不同的BridgeMode会做出不同的反应,比如这里有LoadUrlBridgeMode会通过webview的loadUrl方法运行js代码,EvalBridgeMode这个模式会通过webview的evaluateJavascript方法运行js代码等等。这样就完成了native对js的调用。

这里对js的调用做了一个封装,让调用更加灵活和可扩展。

js如何调用native。

js调用native主要使用的是addJavascriptInterface的对象映射。但是对于有些安卓版本这个方式会有任意执行漏洞,所以会有一个bridgeSecret用来验证安全性。

js会先调用prompt来初始化bridgeSecret,在native端生成bridgeSecret并且传递到js端,然后每次js调用JavascriptInterface的时候都会带上这个secret来验证安全性。

看addJavascriptInterface这种方式注册的给js调用的接口。

    @SuppressLint("AddJavascriptInterface")
    private static void exposeJsInterface(WebView webView, CordovaBridge bridge) {
        SystemExposedJsApi exposedJsApi = new SystemExposedJsApi(bridge);
        webView.addJavascriptInterface(exposedJsApi, "_cordovaNative");
    }

看SystemExposedJsApi类提供给js的方法:

    @JavascriptInterface
    public String exec(int bridgeSecret, String service, String action, String callbackId, String arguments) throws JSONException, IllegalAccessException {
        return bridge.jsExec(bridgeSecret, service, action, callbackId, arguments);
    }

    @JavascriptInterface
    public void setNativeToJsBridgeMode(int bridgeSecret, int value) throws IllegalAccessException {
        bridge.jsSetNativeToJsBridgeMode(bridgeSecret, value);
    }

    @JavascriptInterface
    public String retrieveJsMessages(int bridgeSecret, boolean fromOnlineEvent) throws IllegalAccessException {
        return bridge.jsRetrieveJsMessages(bridgeSecret, fromOnlineEvent);
    }

再看prompt方法的调用链。

在SystemWebChromeClient里面,会先调用bridge看是否要处理,如果bridge处理了这个prompt,则返回,否则调用dialogHelper处理。

在bridge里会有一系列处理js命令的逻辑
CordovaBridge.java

    public String promptOnJsPrompt(String origin, String message, String defaultValue) {
        if (defaultValue != null && defaultValue.length() > 3 && defaultValue.startsWith("gap:")) {
            
//...
            return "";
        }
        // Sets the native->JS bridge mode.
        else if (defaultValue != null && defaultValue.startsWith("gap_bridge_mode:")) {
            //....
            return "";
        }
        // Polling for JavaScript messages
        else if (defaultValue != null && defaultValue.startsWith("gap_poll:")) {
            //...
            return "";
        }
        else if (defaultValue != null && defaultValue.startsWith("gap_init:")) {
            // Protect against random iframes being able to talk through the bridge.
            // Trust only pages which the app would have been allowed to navigate to anyway.
            if (pluginManager.shouldAllowBridgeAccess(origin)) {
                // Enable the bridge
                int bridgeMode = Integer.parseInt(defaultValue.substring(9));
                jsMessageQueue.setBridgeMode(bridgeMode);
                // Tell JS the bridge secret.
                int secret = generateBridgeSecret();
                return ""+secret;
            } else {
                LOG.e(LOG_TAG, "gap_init called from restricted origin: " + origin);
            }
            return "";
        }
        return null;
    }

这里会有几个命令:

  • gap:开头:则调用js那边传过来的命令,并且执行对应的plugin。
  • gap_bridge_mode:开头,设置JS bridge mode。
  • gap_poll:开头,获取所有需要发送给js的消息。
  • gap_init:开头,初始化bridgeSecret。

加载plugin的时候会初始化plugin吗?

有一个onload的标记位,true的时候在初始化plguinManager的时候就会初始化,否则用到的时候才会初始化。

plugin是如何加载和初始化的?

在CordovaActivity的onCreate里初始化的,解析xml获取到preference设置和pluginEntry的列表。

cordova 常用命令

安装cordova
npm install -g cordova

创建工程:
cordova create myApp com.myCompany.myApp myApp

//进入工程目录
cd myApp

//在工程中可以运行的。
添加平台
cordova platform add android --save
添加插件
cordova plugin add cordova-plugin-camera --save
查看插件列表
cordova plugin list
移除插件
cordova plguin remove xxx
编译安卓
cordova build android --verbose
运行安卓
cordova run android

参考文档:

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,290评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,399评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,021评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,034评论 0 207
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,412评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,651评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,902评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,605评论 0 199
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,339评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,586评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,076评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,400评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,060评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,083评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,851评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,685评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,595评论 2 270

推荐阅读更多精彩内容