淺談Cordova框架的一些理解

前言

因?yàn)楣ぷ髟蚰模罱枰芯緾ordova框架潘拱,看了其中的源碼和實(shí)現(xiàn)方式,當(dāng)場(chǎng)在看的時(shí)候馬上能理解拧略,但是事后再回去看相關(guān)源碼時(shí)候卻發(fā)現(xiàn)之前理解的內(nèi)容又忘記了芦岂,又不得不重新開始看,所以總覺得需要記錄下來(lái)垫蛆,這樣也表明之前也是學(xué)習(xí)過(guò)禽最,俗話說(shuō)「好記性不如爛筆頭 」,想必也是體現(xiàn)了筆記的重要性月褥。

目錄

  • 為何要用Cordova

  • 什么是Cordova

  • Cordova中UML類圖

  • Cordova實(shí)現(xiàn)機(jī)制

  • 小結(jié)

    ?

為何要用Cordova

隨著移動(dòng)互聯(lián)網(wǎng)的發(fā)展弛随,現(xiàn)在基本是APP滿天飛,不知在大家印象中宁赤,如果我去下載一個(gè)APP,那么基本都能看到有兩種選擇栓票,一種是Android版本决左,一種是IOS版本愕够。不管我的手機(jī)是哪種操作系統(tǒng),安裝完一個(gè)APP之后佛猛,后續(xù)如果有新的版本發(fā)布的時(shí)候惑芭,我還必須去更新,才能享用新版本里的功能继找,比如我裝了“京東”這個(gè)APP遂跟,前幾天正好碰到“618”活動(dòng),那么之前一個(gè)月APP Store就提醒我要去更新最新的APP版本婴渡,以免錯(cuò)過(guò)“618”活動(dòng)中新的功能使用幻锁。相對(duì)來(lái)說(shuō)IOS系統(tǒng)更新APP比起Android系統(tǒng)用戶體驗(yàn)會(huì)好一點(diǎn),但是還是稍顯麻煩點(diǎn)边臼。

那么有沒有一種方式哄尔,我只需要開發(fā)一個(gè)APP版本,就能去適配通用的操作系統(tǒng)呢柠并,不僅可以適配Android岭接、IOS,還可以適配其他系統(tǒng)臼予,比如Windows Phone鸣戴、 Palm WebOS、Blackberry等等粘拾。有窄锅,Cordova就能提供這種能力,代碼寫一次半哟,就能到處運(yùn)行酬滤,跟我們?nèi)粘i_發(fā)網(wǎng)站效果一樣,基于寫Web APP寓涨,根據(jù)輸出平臺(tái)要求不同盯串,就能提供不同類型的安裝包。Cordova其設(shè)計(jì)初衷是希望用戶群體能夠通過(guò)跨平臺(tái)開發(fā)的方法降低原生開發(fā)的成本戒良,為此体捏,開發(fā)人員需要安裝原生開發(fā)環(huán)境,配置工程糯崎,使用HTML5几缭、CSS3JS和原生SDK生成應(yīng)用沃呢。

什么是Cordova

官網(wǎng)定義如下:

Apache Cordova是一個(gè)開源的移動(dòng)開發(fā)框架年栓。允許你用標(biāo)準(zhǔn)的web技術(shù)-HTML5,CSS3和JavaScript做跨平臺(tái)開發(fā)。 應(yīng)用在每個(gè)平臺(tái)的具體執(zhí)行被封裝了起來(lái)薄霜,并依靠符合標(biāo)準(zhǔn)的API綁定去訪問(wèn)每個(gè)設(shè)備的功能某抓,比如說(shuō):傳感器纸兔、數(shù)據(jù)、網(wǎng)絡(luò)狀態(tài)等否副。

使用Apache Cordova的人群:

  • 移動(dòng)應(yīng)用開發(fā)者汉矿,想擴(kuò)展一個(gè)應(yīng)用的使用平臺(tái),而不通過(guò)每個(gè)平臺(tái)的語(yǔ)言和工具集重新實(shí)現(xiàn)备禀。

  • web開發(fā)者洲拇,想包裝部署自己的web App將其分發(fā)到各個(gè)應(yīng)用商店門戶。

  • 移動(dòng)應(yīng)用開發(fā)者曲尸,有興趣混合原生應(yīng)用組建和一個(gè)WebView(一個(gè)特別的瀏覽器窗口) 可以接觸設(shè)備A級(jí)PI赋续,或者你想開發(fā)一個(gè)原生和WebView組件之間的插件接口。

    ?

架構(gòu)圖

框架圖

從圖中队腐,我們可以看到它提供了Web APP蚕捉、WebView、Cordova Plugins柴淘。

Web APP

這是存放應(yīng)用程序代碼的地方迫淹,體現(xiàn)是你的具體業(yè)務(wù)邏輯模塊。應(yīng)用的實(shí)現(xiàn)是通過(guò)web頁(yè)面为严,默認(rèn)的本地文件名稱是是index.html敛熬,這個(gè)本地文件應(yīng)用CSS,JavaScript,圖片,媒體文件和其他運(yùn)行需要的資源第股。應(yīng)用執(zhí)行在原生應(yīng)用包裝的WebView中应民,這個(gè)原生應(yīng)用是你分發(fā)到app stores中的。

WebView

Cordova啟用的WebView可以給應(yīng)用提供完整用戶訪問(wèn)界面夕吻。在一些平臺(tái)中诲锹,他也可以作為一個(gè)組件給大的、混合應(yīng)用涉馅,這些應(yīng)用混合和Webview和原生的應(yīng)用組件归园。

Cordova Plugins

插件是Cordova生態(tài)系統(tǒng)的重要組成部分。他提供了Cordova和原生組件相互通信的接口并綁定到了標(biāo)準(zhǔn)的設(shè)備API上稚矿,這使你能夠通過(guò)JavaScript調(diào)用原生代碼庸诱。

Cordova中UML類圖

其實(shí)Cordova通過(guò)命令來(lái)添加項(xiàng)目的,但是可以選擇哪個(gè)平臺(tái)去編譯晤揣,比如我們添加Android平臺(tái)桥爽,在Android默認(rèn)mainActivity類,我們可以看到它其實(shí)繼承CordovaActivity類昧识,一切初始化條件是從loadUrl方法開始钠四。

package com.example.hello;

import android.os.Bundle;
import org.apache.cordova.*;

public class MainActivity extends CordovaActivity
{
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);

        // enable Cordova apps to be started in the background
        Bundle extras = getIntent().getExtras();
        if (extras != null && extras.getBoolean("cdvStartInBackground", false)) {
            moveTaskToBack(true);
        }

        // Set by <content src="index.html" /> in config.xml
        loadUrl(launchUrl);
    }
}

進(jìn)而得到以下UML類圖


UML圖

簡(jiǎn)單分析下,CordovaActivity內(nèi)依賴一個(gè)WebView類跪楞,一個(gè)Preferences類形导,一個(gè)CordovaInterface接口环疼,并同時(shí)初始化一些配置信息习霹。WebView具體實(shí)現(xiàn)是由CordovaWebViewImpl類朵耕,CordovaInterface接口具體實(shí)現(xiàn)是由CordovaInterfaceImpl類實(shí)現(xiàn)。

CordovaWebViewImpl是核心類淋叶,里面會(huì)把一些插件能力初始化阎曹,用一個(gè)PluginManager進(jìn)行管理,包含一個(gè)引擎類—CordovaWebViewEngine煞檩,這個(gè)引擎是通過(guò)反射的方式創(chuàng)建处嫌,自身初始化的時(shí)候把NativeToJsMessageQueue關(guān)聯(lián)起來(lái),里面包含著以Js字符串為主的雙向鏈表斟湃,把每次從前端通過(guò)JS代碼存儲(chǔ)起來(lái)熏迹,然后通過(guò)綁定的橋接方式Pop出到相應(yīng)的Native代碼中去。

最終實(shí)現(xiàn)由SystemWebViewEngine類來(lái)對(duì)Android系統(tǒng)中WebView控件進(jìn)行二次包裝凝赛,這個(gè)類的初始化是在CordovaWebViewImpl類反射創(chuàng)建注暗,相關(guān)插件和消息傳遞也是通過(guò)SystemWebViewEngine進(jìn)行綁定。

Cordova實(shí)現(xiàn)機(jī)制

當(dāng)Cordova框架啟動(dòng)時(shí)候墓猎,CordovaActivity類中的onCreate方法調(diào)用loadUrl方法即可啟動(dòng)捆昏,最終在SystemWebViewEngine類的init方法中,會(huì)調(diào)用webView的addJavascriptInterface方法毙沾,看到這個(gè)方法是不是很熟悉骗卜,我們常規(guī)讓webView支持開啟JavaScript調(diào)用接口也是使用此特性。

 private static void exposeJsInterface(WebView webView, CordovaBridge bridge) {
        if ((Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1)) {
            LOG.i(TAG, "Disabled addJavascriptInterface() bridge since Android version is old.");
            // Bug being that Java Strings do not get converted to JS strings automatically.
            // This isn't hard to work-around on the JS side, but it's easier to just
            // use the prompt bridge instead.
            return;
        }
        SystemExposedJsApi exposedJsApi = new SystemExposedJsApi(bridge);
        webView.addJavascriptInterface(exposedJsApi, "_cordovaNative");
    }

那么SystemExposedJsApi類new出來(lái)的對(duì)象就等同拋出“_cordovaNative”對(duì)象給JS端調(diào)用左胞,進(jìn)去看下SystemExposedJsApi類包含哪些內(nèi)容寇仓,

class SystemExposedJsApi implements ExposedJsApi {
    private final CordovaBridge bridge;

    SystemExposedJsApi(CordovaBridge bridge) {
        this.bridge = bridge;
    }

    @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);
    }
}

其中最關(guān)鍵是exec方法,其中bridgeSecret代表選擇哪個(gè)橋接方式烤宙,service一般對(duì)應(yīng)著你本地Java文件類名遍烦,action代表java文件中方法名,callbackId代表回調(diào)函數(shù)的Id门烂,也就是句柄乳愉,arguments代表傳遞的參數(shù)⊥驮叮看出其中設(shè)計(jì)思想了沒蔓姚,service往往是本地能力集的類名,比如web端想調(diào)用相機(jī)慨丐,一般起個(gè)Camera類代表這個(gè)相機(jī)服務(wù)類坡脐,然后在這個(gè)類中定義方法,也就是action參數(shù)房揭,這個(gè)action名稱可擴(kuò)展备闲,因?yàn)榉椒Q可各種各樣晌端,適合自定義功能擴(kuò)展。

SystemExposedJsApi對(duì)象初始化

在創(chuàng)建SystemExposedJsApi時(shí)需要CordovaBridge類恬砂,CordovaBridge類初始化需要CordovaWebView的PluginManager對(duì)象和NativeToJsMessageQueue對(duì)象咧纠。因?yàn)樗械腏S端與Android native代碼交互都是通過(guò)SystemExposedJsApi對(duì)象的exec方法。在exec方法中執(zhí)行PluginManager的exec方法泻骤,PluginManager去查找具體的Plugin并實(shí)例化然后再執(zhí)行Plugin的execute方法漆羔,并根據(jù)同步標(biāo)識(shí)判斷是同步返回給JS消息還是異步。由NativeToJsMessageQueue統(tǒng)一管理返回給JS的消息狱掂。

何時(shí)加載Plugin演痒,如何加載

Cordova中很重要的部分是插件,Cordova在啟動(dòng)每個(gè)Activity的時(shí)候都會(huì)將配置文件中的所有plugin加載到PluginManager趋惨,在第一次loadUrl方法時(shí)鸟顺,就會(huì)去初始化PluginManager并加載plugin,PluginManager在加載plugin的時(shí)候并不是馬上實(shí)例化plugin對(duì)象器虾,而是只是將plugin的Class名字保存到一個(gè)hashmap中讯嫂,用service名字作為key值。當(dāng)JS端通過(guò)JavascriptInterface接口的SystemExposedJsApi對(duì)象請(qǐng)求Android時(shí)曾撤,PluginManager會(huì)從hashmap中查找到plugin端姚,如果該plugin還未實(shí)例化,利用java反射機(jī)制實(shí)例化該plugin挤悉,并執(zhí)行plugin的execute方法渐裸。

Cordova的數(shù)據(jù)返回

Cordova中通過(guò)exec()函數(shù)請(qǐng)求android插件,數(shù)據(jù)的返回可同步也可以異步于exec()函數(shù)的請(qǐng)求装悲。在開發(fā)android插件的時(shí)候可以重寫public boolean isSynch(String action)方法來(lái)決定是同步還是異步昏鹃。Cordova在android端使用了一個(gè)隊(duì)列(NativeToJsMessageQueue)來(lái)專門管理返回給JS的數(shù)據(jù)。

1诀诊,同步
Cordova在執(zhí)行完exec()后洞渤,android會(huì)馬上返回?cái)?shù)據(jù),但不一定就是該次請(qǐng)求的數(shù)據(jù)属瓣,可能是前面某次請(qǐng)求的數(shù)據(jù)载迄;因?yàn)楫?dāng)exec()請(qǐng)求的插件是允許同步返回?cái)?shù)據(jù)的情況下,Cordova也是從NativeToJsMessageQueue隊(duì)列頭pop頭數(shù)據(jù)并返回抡蛙。然后再根據(jù)callbackID反向查找某個(gè)JS請(qǐng)求护昧,并將數(shù)據(jù)返回給該請(qǐng)求的success函數(shù)。
2粗截,異步
Cordova在執(zhí)行完exec()后并不會(huì)同步得到一個(gè)返回?cái)?shù)據(jù)惋耙。Cordova在執(zhí)行exec()的同時(shí)啟動(dòng)了一個(gè)XMLHttpRequest對(duì)象方式或者prompt()函數(shù)方式的循環(huán)函數(shù)來(lái)不停的去獲取NativeToJsMessageQueue隊(duì)列中的數(shù)據(jù),并根據(jù)callbackID反向查找到相對(duì)應(yīng)的JS請(qǐng)求,并將該數(shù)據(jù)交給success函數(shù)绽榛。

webView.sendJavascript 發(fā)送到j(luò)s隊(duì)列湿酸,onNativeToJsMessageAvailable 負(fù)責(zé)執(zhí)行js.

Native 調(diào)用 JS 執(zhí)行方式有三種實(shí)現(xiàn) LoadUrlBridgeMode、 OnlineEventsBridgeMode灭美、PrivateApiBridgeMode

1推溃、webView.sendJavascript 發(fā)送js方法到JS隊(duì)列

2、onJsPrompt 方法攔截冲粤,獲取調(diào)用方式

  • 如果是gap_bridge_mode美莫,則執(zhí)行 appView.exposedJsApi.setNativeToJsBridgeMode(Integer.parseInt(message));
  • 如果是gap_poll, 則執(zhí)行 appView.exposedJsApi.retrieveJsMessages("1".equals(message));

3、調(diào)用setBridgeMode 方法調(diào)用onNativeToJsMessageAvailable 執(zhí)行javascript調(diào)用

小結(jié)

總的來(lái)說(shuō)梯捕,使用Cordova框架開發(fā)優(yōu)缺點(diǎn)很明顯。

優(yōu)點(diǎn):

  • 跨平臺(tái)窝撵,開發(fā)簡(jiǎn)單傀顾,學(xué)習(xí)成本低
  • 框架多,插件多碌奉,可自定義插件
  • 發(fā)展最早短曾,社區(qū)資源豐富,

缺點(diǎn):

  • WebView性能低下時(shí)赐劣,用戶體驗(yàn)差嫉拐,反應(yīng)慢
  • 畢竟是老外的框架,中文文檔資源少
  • 調(diào)試不方便魁兼,既不像原生那么好調(diào)試婉徘,也不像純web那種調(diào)試

最后想說(shuō)一句,無(wú)論是選擇原生模式開發(fā)還是Hybrid混合模式咐汞,一定是要基于具體業(yè)務(wù)場(chǎng)景去選擇盖呼,而不是盲目和絕對(duì)化覺得哪種模式好就不做分析想當(dāng)然的去選擇,還是有選擇的結(jié)合化撕,要知道應(yīng)用之美在于藥到病除几晤。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市植阴,隨后出現(xiàn)的幾起案子蟹瘾,更是在濱河造成了極大的恐慌,老刑警劉巖掠手,帶你破解...
    沈念sama閱讀 221,331評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件憾朴,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡惨撇,警方通過(guò)查閱死者的電腦和手機(jī)伊脓,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,372評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人报腔,你說(shuō)我怎么就攤上這事株搔。” “怎么了纯蛾?”我有些...
    開封第一講書人閱讀 167,755評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵纤房,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我翻诉,道長(zhǎng)炮姨,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,528評(píng)論 1 296
  • 正文 為了忘掉前任碰煌,我火速辦了婚禮舒岸,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘芦圾。我一直安慰自己蛾派,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,526評(píng)論 6 397
  • 文/花漫 我一把揭開白布个少。 她就那樣靜靜地躺著洪乍,像睡著了一般。 火紅的嫁衣襯著肌膚如雪夜焦。 梳的紋絲不亂的頭發(fā)上壳澳,一...
    開封第一講書人閱讀 52,166評(píng)論 1 308
  • 那天,我揣著相機(jī)與錄音茫经,去河邊找鬼巷波。 笑死,一個(gè)胖子當(dāng)著我的面吹牛科平,可吹牛的內(nèi)容都是我干的褥紫。 我是一名探鬼主播,決...
    沈念sama閱讀 40,768評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼瞪慧,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼髓考!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起弃酌,我...
    開封第一講書人閱讀 39,664評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤氨菇,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后妓湘,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體查蓉,經(jīng)...
    沈念sama閱讀 46,205評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,290評(píng)論 3 340
  • 正文 我和宋清朗相戀三年榜贴,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了豌研。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,435評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖鹃共,靈堂內(nèi)的尸體忽然破棺而出鬼佣,到底是詐尸還是另有隱情,我是刑警寧澤霜浴,帶...
    沈念sama閱讀 36,126評(píng)論 5 349
  • 正文 年R本政府宣布晶衷,位于F島的核電站,受9級(jí)特大地震影響阴孟,放射性物質(zhì)發(fā)生泄漏晌纫。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,804評(píng)論 3 333
  • 文/蒙蒙 一永丝、第九天 我趴在偏房一處隱蔽的房頂上張望锹漱。 院中可真熱鬧,春花似錦类溢、人聲如沸凌蔬。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,276評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至懈词,卻和暖如春蛇耀,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背坎弯。 一陣腳步聲響...
    開封第一講書人閱讀 33,393評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工纺涤, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人抠忘。 一個(gè)月前我還...
    沈念sama閱讀 48,818評(píng)論 3 376
  • 正文 我出身青樓撩炊,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親崎脉。 傳聞我的和親對(duì)象是個(gè)殘疾皇子拧咳,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,442評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,265評(píng)論 25 707
  • afinalAfinal是一個(gè)android的ioc,orm框架 https://github.com/yangf...
    passiontim閱讀 15,435評(píng)論 2 45
  • 導(dǎo)語(yǔ) 這篇文章主要介紹了如何在Android平臺(tái)上使用Cordova 的command-line interfac...
    jorstinchan閱讀 22,631評(píng)論 6 41
  • 今天得了空兒囚灼,忽然想到去生態(tài)園逛逛骆膝,因?yàn)樾闹袑?shí)在盼望了很久。 停車場(chǎng)空蕩蕩的灶体,讓人疑惑是不是只有我們前來(lái)阅签。到了湖邊...
    元程閱讀 289評(píng)論 0 0
  • 1.自己的路,自己走蝎抽,走得好與壞政钟,只有走到終點(diǎn)時(shí)才知道。 2.己所不欲勿施于人,己所欲养交,也勿施于人精算。 3.要想清楚...
    絢爛的陽(yáng)光閱讀 168評(píng)論 0 0