概述
介紹如何自己開發(fā)一個Cordova插件(android平臺)。我們用ionic開發(fā)時,如果需要使用Android系統(tǒng)原生功能,Cordova插件是一個很好的選擇。如果沒有現(xiàn)成的cordova 插件,我們就需要自己開發(fā)一個。下面簡單介紹一下如何開發(fā)。
cordova插件分為2大部分,js端和平臺端(Android、ios等代碼);js端定義了統(tǒng)一的接口,供ionic等web開發(fā)框架調(diào)用(用JavaScript方式)。平臺端是具體功能針對不同平臺的實(shí)現(xiàn)。(對應(yīng)Android來說,就是實(shí)現(xiàn)具體功能的Java類,jar包等)。
詳細(xì)的介紹大家參加cordova官網(wǎng)介紹:cordova plugin 。
大致步驟:
這里介紹一下本文的主要步驟:
- 開發(fā)一個簡單的cordova plugin 。例子就采用官網(wǎng)的示例:EchoPlugin。
- 新建一個ionic 項(xiàng)目。使用流行的tabs方式(當(dāng)然這是無關(guān)緊要的)。
- 將EchoPlugin安裝到ionic項(xiàng)目。
- 在ionic項(xiàng)目中,利用JavaScript調(diào)用插件提供的echo接口。
- 運(yùn)行項(xiàng)目,查看結(jié)果。
1. 創(chuàng)建插件
創(chuàng)建插件前,先簡單介紹一下這個EchoPlugin,Android本地端提供了一個EchoPlugin.echo(String message, CallbackContext cbc)方法。這個方法的作用就是把message返回前臺。org.apache.cordova.CallbackContext是cordova框架提供的。JS端定義了window.echo()接口,只要以web js方式調(diào)用這個接口,后臺就能執(zhí)行執(zhí)行的功能(EchoPlugin.echo)。
1.1 創(chuàng)建項(xiàng)目EchoPlugin 框架
在硬盤里找個地方(比如d:\app),創(chuàng)建項(xiàng)目EchoPlugin,官網(wǎng)上也沒提供Maven模板。自己手工建吧。先建項(xiàng)目根目錄:EchoPlugin,然后在根目錄里面建plugin.xml,www目錄(js端啦),src\android(平臺端啦)。參考一下圖片:
1.2 編寫js端代碼(EchoPlugin.js):
window.echo = function(str, callback) {
cordova.exec(callback, function(err) {
callback('Nothing to echo.');
}, "EchoPlugin", "echo", [str]);
};
js端通過cordova.exec()方法,實(shí)現(xiàn)前臺js代碼對后臺Java的調(diào)用。你可以先把項(xiàng)目跑成功再來看下面的廢話。
根據(jù)Cordva官網(wǎng)介紹:Cordova.exec有五個參數(shù),其作用分別如下:
第一個:success回調(diào),當(dāng)Android平臺端函數(shù)執(zhí)行成功后被調(diào)用。
第二個:error回調(diào),當(dāng)Android平臺端函數(shù)執(zhí)行不成功后被調(diào)用。
第三個:Java類名,用于指定Android平臺被調(diào)用方法所在的Java類的類名。
第四個:Java方法名,用于指定Android平臺被調(diào)用方法的method name。
第五個:字符串?dāng)?shù)組,作為參數(shù)args傳遞給Android Java method.
window.echo = function(str,callback) {...} 有兩個參數(shù),str、callback。他里面就調(diào)用了cordava.exec(...) .這里很明顯,str傳遞給cordava.exec作為了第五個參數(shù)。callcack傳遞給cordava.exec成為了第一個參數(shù)。這都不是必須的,自己可以靈活掌握。
1.3 編輯Java端代碼(EchoPlugin.java):
package org.apache.cordova.plugin;
import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.CallbackContext;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
/**
* This class echoes a string called from JavaScript.
*/
public class EchoPlugin extends CordovaPlugin {
@Override
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
if (action.equals("echo")) {
String message = args.getString(0);
this.echo(message, callbackContext);
return true;
}
return false;
}
private void echo(String message, CallbackContext callbackContext) {
if (message != null && message.length() > 0) {
callbackContext.success(message);
} else {
callbackContext.error("Expected one non-empty string argument.");
}
}
}
自定義插件需要繼承CordovaPlugin 并且Override其execute方法。此外我們這個插件還需要實(shí)現(xiàn)echo()方法,這是js接口中指定的后臺方法。使用插件的時候并不關(guān)心這些。
1.4 編寫manifest文件: plugin.xml
<?xml version="1.0" encoding="UTF-8"?>
<plugin xmlns="http://apache.org/cordova/ns/plugins/1.0"
id="cordova-plugin-echo" version="0.1.1">
<name>Echo</name>
<description>Cordova Echo Plugin</description>
<author>July</author>
<license>Apache 2.0</license>
<keywords>cordova,Echo</keywords>
<js-module src="www/EchoPlugin.js" name="echoPlugin">
<clobbers target="echoplugin" />
</js-module>
<platform name="android">
<config-file target="res/xml/config.xml" parent="/*">
<feature name="EchoPlugin">
<param name="android-package" value="org.apache.cordova.plugin.EchoPlugin" />
</feature>
</config-file>
<!-- Required 一些系統(tǒng)要求的權(quán)限,如訪問網(wǎng)絡(luò)等-->
<!-- <config-file target="AndroidManifest.xml" parent="/manifest">
<uses-permission android:name="android.permission.INTERNET" />
</config-file> -->
<source-file src="src/android/EchoPlugin.java" target-dir="src/org/apache/cordova/plugin" />
</platform>
</plugin>
這個文件的ID是插件的名字,估計(jì)發(fā)布到cordova服務(wù)器后可以通過ID就能安裝插件;js-module元素描述了JavaScript接口信息;主要就是指出js文件位置啦;platform元素指定了各平臺(這里只有android)代碼的位置。config-file指定編譯后目標(biāo)平臺配置文件config.xml的位置,cordova要把feature元素中的內(nèi)容復(fù)制到config.xml中。source-file指定Java源碼位置這里有2個位置,src源文件位置,target是ionic項(xiàng)目build android,編譯為目標(biāo)平臺(android)項(xiàng)目后代碼的位置。同樣這段廢話可以等項(xiàng)目跑成功后再看。
我們的插件開發(fā)完啦,libs目錄是空的,因?yàn)檫@個Java類太簡單啦,如果有需要可以將Jar包放在libs目錄里。
下面我們新建一個ionic項(xiàng)目來使用這個插件。當(dāng)然cordova項(xiàng)目,phonegap項(xiàng)目都能用這個插件。我的ionic系列是不會提供環(huán)境搭建教程的。第一次搭建環(huán)境推薦找梯子。
2. 創(chuàng)建ionic 項(xiàng)目。找一個比較合適的目錄,cmd切換到該目錄,執(zhí)行下面的命令。
ionic start testEcho tabs
cd testEcho
ionic platform add android
cordova plugin add D:\app\EchoPlugin
3. 安裝剛才開發(fā)的插件。
嗯剛才的最后一行代碼就是安裝自己開發(fā)的Echo cordova插件。
4. 在ionic項(xiàng)目中使用插件。
找到www\js\app.js,添加如下代碼:
window.echo("echoMe",function(echoValue){
console.log("Echo Plugin called");
console.log(echoValue);
alert(echoValue == "echoMe");
});
添加位置:
// Ionic Starter App
// angular.module is a global place for creating, registering and retrieving Angular modules
// 'starter' is the name of this angular module example (also set in a <body> attribute in index.html)
// the 2nd parameter is an array of 'requires'
// 'starter.services' is found in services.js
// 'starter.controllers' is found in controllers.js
angular.module('starter', ['ionic', 'starter.controllers', 'starter.services'])
.run(function($ionicPlatform) {
$ionicPlatform.ready(function() {
// Hide the accessory bar by default (remove this to show the accessory bar above the keyboard
// for form inputs)
if (window.cordova && window.cordova.plugins && window.cordova.plugins.Keyboard) {
cordova.plugins.Keyboard.hideKeyboardAccessoryBar(true);
cordova.plugins.Keyboard.disableScroll(true);
}
if (window.StatusBar) {
// org.apache.cordova.statusbar required
StatusBar.styleDefault();
}
// 調(diào)用Echo插件
window.echo("echoMe",function(echoValue){
console.log("Echo Plugin called");
console.log(echoValue);
alert(echoValue == "echoMe");
});
});
})
...
5. 查看效果。cmd切換到ionic項(xiàng)目根目錄,就是www目錄的父目錄。
ionic build android
ionic emulate android
效果圖:
控制臺:
6. 其他補(bǔ)充
這個例子很簡單,但包含了開發(fā)一個插件的基本要素。但是Java 插件只是返回了一個信息,沒有和Android原生程序交互。下面補(bǔ)充一下如何調(diào)用Android項(xiàng)目原生activity;比如某個項(xiàng)目的DemoActivity.java.
首先 把DemoActivity.java; 他的layout文件main.xml;他用到的jar包,so文件都辨識出來。一開始漏掉也沒關(guān)系編譯通不過會提示你缺少那些類,根據(jù)提示再補(bǔ)充。然后通過plugin.xml配置這些文件信息,指定文件編譯后在Android項(xiàng)目中的位置就行了。
6.1 activity及 layout;這些文件都是原生Android項(xiàng)目文件,建議在Android項(xiàng)目中完成開發(fā)并且運(yùn)行成功。然后直接復(fù)制文件到相應(yīng)的目錄。參加1.1項(xiàng)目框架。
DemoActivity.java
package com.test.demo;
import android.app.Activity;
/**
* 這里要用ionic項(xiàng)目編譯后的R文件,包名需要自己去查詢*
* %projecthomefolder%\platforms\android\build\generated\source\r\armv7\debug\ *
*/
import com.ionicframework.xxxx.R;
import ...
public class DemoActivity extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
/** other method */
@Override
...
}
main.xml
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:fillViewport="true" >
<LinearLayout
android:id="@+id/Play"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
...
</ScrollView>
6.2 插件后臺Java類,需集成CordovaPlugin
public class DemoPlugin extends CordovaPlugin {
private CallbackContext context;
/** Constructor */
public DemoPlugin() {
}
@Override
public boolean execute(String action,JSONArray args,CallbackContext callbackContext) throws JSONException {
this.context = callbackContext;
if("openDemoActivity".equals(action)) {
//使用Intent啟動DemoActivity
Intent intent = new Intent(this.cordova.getActivity(),com.test.demo.DemoActivity.class);
if(this.cordova != null) {
this.cordova.startActivityForResult((CordovaPlugin) this, intent, 0);
return true;
}
return false;
}
return false;
}
@Override
public void onActivityResult(int requestCode,int resultCode,Intent intent) {
super.onActivityResult(requestCode,resultCode,intent);
//decide what to do by resultCode
if(resultCode == Activity.RESULT_OK) {
context.success("ok");
}
}
}
6.3 plugin.xml
<?xml version="1.0" encoding="UTF-8"?>
<plugin xmlns="http://apache.org/cordova/ns/plugins/1.0"
id="cordova-plugin-hcnet" version="0.1.1">
<name>HcnetPlugin</name>
<description>Cordova HcNet Plugin</description>
<author>July</author>
<license>Apache 2.0</license>
<keywords>cordova,Hcnet</keywords>
<js-module src="www/DemoPlugin.js" name="DemoPlugin">
<clobbers target="DemoPlugin" />
</js-module>
<platform name="android">
<config-file target="res/xml/config.xml" parent="/*">
<feature name="DemoPlugin">
<param name="android-package" value="org.apache.cordova.plugin.DemoPlugin" />
</feature>
</config-file>
<!-- Required 一些系統(tǒng)要求的權(quán)限,如訪問網(wǎng)絡(luò)等-->
<config-file target="AndroidManifest.xml" parent="/manifest">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.CAPTURE_AUDIO_OUTPUT"/>
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
</config-file>
<config-file target="AndroidManifest.xml" parent="/manifest/application">
<activity android:name="com.test.demo.DemoActivity"
android:label="@string/app_name">
</activity>
</config-file>
<source-file src="src/android/DemoPlugin.java" target-dir="src/org/apache/cordova/plugin" />
<source-file src="src/android/DemoActivity.java" target-dir="src/com/test/demo" />
<source-file src="src/android/xxx.java" target-dir="src/com/test/demo" />
<!-- 其他包里面的Java文件,target-dir寫上src/包的路徑 -->
<source-file src="src/android/Other.java" target-dir="src/com/xxx/xxx" />
<!-- lib目錄里面的jar文件,target-dir寫上libs -->
<source-file src="src/android/libs/SomeSDK.jar" target-dir="libs" />
<!-- 布局文件,target-dir寫上libs -->
<source-file src="src/android/res/layout/main.xml" target-dir="res/layout" />
<source-file src="src/android/libs/armeabi/libAudioEngine.so" target-dir="libs/armeabi" />
<source-file src="src/android/libs/armeabi/libCpuFeatures.so" target-dir="libs/armeabi" />
<source-file src="src/android/libs/armeabi/libgnustl_shared.so" target-dir="libs/armeabi" />
<source-file src="src/android/libs/armeabi/libHCAlarm.so" target-dir="libs/armeabi" />
<source-file src="src/android/libs/armeabi/libHCCore.so" target-dir="libs/armeabi" />
<source-file src="src/android/libs/armeabi/libHCCoreDevCfg.so" target-dir="libs/armeabi" />
<source-file src="src/android/libs/armeabi/libHCDisplay.so" target-dir="libs/armeabi" />
<source-file src="src/android/libs/armeabi/libHCGeneralCfgMgr.so" target-dir="libs/armeabi" />
<source-file src="src/android/libs/armeabi/libHCIndustry.so" target-dir="libs/armeabi" />
<source-file src="src/android/libs/armeabi/libhcnetsdk.so" target-dir="libs/armeabi" />
<source-file src="src/android/libs/armeabi/libHCPlayBack.so" target-dir="libs/armeabi" />
<source-file src="src/android/libs/armeabi/libHCPreview.so" target-dir="libs/armeabi" />
<source-file src="src/android/libs/armeabi/libHCVoiceTalk.so" target-dir="libs/armeabi" />
<source-file src="src/android/libs/armeabi/libjnidispatch.so" target-dir="libs/armeabi" />
<source-file src="src/android/libs/armeabi/libopensslwrap.so" target-dir="libs/armeabi" />
<source-file src="src/android/libs/armeabi/libPlayCtrl.so" target-dir="libs/armeabi" />
<source-file src="src/android/libs/armeabi/libSystemTransform.so" target-dir="libs/armeabi" />
</platform>
</plugin>