淺談Cordova框架的一些理解
前言
因為工作原因,最近需要研究Cordova框架,看了其中的原始碼和實現方式,當場在看的時候馬上能理解,但是事後再回去看相關原始碼時候卻發現之前理解的內容又忘記了,又不得不重新開始看,所以總覺得需要記錄下來,這樣也表明之前也是學習過,俗話說「好記性不如爛筆頭 」,想必也是體現了筆記的重要性。
目錄
為何要用Cordova
什麼是Cordova
Cordova中UML類圖
Cordova實現機制
小結
為何要用Cordova
隨著移動網際網路的發展,現在基本是APP滿天飛,不知在大家印象中,如果我去下載一個APP,那麼基本都能看到有兩種選擇,一種是Android版本,一種是IOS版本。不管我的手機是哪種作業系統,安裝完一個APP之後,後續如果有新的版本釋出的時候,我還必須去更新,才能享用新版本里的功能,比如我裝了“京東”這個APP,前幾天正好碰到“618”活動,那麼之前一個月APP Store就提醒我要去更新最新的APP版本,以免錯過“618”活動中新的功能使用。相對來說IOS系統更新APP比起Android系統使用者體驗會好一點,但是還是稍顯麻煩點。
那麼有沒有一種方式,我只需要開發一個APP版本,就能去適配通用的作業系統呢,不僅可以適配Android、IOS,還可以適配其他系統,比如Windows Phone、 Palm WebOS、Blackberry等等。有,Cordova就能提供這種能力,程式碼寫一次,就能到處執行,跟我們日常開發網站效果一樣,基於寫Web APP,根據輸出平臺要求不同,就能提供不同型別的安裝包。Cordova其設計初衷是希望使用者群體能夠通過跨平臺開發的方法降低原生開發的成本,為此,開發人員需要安裝原生開發環境,配置工程,使用HTML5、CSS3、JS和原生SDK生成應用。
什麼是Cordova
官網定義如下:
Apache Cordova是一個開源的移動開發框架。允許你用標準的web技術-HTML5,CSS3和JavaScript做跨平臺開發。 應用在每個平臺的具體執行被封裝了起來,並依靠符合標準的API繫結去訪問每個裝置的功能,比如說:感測器、資料、網路狀態等。
使用Apache Cordova的人群:
移動應用開發者,想擴充套件一個應用的使用平臺,而不通過每個平臺的語言和工具集重新實現。
web開發者,想包裝部署自己的web App將其分發到各個應用商店門戶。
移動應用開發者,有興趣混合原生應用組建和一個WebView(一個特別的瀏覽器視窗) 可以接觸裝置A級PI,或者你想開發一個原生和WebView元件之間的外掛介面。
架構圖
從圖中,我們可以看到它提供了Web APP、WebView、Cordova Plugins。
Web APP
這是存放應用程式程式碼的地方,體現是你的具體業務邏輯模組。應用的實現是通過web頁面,預設的本地檔名稱是是index.html
WebView
Cordova啟用的WebView可以給應用提供完整使用者訪問介面。在一些平臺中,他也可以作為一個元件給大的、混合應用,這些應用混合和Webview和原生的應用元件。
Cordova Plugins
外掛是Cordova生態系統的重要組成部分。他提供了Cordova和原生元件相互通訊的介面並繫結到了標準的裝置API上,這使你能夠通過JavaScript呼叫原生程式碼。
Cordova中UML類圖
其實Cordova通過命令來新增專案的,但是可以選擇哪個平臺去編譯,比如我們新增Android平臺,在Android預設mainActivity類,我們可以看到它其實繼承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);
}
}
進而得到以下UML類圖
簡單分析下,CordovaActivity內依賴一個WebView類,一個Preferences類,一個CordovaInterface介面,並同時初始化一些配置資訊。WebView具體實現是由CordovaWebViewImpl類,CordovaInterface介面具體實現是由CordovaInterfaceImpl類實現。
CordovaWebViewImpl是核心類,裡面會把一些外掛能力初始化,用一個PluginManager進行管理,包含一個引擎類—CordovaWebViewEngine,這個引擎是通過反射的方式建立,自身初始化的時候把NativeToJsMessageQueue關聯起來,裡面包含著以Js字串為主的雙向連結串列,把每次從前端通過JS程式碼儲存起來,然後通過繫結的橋接方式Pop出到相應的Native程式碼中去。
最終實現由SystemWebViewEngine類來對Android系統中WebView控制元件進行二次包裝,這個類的初始化是在CordovaWebViewImpl類反射建立,相關外掛和訊息傳遞也是通過SystemWebViewEngine進行繫結。
Cordova實現機制
當Cordova框架啟動時候,CordovaActivity類中的onCreate方法呼叫loadUrl方法即可啟動,最終在SystemWebViewEngine類的init方法中,會呼叫webView的addJavascriptInterface方法,看到這個方法是不是很熟悉,我們常規讓webView支援開啟JavaScript呼叫介面也是使用此特性。
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出來的物件就等同丟擲“_cordovaNative”物件給JS端呼叫,進去看下SystemExposedJsApi類包含哪些內容,
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);
}
}
其中最關鍵是exec方法,其中bridgeSecret代表選擇哪個橋接方式,service一般對應著你本地Java檔案類名,action代表java檔案中方法名,callbackId代表回撥函式的Id,也就是控制代碼,arguments代表傳遞的引數。看出其中設計思想了沒,service往往是本地能力集的類名,比如web端想呼叫相機,一般起個Camera類代表這個相機服務類,然後在這個類中定義方法,也就是action引數,這個action名稱可擴充套件,因為方法名稱可各種各樣,適合自定義功能擴充套件。
SystemExposedJsApi物件初始化
在建立SystemExposedJsApi時需要CordovaBridge類,CordovaBridge類初始化需要CordovaWebView的PluginManager物件和NativeToJsMessageQueue物件。因為所有的JS端與Android native程式碼互動都是通過SystemExposedJsApi物件的exec方法。在exec方法中執行PluginManager的exec方法,PluginManager去查詢具體的Plugin並例項化然後再執行Plugin的execute方法,並根據同步標識判斷是同步返回給JS訊息還是非同步。由NativeToJsMessageQueue統一管理返回給JS的訊息。
何時載入Plugin,如何載入
Cordova中很重要的部分是外掛,Cordova在啟動每個Activity的時候都會將配置檔案中的所有plugin載入到PluginManager,在第一次loadUrl方法時,就會去初始化PluginManager並載入plugin,PluginManager在載入plugin的時候並不是馬上例項化plugin物件,而是隻是將plugin的Class名字儲存到一個hashmap中,用service名字作為key值。當JS端通過JavascriptInterface介面的SystemExposedJsApi物件請求Android時,PluginManager會從hashmap中查詢到plugin,如果該plugin還未例項化,利用java反射機制例項化該plugin,並執行plugin的execute方法。
Cordova的資料返回
Cordova中通過exec()函式請求android外掛,資料的返回可同步也可以非同步於exec()函式的請求。在開發android外掛的時候可以重寫public boolean isSynch(String action)方法來決定是同步還是非同步。Cordova在android端使用了一個佇列(NativeToJsMessageQueue)來專門管理返回給JS的資料。
1,同步
Cordova在執行完exec()後,android會馬上返回資料,但不一定就是該次請求的資料,可能是前面某次請求的資料;因為當exec()請求的外掛是允許同步返回資料的情況下,Cordova也是從NativeToJsMessageQueue佇列頭pop頭資料並返回。然後再根據callbackID反向查詢某個JS請求,並將資料返回給該請求的success函式。
2,非同步
Cordova在執行完exec()後並不會同步得到一個返回資料。Cordova在執行exec()的同時啟動了一個XMLHttpRequest物件方式或者prompt()函式方式的迴圈函式來不停的去獲取NativeToJsMessageQueue佇列中的資料,並根據callbackID反向查詢到相對應的JS請求,並將該資料交給success函式。
webView.sendJavascript 傳送到js佇列,onNativeToJsMessageAvailable 負責執行js.
Native 呼叫 JS 執行方式有三種實現 LoadUrlBridgeMode、 OnlineEventsBridgeMode、PrivateApiBridgeMode
1、webView.sendJavascript 傳送js方法到JS佇列
2、onJsPrompt 方法攔截,獲取呼叫方式
- 如果是gap_bridge_mode,則執行 appView.exposedJsApi.setNativeToJsBridgeMode(Integer.parseInt(message));
- 如果是gap_poll, 則執行 appView.exposedJsApi.retrieveJsMessages("1".equals(message));
3、呼叫setBridgeMode 方法呼叫onNativeToJsMessageAvailable 執行javascript呼叫
小結
總的來說,使用Cordova框架開發優缺點很明顯。
優點:
- 跨平臺,開發簡單,學習成本低
- 框架多,外掛多,可自定義外掛
- 發展最早,社群資源豐富,
缺點:
- WebView效能低下時,使用者體驗差,反應慢
- 畢竟是老外的框架,中文文件資源少
- 除錯不方便,既不像原生那麼好除錯,也不像純web那種除錯
最後想說一句,無論是選擇原生模式開發還是Hybrid混合模式,一定是要基於具體業務場景去選擇,而不是盲目和絕對化覺得哪種模式好就不做分析想當然的去選擇,還是有選擇的結合,要知道應用之美在於藥到病除。