在混合式应用中,我们通过现有的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.java
和MyToast.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')
+ })
修改完成后,然后构建项目在运行
小菜是在android虚拟机上运行的