ionic是一個運作在webview上的應用,但是很多功能js搞不定,免不了本地代碼的支援。
ionic在native支援這塊直接用的cordova,cordova有一套webview裡js代碼與native代碼互動的方案,這個就是cordova plugin。
-
- 什麼是cordova plugin
- 如何寫一個自己的插件
-
- 初始化插件結構
- 編寫插件的 js api
- 編寫native代碼
- native代碼如何在項目工程裡生效
- 如何找到native入口類
- 如何在項目工程裡調用插件的js方法
- 如何在接口中使用Promise
- 如何友善的編寫插件native代碼
-
什麼是cordova plugin
一個cordova plugin基本就長這樣:
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiQ3chVEa0V3bT9CX5RXa2Fmcn9CXwczLcVmds92czlGZvwVP9EUTDZ0aRJkSwk0LcxGbpZ2LcBDM08CXlpXazRnbvZ2LcRlMMVDT2EWNvwFdu9mZvwFeFpnTyElaOFTS6hVaWhUYwwmMaZXUYpVd1kmYr50MZV3YyI2cKJDT29GRjBjUIF2LcRHelR3LcJzLctmch1mclRXY39TN3kTO1IDM0ETMxMDM3EDMy8CX0Vmbu4GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.jpg)
他是一個完整的功能子產品,并且在js層向外提供服務。
它包含 javascript代碼,也包含native代碼。javascript代碼向程式提供調用,方法調用時,調用資訊會通過 cordova 的jssdk傳入到native代碼。
是以說,它的核心是js,native之間的調用,其實就是webview的jssdk。
當将插件加入工程時,插件中的js代碼,native代碼都會被copy到工程的相應目錄下,這樣程式運作時,你的程式就可以成功調用到這個插件的功能。
目前,cordova已經有大量的插件,如sqlite,camera , video , 二維碼 等等,可以在下面幾個地方找。
ionic native api list
cordova plugin center
如何寫一個自己的插件
初始化插件結構
需要使用一個工具plugman
npm install -g plugman
建立一個空插件
plugman create –name [dir] –plugin_id [id] –plugin_version 1.0.0
這樣,一個空的插件結構就建立好了。
編寫插件的 js api
插件的目的就是在js層向外提供服務,是以我們先寫這個檔案。
可以看到www目錄結構下的那個js檔案,在裡面編寫調用方法。
var exec = require('cordova/exec');
exports.callNative = function(arg0, success, error) {
exec(success, error, "plugin-name", "callNative", [arg0]);
};
這裡導出了一個叫做 callNative的方法,你可以在程式的ts代碼中調用它。它接受參數,包括回調。你可以根據自己的需要定義自己的接口。
這個方法調用了cordova的exec方法,就是這個方法将你的調用資訊傳遞給native代碼。
我們仔細看一看這個方法:
exec(success, error, plugin-name, method-name, [arg0]);
- success:成功後的回調
- error:失敗回調
- plugin-name: 這裡替換成你的插件名。cordova 運作時維護了一個插件清單,就是根據這個值來路由到你的插件。在plugin.xml中配置。
//plugin.xml
<platform name="android">
<config-file parent="/*" target="res/xml/config.xml">
<feature name="bpdriver”>
<param name="android-package" value="blueprint/plugin/driver/driver" />
</feature>
</config-file>
</platform>
這裡的bpdriver就是你的插件名,cordova.exec方法使用。
這個插件名時配置在android下的,ios下也有一個,建議配成一樣的。而且最好和 plugin—>name 配置成一樣的。
- method-name:方法名,這個參數會傳遞給native方法。
- [arg0]:這裡是一個數組類型的參數,傳遞給native方法。
編寫native代碼。
首先要增加平台
plugman platform add –platform_name android
plugman platform add –platform_name ios
調用
plugman platform add
時,plugman幫大家生成了native入口類示例.
我隻說一下android的。即圖上的那個Driver.java(具體名稱跟這個不一樣,具體要看你的插件工程名)
public class Driver extends CordovaPlugin {
@Override
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
if (action.equals("callNative")) {
Log.d("TAG" , "callNative");
return true;
}
return false;
}
}
這個類我叫他native入口類,因為之前寫的 js接口類中的cordova.exec調用,最後會調用到這個類的 execute方法。
這個類一定要繼承了CordovaPlugin。
我們來看一看參數對應關系
corodva.exec(success, error, plugin-name, method-name, [arg0]);
public boolean execute(String action, JSONArray args, CallbackContext callbackContext)
- cordova.exec參數plugin-name,用于定位到你寫的插件,
- method-name 傳入到 execute 中的 action。
- [arg0]數組,傳入到 execute 中的 args。
- callbackContext中有一系列
success(xxx)
,
error(xxx)
方法,調用這些方法,最後回調用到corodva.exec 傳入的 success , error 回調。
到現在為止, 插件裡的 js方法調用 —> native 方法 —> js回調接口被調用 這一個流程就已經通了。
這一段流程其實就是一個cordova的jssdk。
cordova jssdk的原理?
android的是通過向webview.addJavascriptInterface的方式.
SystemExposedJsApi exposedJsApi = new SystemExposedJsApi(bridge);
webView.addJavascriptInterface(exposedJsApi, "_cordovaNative");
ios的我不懂,誰知道請不吝回複。
到現在為止,整個流程還少兩步:
1. 插件native代碼如何在項目中生效?
2. 如何在項目工程裡調用插件的js代碼?
native代碼如何在項目工程裡生效?
其實plugin之是以能生效,是因為cordova幫你把代碼都copy到工程裡了,包括js和native代碼。
這裡有兩個問題:
1. 如何将native代碼copy到相應的工程目錄下?
2. 如何找到native入口類?
cordova的腳本已經幫我們做了所有的事情,添加插件或添加平台時,會自動将插件的native代碼,copy到相應工程裡。但是,我們需要在 plugin.xml中配置好。
這裡以android來講,ios也是相同的思路。
//plugin.xml中的一段
<platform name="android">
<source-file src="src/android/Driver.java" target-dir="src/blueprint/plugin/driver" />
</platform>
source-file标簽配置了将native代碼copy到相應的工程目錄。
src 填寫在native代碼在插件中的位置,可以是檔案,也可以是檔案夾。
target-dir 是指 複制到工程裡的目錄。
類似的标簽還有 header-file , resource-file。
這個copy有兩個時機起作用
1. 将插件添加到平台時,将檔案從插件copy到相應平台的工程。
2. 将插件從平台移除時,将檔案從相應平台删除。
如何找到native入口類?
//plugin.xml中的一段
<platform name="android">
<feature name=“bpdriver">
<param name="android-package" value=“blueprint.plugin.driver.Driver" />
</feature>
</platform>
這一段,配置了android的native入口類的類名。
ios的配置類似。
如何在項目工程裡調用插件的js方法?
首先,要知道你這個插件js調用對像在哪裡?
//plugin.xml
<js-module name="bpdriver" src="www/driver.js">
<clobbers target="cordova.plugins.bpdriver" />
</js-module>
clobbers配置了這個對象運作時的位置。
如果你的工程是js寫的,那麼,你在工程裡可以這麼調用
window.cordova.plugins.bpdriver.callNative("hello world!",(success)=>{
},(err)=>{
});
如果你的工程是ts寫的,怎麼調用?
方案一: 将window聲明為any類型,使編譯器忽略類型檢查
let w = window as any;
w.cordova.plugins.bpdriver.call(“hello world!”,(success)=>{
},(err)=>{
});
方案二:給插件寫聲明檔案
在插件根目錄下,增加一個ts聲明檔案。
declare interface DriverPlugin {
callNative(data : any , success : (data : any) => void ,err : (data : any) => void);
}
然後在工程裡引用這個聲明檔案
可以在 project/src/declareations.d.ts 中
/// <reference path="../plugins/pluginname/DriverPlugin.d.ts" />
在調用處
let w = window as any;
let driver: DriverPlugin = w.cordova.plugins.bpdriver;
driver.call("hello world!",(success)=>{
},(err)=>{
});
}
如何在接口中使用Promise?
大家調用ionic插件,都知道ionic插件的異步調用都是Promise的,很友善,那麼咱們的自定義插件可不可以也使用Promise?
方案一?
如下:
var exec = require(‘cordova/exec’);
exports.call = function(arg0) {
return new Promise((resolve , reject) =>{
exec(resolve, reject, “bpdriver”, “callPromise”, [arg0]);
});
};
很遺憾,不可以!因為Ionic工程的ts編譯選項可以看一下,target=es5,而Promise是es6的标準。
在ts主工程裡可以使用Promise是因為ts編譯器會吧Promise轉換為callback調用方式。
方案二?
插件接口檔案直接用ts寫行不行?
很遺憾,不可以!因為cordova架構并沒有對插件ts做支援。都到手機裡了還是ts代碼,明顯用不了。
那為什麼ionic的插件都是Promise的?
ionic 之是以能用Promise調用,是因為ionic-native工程裡針對每一個插件都寫一個*.ts類,這個類将callback方式轉換為Promise方式。大家也可以在自己的工程裡寫一個ts的包裝類,将調用都轉換為Promise的方式。
如何友善的編寫插件native代碼?
說實在的,大家也不可能直接就在vscode上這麼啪啪啪,實在傷不起呀。
當然得是 用 android stuido 在 platforms/android 下開發。用xcode在platforms/ios下開發。
開發過程中切記不要運作
ionic build
ionic run
,他會把plugins/xxx下的代碼copy到platform/下,沖掉你剛寫的代碼,你會哭的。
開發完記得将代碼copy到插件目錄,不然等于白幹。
手動copy太麻煩,容易出問題,還是寫個腳本。
修改插件js,plugin.xml 時,需要更新插件,沒有直接的指令,隻好删除再添加插件。
cordova plugin remove blueprint-plugin-driver && cordova plugin add ../cordovaplugin/driver