一、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");