在混合式应用中,我们通过现有的Cordova插件,可以轻松的在 H5 上调用手机native的功能。现有的Cordova插件能满足平时大部分的开发需求,然而,有时候找不到合适的插件、或对找到的插件有不满意的地方,那就要动手去做或改写一个插件,这时候就要了解一些Cordova插件的相关知识。

那Cordova插件的基础要点是什么呢?其实就是把原生代码调用方法映射为js的统一接口,供H5使用而已.

1.观察现有应用结构

打开任意一个基于Cordova技术的hybird app的开发目录,添加过平台(android、ios等)和安装过插件的话,你会发现结构大致是这样子的:

├── platforms
|    ├── android
|    ├── ios
|    └── ...
├── plugins
|    ├── org.apache.cordova.device
|    └── ...
├── config.xml
└── www

这里的platforms是我们应用支持的平台目录,plugins是我们安装的插件目录,config.xml 是应用的配置信息(应用名称、描述等),www 是我们的 web 工程目录。

也就是说,如果我们创建一个新的插件并安装,也会添加到plugins目录中,同时修改其它文件信息。

2.使用plugman开发Cordova插件

一个独立插件的目录基本结构是这样的:

MyToast
├── src
|    ├── android
|    |    └── MyToast.java
|    ├── ios
|    └── ...
├── www
|    └── MyToast.js
└── plugin.xml

src存放的是各平台的原生代码,plugin.xml为插件描述及配置文件,www是web工程目录(其实主要就是MyToast.js这个js中间件),我们可以手动创建这几个目录及文件,然而这并不是一个好的方式,因为效率不高,推荐的方式是使用plumam。

安装plugman命令

npm install -g plugman

#也可用yarn命令进行安装
yarn global add plugman

创建plugin

使用plumam创建插件的命令是

plugman create --name pluginName --plugin_id pluginID --plugin_version version  [--path path] [--variable NAME=VALUE]
参数说明
pluginName插件名称,如MyToast
pluginID插件id, 如:org.demo.mytoast
version版本号, 如:0.0.1
path插件存放的绝对或相对路径
variable NAME=VALUE扩展参数,如说明或作者,如woodstream

创建一个MyToast插件

plugman create --name MyToast --plugin_id org.demo.mytoast --plugin_version 0.0.1

这样将会在当前目录创建一个MyToast插件,进入插件目录,打开plugin.xml查看,注意以下内容及说明:

plugin
- id:插件唯一标识
- version:版本号
- js-module
    src:js中间件相对文件地址(www目录下的那个js)
    name:模块名称
    clobbers/merges
        target:H5通过它调用js中间件方法(ts调用方法的前缀)
- platform
    name:对应平台android | ios
    source-file
        src:类名
        tartget-dir:插件文件复制到到原生项目位置
        feature
             name:js中间件通过它调用原生方法(包名)
        uses-permission:相关原生权限

等你消化完plugin.xml文件后,敲入命令进入插件目录:

cd MyToast

添加支持平台

plugman platform add --platform_name android
plugman platform add --platform_name ios

添加完支持的平台后,就会分别创建MyToast.javaMyToast.m文件

src
|--android
    |--MyToast.java
|ios
   |--MyToast.m

这里演示以android代码为例,打开MyToast.java文件

MyToast.java

public class MyToast extends CordovaPlugin {

  @Override
  public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
    if (action.equals("coolMethod")) {
        String message = args.getString(0);
        this.coolMethod(message, callbackContext);
        return true;
    }
    return false;
  }

  private void coolMethod(String message, CallbackContext callbackContext) {
    if (message != null && message.length() > 0) {
        callbackContext.success(message);
    } else {
        callbackContext.error("Expected one non-empty string argument.");
    }
  }
}

我们修改一下java代码让其弹出一个提示框。

package cordova.plugin.mytoast;

+ import android.app.Activity;
+ import android.widget.Toast;

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 MyToast extends CordovaPlugin {

    @Override
    public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
        if (action.equals("coolMethod")) {
            String message = args.getString(0);
            this.coolMethod(message, callbackContext);
            return true;
        }
        return false;
    }

    private void coolMethod(String message, CallbackContext callbackContext) {
        if (message != null && message.length() > 0) {
+           Activity activity = this.cordova.getActivity();
+           android.widget.Toast.makeText(activity, message, Toast.LENGTH_SHORT).show();
            callbackContext.success(message);
        } else {
            callbackContext.error("Expected one non-empty string argument.");
        }
    }
}

打开www/MyToast.js

var exec = require('cordova/exec');

exports.coolMethod = function (arg0, success, error) {
    exec(success, error, 'MyToast', 'coolMethod', [arg0]);
};

MyToast.java代码中 class MyToast 继承了CordovaPlugin,将MyToast类挂载到了cordova.plugin上面。MyToast.js代码中执行一下MyToast中coolMethod方法。

3.创建json文件

后来的Cordova版本要求添加一个package.json来管理插件,而plumam没有给我们创建这样一个文件,于是我们手动创建,借助npm init命令创建package.json。里面的参数从plugin.xml拿过来便是:

{
 "name": "MyToast",
  "version": "0.0.1",
  "description": "demo",
  "cordova": {
    "id": "org.demo.mytoast",
    "platforms": [
      "android"
    ]
  },
  "keywords": [],
  "author": "demo",
  "license": "MIT"
}

4.安装已开发完成的插件

如果没有现成的项目,可以创建一个新的cordova项目来测试:

cordova create hello com.example.hello HelloWorld

cd hello

cordova platform add android

然后像平常添加插件一样,运行如下命令:(add 后面为插件所在本地或网络路径):

cordova plugin add F:\cordova\firstApp\MyToast

如果已有项目且是ionic项目,则命令前追加上ionic:

ionic cordova plugin add F:\cordova\firstApp\MyToast

在ionic2或以上使用时,打开任意一个ts文件,在头部声明如下:

declare let cordova: any;

检测插件是否安装

执行下面命令,查看刚才安装的插件是否被添加进去

#查看插件
cordova plugin ls
或者
ionic cordova plugin list

#移除插件
cordova plugin remove XXXXX(你的plugin_id)
或者
ionic cordova plugin remove XXXXX(你的plugin_id)

5.调用自定义的插件

打开刚才创建的hello项目,修改下面文件

www/index.html

<!DOCTYPE html>

<html>
    <head>
        <!--
        Customize this policy to fit your own app's needs. For more guidance, see:
            https://github.com/apache/cordova-plugin-whitelist/blob/master/README.md#content-security-policy
        Some notes:
            * gap: is required only on iOS (when using UIWebView) and is needed for JS->native communication
            * https://ssl.gstatic.com is required only on Android and is needed for TalkBack to function properly
            * Disables use of inline scripts in order to mitigate risk of XSS vulnerabilities. To change this:
                * Enable inline JS: add 'unsafe-inline' to default-src
        -->
        <meta http-equiv="Content-Security-Policy" content="default-src 'self' data: gap: https://ssl.gstatic.com 'unsafe-eval'; style-src 'self' 'unsafe-inline'; media-src *; img-src 'self' data: content:;">
        <meta name="format-detection" content="telephone=no">
        <meta name="msapplication-tap-highlight" content="no">
        <meta name="viewport" content="initial-scale=1, width=device-width, viewport-fit=cover">
        <link rel="stylesheet" type="text/css" href="css/index.css">
        <title>Hello World</title>
    </head>
    <body>
        <div class="app">
            <h1>Apache Cordova</h1>
+           <button id="wechat">微信</button>
            <div id="deviceready" class="blink">
                <p class="event listening">Connecting to Device</p>
                <p class="event received">Device is Ready</p>
            </div>
        </div>
        <script type="text/javascript" src="cordova.js"></script>
        <script type="text/javascript" src="js/index.js"></script>
    </body>
</html>

www/js/index.js

var app = {
    // Application Constructor
    initialize: function() {
        document.addEventListener('deviceready', this.onDeviceReady.bind(this), false);
    },

    // deviceready Event Handler
    //
    // Bind any cordova events here. Common events are:
    // 'pause', 'resume', etc.
    onDeviceReady: function() {
        this.receivedEvent('deviceready');
    },

    // Update DOM on a Received Event
    receivedEvent: function(id) {
        var parentElement = document.getElementById(id);
        var listeningElement = parentElement.querySelector('.listening');
        var receivedElement = parentElement.querySelector('.received');

        listeningElement.setAttribute('style', 'display:none;');
        receivedElement.setAttribute('style', 'display:block;');

        console.log('Received Event: ' + id);
    }
};

app.initialize();



+ document.getElementById('wechat').addEventListener('click', function () {
+    cordova.plugins.MyToast.coolMethod('hello') 
+ })

修改完成后,然后构建项目在运行

cordova run android

小菜是在android虚拟机上运行的

ssl

cordova 教程

cordova 安装创建项目(1) cordova 自定义使用插件(2) cordova-plugin-wechat 微信登录(3) android 微信登录报错 android QQ报错