开发一个Cordova插件 ionic系列一

概述

介绍如何自己开发一个Cordova插件(android平台)。我们用ionic开发时,如果需要使用Android系统原生功能,Cordova插件是一个很好的选择。如果没有现成的cordova 插件,我们就需要自己开发一个。下面简单介绍一下如何开发。
cordova插件分为2大部分,js端和平台端(Android、ios等代码);js端定义了统一的接口,供ionic等web开发框架调用(用JavaScript方式)。平台端是具体功能针对不同平台的实现。(对应Android来说,就是实现具体功能的Java类,jar包等)。
详细的介绍大家参加cordova官网介绍:cordova plugin

大致步骤:

这里介绍一下本文的主要步骤:

  1. 开发一个简单的cordova plugin 。例子就采用官网的示例:EchoPlugin。
  2. 新建一个ionic 项目。使用流行的tabs方式(当然这是无关紧要的)。
  3. 将EchoPlugin安装到ionic项目。
  4. 在ionic项目中,利用JavaScript调用插件提供的echo接口。
  5. 运行项目,查看结果。

1. 创建插件

创建插件前,先简单介绍一下这个EchoPlugin,Android本地端提供了一个EchoPlugin.echo(String message, CallbackContext cbc)方法。这个方法的作用就是把message返回前台。org.apache.cordova.CallbackContext是cordova框架提供的。JS端定义了window.echo()接口,只要以web js方式调用这个接口,后台就能执行执行的功能(EchoPlugin.echo)。

1.1 创建项目EchoPlugin 框架

在硬盘里找个地方(比如d:\app),创建项目EchoPlugin,官网上也没提供Maven模板。自己手工建吧。先建项目根目录: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()方法,实现前台js代码对后台Java的调用。你可以先把项目跑成功再来看下面的废话。
根据Cordva官网介绍:Cordova.exec有五个参数,其作用分别如下:
第一个:success回调,当Android平台端函数执行成功后被调用。
第二个:error回调,当Android平台端函数执行不成功后被调用。
第三个:Java类名,用于指定Android平台被调用方法所在的Java类的类名。
第四个:Java方法名,用于指定Android平台被调用方法的method name。
第五个:字符串数组,作为参数args传递给Android Java method.
window.echo = function(str,callback) {...} 有两个参数,str、callback。他里面就调用了cordava.exec(...) .这里很明显,str传递给cordava.exec作为了第五个参数。callcack传递给cordava.exec成为了第一个参数。这都不是必须的,自己可以灵活掌握。

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方法。此外我们这个插件还需要实现echo()方法,这是js接口中指定的后台方法。使用插件的时候并不关心这些。

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  一些系统要求的权限,如访问网络等-->
        <!-- <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是插件的名字,估计发布到cordova服务器后可以通过ID就能安装插件;js-module元素描述了JavaScript接口信息;主要就是指出js文件位置啦;platform元素指定了各平台(这里只有android)代码的位置。config-file指定编译后目标平台配置文件config.xml的位置,cordova要把feature元素中的内容复制到config.xml中。source-file指定Java源码位置这里有2个位置,src源文件位置,target是ionic项目build android,编译为目标平台(android)项目后代码的位置。同样这段废话可以等项目跑成功后再看。
我们的插件开发完啦,libs目录是空的,因为这个Java类太简单啦,如果有需要可以将Jar包放在libs目录里。
下面我们新建一个ionic项目来使用这个插件。当然cordova项目,phonegap项目都能用这个插件。我的ionic系列是不会提供环境搭建教程的。第一次搭建环境推荐找梯子。

2. 创建ionic 项目。找一个比较合适的目录,cmd切换到该目录,执行下面的命令。

 ionic start testEcho tabs
 cd testEcho
ionic platform add android
cordova plugin add D:\app\EchoPlugin

3. 安装刚才开发的插件。

嗯刚才的最后一行代码就是安装自己开发的Echo cordova插件。

4. 在ionic项目中使用插件。

找到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();
    }

    // 调用Echo插件

    window.echo("echoMe",function(echoValue){
      console.log("Echo Plugin called");
      console.log(echoValue);
      alert(echoValue == "echoMe");
    });
  });
})
...

5. 查看效果。cmd切换到ionic项目根目录,就是www目录的父目录。

ionic build android
ionic emulate android

效果图:

cordova_plugin_result1.png

控制台:

cordova_result.png

6. 其他补充

这个例子很简单,但包含了开发一个插件的基本要素。但是Java 插件只是返回了一个信息,没有和Android原生程序交互。下面补充一下如何调用Android项目原生activity;比如某个项目的DemoActivity.java.

首先 把DemoActivity.java; 他的layout文件main.xml;他用到的jar包,so文件都辨识出来。一开始漏掉也没关系编译通不过会提示你缺少那些类,根据提示再补充。然后通过plugin.xml配置这些文件信息,指定文件编译后在Android项目中的位置就行了。

6.1 activity及 layout;这些文件都是原生Android项目文件,建议在Android项目中完成开发并且运行成功。然后直接复制文件到相应的目录。参加1.1项目框架。

DemoActivity.java
package com.test.demo;
import android.app.Activity;
/**
* 这里要用ionic项目编译后的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  一些系统要求的权限,如访问网络等-->
    <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>

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 162,408评论 4 371
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,690评论 2 307
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 112,036评论 0 255
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,726评论 0 221
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 53,123评论 3 296
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 41,037评论 1 225
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,178评论 2 318
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,964评论 0 213
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,703评论 1 250
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,863评论 2 254
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,333评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,658评论 3 263
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,374评论 3 244
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,195评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,988评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,167评论 2 285
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,970评论 2 279

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,598评论 25 707
  • ionic是一个运行在webview上的应用,但是很多功能js搞不定,免不了本地代码的支持。ionic在nativ...
    李泽1988阅读 2,954评论 0 3
  • 偶尔会听到身边有朋友说,自己的爱人很“假”,口是心非。我很好奇,为什么对方的反应会他们以为爱人很“假”呢?直接有一...
    昭君故事荟阅读 267评论 0 0
  • 2017.4.1感赏日记 感赏女儿今天感冒好多了 感赏小鸟相处的融洽一些了 感赏自己利用晚上看完了省下的半本书 感...
    悄然h阅读 193评论 0 0