開發(fā)一個Cordova插件 ionic系列一

概述

介紹如何自己開發(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

大致步驟:

這里介紹一下本文的主要步驟:

  1. 開發(fā)一個簡單的cordova plugin 。例子就采用官網(wǎng)的示例:EchoPlugin。
  2. 新建一個ionic 項(xiàng)目。使用流行的tabs方式(當(dāng)然這是無關(guān)緊要的)。
  3. 將EchoPlugin安裝到ionic項(xiàng)目。
  4. 在ionic項(xiàng)目中,利用JavaScript調(diào)用插件提供的echo接口。
  5. 運(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(平臺端啦)。參考一下圖片:

cordova_plugin_archetype.png

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

效果圖:

cordova_plugin_result1.png

控制臺:

cordova_result.png

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>

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,002評論 6 542
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,400評論 3 429
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,136評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,714評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,452評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,818評論 1 328
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,812評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,997評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,552評論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 41,292評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,510評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,035評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,721評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,121評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,429評論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,235評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,480評論 2 379

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,725評論 25 708
  • ionic是一個運(yùn)行在webview上的應(yīng)用,但是很多功能js搞不定,免不了本地代碼的支持。ionic在nativ...
    李澤1988閱讀 3,051評論 0 3
  • 偶爾會聽到身邊有朋友說,自己的愛人很“假”,口是心非。我很好奇,為什么對方的反應(yīng)會他們以為愛人很“假”呢?直接有一...
    昭君故事薈閱讀 301評論 0 0
  • 2017.4.1感賞日記 感賞女兒今天感冒好多了 感賞小鳥相處的融洽一些了 感賞自己利用晚上看完了省下的半本書 感...
    悄然h閱讀 211評論 0 0