Cordova android框架詳解

Blue Sky ......發表於2015-01-05

一、Cordova 核心java類說明

 

CordovaActivity:Cordova Activity入口,已實現PluginManager、WebView的相關初始化工作, 只需繼承CordovaActivity實現自己的業務需求。

PluginManager: 外掛管理器

ExposedJsApi :javascript呼叫Native, 通過外掛管理器PluginManager 根據service找到具體實現類。

NativeToJsMessageQueue:Native呼叫javascript,主要包括三種方式:loadUrl 、 輪詢、反射WebViewCore執行js

 

二、 Cordova框架類圖

 

三、Cordova框架啟動

 

當實現了DroidGap或者CordovaInterface介面的Activity的onCreate方法中呼叫DroidGap的loadUrl方法即啟動了Cordova框架。

 

Cordova提供了一個Class(DroidGap extends CordovaActivity)和一個interface(CordovaInterface)來讓Android開發者開發Cordova。

一般情況下實現DroidGap即可,因為DroidGap類已經做了很多準備工作,可以說DroidGap類是Cordova框架的一個重要部分;如果在必要的情況下實現CordovaInterface介面,那麼這個類中很多DroidGap的功能需要自己去實現。繼承了DroidGap或者CordovaInterface的Activity就是一個獨立的Cordova模組,獨立的Cordova模組指的是每個實現了DroidGap或者CordovaInterface介面的Activity都對應一套獨立的WebView,Plugin,PluginManager,沒有共享的。

 

在初始化完CordovaWebView後呼叫CordovaWebView.loadUrl()。此時完成Cordova的啟動。


1.Cordova關聯物件初始化

在例項化CordovaWebView的時候, CordovaWebView物件會去建立一個屬於當前CordovaWebView物件的外掛管理器PluginManager物件,一個訊息佇列NativeToJsMessageQueue物件,一個JavascriptInterface物件ExposedJsApi,並將ExposedJsApi物件新增到CordovaWebView中,JavascriptInterface名字為:_cordovaNative。


2. Cordova的JavascriptInterface
在建立ExposedJsApi時需要CordovaWebView的PluginManager物件和NativeToJsMessageQueue物件。因為所有的JS端與Android native程式碼互動都是通過ExposedJsApi物件的exec方法。在exec方法中執行PluginManager的exec方法,PluginManager去查詢具體的Plugin並例項化然後再執行Plugin的execute方法,並根據同步標識判斷是同步返回給JS訊息還是非同步。由NativeToJsMessageQueue統一管理返回給JS的訊息。


3. 何時載入Plugin,如何載入
 Cordova在啟動每個Activity的時候都會將配置檔案中的所有plugin載入到PluginManager。那麼是什麼時候將這些plugin載入到PluginManager的呢?在b中說了最後會呼叫CordovaWebView.loadUrl(),對,就在這個時候會去初始化PluginManager並載入plugin。PluginManager在載入plugin的時候並不是馬上例項化plugin物件,而是隻是將plugin的Class名字儲存到一個hashmap中,用service名字作為key值。
當JS端通過JavascriptInterface介面的ExposedJsApi物件請求Android時,PluginManager會從hashmap中查詢到plugin,如果該plugin還未例項化,利用java反射機制例項化該plugin,並執行plugin的execute方法。


4.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函式。
   注:Cordova對本地的HTML檔案(file:// 開頭的URL)或者手機設定有代理的情況下使用XMLHttpRequest方式獲取返回資料,其他則使用prompt()函式方式獲取返回資料。

 

5、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呼叫

 

四、Native呼叫javascript 方式:NativeToJsMessageQueue


1、loadUrl javascript 呼叫方式

 

private class LoadUrlBridgeMode extends BridgeMode

if (url.startsWith("file://") || url.startsWith("javascript:") || Config.isUrlWhiteListed(url)) {

}

  


2、Navitive事件通知javascript輪詢獲取Navitive資料

 

private class OnlineEventsBridgeMode extends BridgeMode

 

  

3、通過Java反射獲取webview 的sendMessage 方法執行js, 支援 Android 3.2.4之上(包含)

 

---可以解決loadUrl 隱藏鍵盤的問題:當你的焦點在輸入,如果這通過loadUrl呼叫js,會導致鍵盤隱藏

private class PrivateApiBridgeMode extends BridgeMode

    Field f = webViewClass.getDeclaredField("mProvider");
    f.setAccessible(true);
    webViewObject = f.get(webView);
    webViewClass = webViewObject.getClass();

    Field f = webViewClass.getDeclaredField("mWebViewCore");
    f.setAccessible(true);
    webViewCore = f.get(webViewObject);

   if (webViewCore != null) {
        sendMessageMethod =       webViewCore.getClass().getDeclaredMethod("sendMessage", Message.class);
       sendMessageMethod.setAccessible(true);	
    }

     Message execJsMessage = Message.obtain(null, EXECUTE_JS, url);
     sendMessageMethod.invoke(webViewCore, execJsMessage);

 

  


4、Native註冊javascript介面 _cordovaNative

 

boolean isHoneycomb = (SDK_INT >= Build.VERSION_CODES.HONEYCOMB && SDK_INT <= Build.VERSION_CODES.HONEYCOMB_MR2);
// 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.
if (isHoneycomb || (SDK_INT < Build.VERSION_CODES.GINGERBREAD)) {
Log.i(TAG, "Disabled addJavascriptInterface() bridge since Android version is old.");
return; 
} else if (SDK_INT < Build.VERSION_CODES.HONEYCOMB && Build.MANUFACTURER.equals("unknown")) {
// addJavascriptInterface crashes on the 2.3 emulator.
Log.i(TAG, "Disabled addJavascriptInterface() bridge callback due to a bug on the 2.3 emulator");
return;
}
this.addJavascriptInterface(exposedJsApi, "_cordovaNative");

 

  

 

相關文章