鍍金池/ 教程/ 嵌入式/ JS 是如何調(diào)用本地 API 的?
云端 Cordova
UI 框架 jQuery Mobile
配置文件 config.xml
UI 框架 Ionic Framework
Plugin 開發(fā)
slides & books
應用圖標 icon 和啟動頁面 SplashScreen
Sample 工程解析
使用 Hooks 自定義 build 過程
JS 是如何調(diào)用本地 API 的?
deviceready 事件
為 Android APK 簽名
調(diào)試工具 Debug
幾個不可或缺的 lib
環(huán)境搭建(Windows / Android)
Native API 的使用

JS 是如何調(diào)用本地 API 的?

Cordova 應用基于 Webview,所以后臺代碼和 js 交互都是基于 Webview(Webkit)的接口的。

Android:WebView(WebKit-based) WebView(4.4 Chromium-based) Updatable-WebViews(5+) @JavascriptInterface/WebView#addJavascriptInterface()
參考源碼 ExposedJsApi.java

iOS:UIWebView(iOS 4+) WKWebView(iOS 8.1+) UIWebViewDelegate/UIWebView#stringByEvaluatingJavaScriptFromString()
參考源碼 CDVWebViewDelegate.m

http://wiki.jikexueyuan.com/project/cordova-3.x-primer-foundation/images/11.1.png" alt="picture11.1" />

以下以 Android 調(diào)用照相機為例,簡單說明一下調(diào)用及回調(diào)過程。

(1)創(chuàng)建的過程

①添加插件

引用

cordova plugin add org.apache.cordova.camera

在 plugins 的目錄下創(chuàng)建 org.apache.cordova.camera 文件夾,并將該 Plugin 的所有代碼 Copy 進去,具體代碼依賴關系都記錄在 plugin.xml 里。

②創(chuàng)建 Android 工程

引用

cordova platform add android

從上邊的 Plugin 文件夾中把 Java 文件和 js 文件 Copy 到 Android 工程的相應的文件夾下。

  • platforms\android\src\org\apache\cordova\camera\CameraLauncher.java 等
  • platforms\android\assets\www\plugins\org.apache.cordova.camera\www\Camera.js 等

其中 CameraLauncher 擴展自 CordovaPlugin,而 CordovaPlugin 定義在 platforms\android\CordovaLib 中,它是 Cordova 的基礎框架代碼。

(2)調(diào)用的過程(JS->Native)

①HTML 中引入 cordova.js

引用

<script type="text/javascript" src="cordova.js"></script>

先做初始化處理,后根據(jù) cordova_plugins.js 加載所有 plugin 的 js 文件。

②在 deviceready 事件中調(diào)用 Camera

Js 代碼

navigator.camera.getPicture(onSuccess, onFail,   
          { quality: 50,   
            allowEdit: true,   
            destinationType: destinationType.DATA_URL });

③調(diào)用 Camera.js 的 getPicture 方法

assets\www\plugins\org.apache.cordova.camera\www\Camera.js 

getPicture() 
-> 
exec(successCallback, errorCallback, "Camera", "takePicture", args) 
-> 
function androidExec(success, fail, service, action, args) ***cordova.js 
-> 
var messages = nativeApiProvider.get().exec(service, action, callbackId, argsJson); 

④調(diào)入 Java 的 exec()方法

在 CordovaWebView 初期化的時候會根據(jù) Android 的版本,將 ExposedJsApi 對象添加到 CordovaWebView 中。 this.addJavascriptInterface(exposedJsApi, "_cordovaNative");
所以 nativeApiProvider.get()的時候會根據(jù) _cordovaNative 對象是否存在來判斷是使用JavascriptInterface 方式,還是使用 prompt 方式。

如果使用 JavascriptInterface 方式(Android 4.2以上版本),直接進入 ExposedJsApi.java 中定義了@JavascriptInterface 標示的 exec()方法 。

如果使用 prompt 方式,CordovaChromeClient.java 中重寫了 onJsPrompt()方法,來調(diào)用 exposedJsApi.exec()。
prompt(argsJson, 'gap:'+JSON.stringify([service, action, callbackId]));

總之入口都是 exposedJsApi.exec().

@JavascriptInterface

public String exec(String service, String action, String callbackId, String arguments) 
-> 
pluginManager.exec(service, action, callbackId, arguments); 

PluginManager 根據(jù) service 調(diào)用獲取到相應的 CordovaPlugin

-> 
CameraLauncher.execute(String action, JSONArray args, CallbackContext callbackContext)

CameraLauncher 根據(jù) action,這里是“takePicture”做本地 API 調(diào)用。

-> 
takePicture() 
-> 
new Intent("android.media.action.IMAGE_CAPTURE"); 
cordova.startActivityForResult() 

調(diào)用 CordovaInterface(CordovaActivity->Activity)的 startActivityForResult

(3)回調(diào)的過程(Native->JS)

①上述 API 調(diào)用成功后,在 onActivityResult(CameraLauncher.java)設置結果
onActivityResult(int requestCode, int resultCode, Intent intent)

// Send Uri back to JavaScript for viewing image this.callbackContext.success(uri.toString());

②退回到 ExposedJsApi 的 exec()方法

jsMessageQueue.setPaused(false); 
-> 
NativeToJsMessageQueue的onNativeToJsMessageAvailable() 
-> 
sendMessageMethod = webViewCore.getClass().getDeclaredMethod("sendMessage", Message.class) 
-> 
sendMessageMethod.invoke(webViewCore, execJsMessage) 

③cordova.js 中接收消息

androidExec.processMessages(messages) 
-> 
processMessage(message) 
-> 
cordova.callbackFromNative(callbackId, success, status, [payload], keepCallback);

調(diào)用定義好的成功或者失敗的 JS 回調(diào)函數(shù)。(payload 為回傳值)

以上就是完整的過程。

參考:
http://blog.devtang.com/blog/2012/03/24/talk-about-uiwebview-and-phonegap/