首先有Ionic2以及Cordova环境
如果没有在命令行执行以下命令
npm install -g ionic cordova //全局安装ionic 和cordova指令
ionic start myApp tabs --v1 创建ionic2项目之前文章讲到了 “--v1&&--v2代表ionic不同版本”
npm install -g plugman //全局安装cordova插件命令
cordova platform add android //添加android平台 默认创建的项目只包含ios平台 其他的平台需要使用命令添加
现在基本基本开发环境已经就绪如果需要具体安装环境请跳转到Ionic2探索总结
首先我们查看一下plugman的帮助命令 直接输入plugman回车就能看到帮助,从帮助中找到下面这一句话
plugman create --name <pluginName> --plugin_id <pluginID> --plugin_version <version> [--path <directory>] [--variable NAME=VALUE]
这句话就是创建插件的命令,下面我给出一个创建的事例代码 最好是到刚才创建好的项目跟目录执行
plugman create --name bluetooths --plugin_id bluetooths --plugin_version 0.0.1
/*
[--path <directory>]这个是可选的,如果你想创建到别的目录下的话可以通过这个可选的指定路径,如果仅仅想创建到当前路径下的话 可以省略这个可选的指令
*/
来看一下插件创建完毕后的目录以及文件
.
├── plugin.xml
├── src
└── www
└── bluetooths.js
这是当前的目录,然而感觉缺点什么?缺的是各平台的代码。我们再回头看看plugman这个命令的帮助 有这样的命令
Add a Platform to a Plugin
--------------------------
$ plugman platform add --platform_name <platform>
Parameters:
- <platform>: One of android, ios
Remove a Platform from a Plugin
-------------------------------
$ plugman platform remove --platform_name <platform>
Parameters:
- <platform>: One of android, ios
看到这应该就知道了 我们添加平台
cd bluetooths
plugman platform add --platform_name android
plugman platform add --platform_name ios
我们查看目录
.
├── plugin.xml
├── src
│ ├── android
│ │ └── bluetooths.java
│ └── ios
│ └── bluetooths.m
└── www
└── bluetooths.js
4 directories, 4 files
分析文件
plugin.xml
<?xml version='1.0' encoding='utf-8'?>
<plugin id="bluetooths" version="0.0.1" xmlns="http://apache.org/cordova/ns/plugins/1.0" @1 xmlns:android="http://schemas.android.com/apk/res/android">
<name>bluetooths</name> @2
<js-module name="bluetooths" src="www/bluetooths.js"> @3
<clobbers target="cordova.plugins.bluetooths" /> @4
</js-module> @5
<platform name="android"> @6
<config-file parent="/*" target="res/xml/config.xml">
<feature name="bluetooths">
<param name="android-package" value="bluetooths.bluetooths" />
</feature>
</config-file>
<config-file parent="/*" target="AndroidManifest.xml" />
<source-file src="src/android/bluetooths.java" target-dir="src/bluetooths/bluetooths" />
</platform>
<platform name="ios"> @7
<config-file parent="/*" target="config.xml">
<feature name="bluetooths">
<param name="ios-package" value="bluetooths" />
</feature>
</config-file>
<source-file src="src/ios/bluetooths.m" />
</platform>
</plugin>
1, 这个行指定了这个插件的id 版本
2, 插件名字
3, 4,5, 这个是插件的js部分 src说明js插件的文件的位置 target代表在怎么在全局中引用这个插件如果在es5中可以直接使用cordova.plugins.bluetooths这个对象上的各个方法,如果在es6以及typescript中使用的时候得先在代码的最上边加入这个
declare var cordova:any
...
cordova.plugins.bluetooths.function...
6, android平台的配置
7, ios平台的配置
bluetooths.js
var exec = require('cordova/exec'); //引入cordova内部已经实现与原生交互的接口
exports.coolMethod = function(arg0, success, error) {
exec(success, error, "bluetooths", "coolMethod", [arg0]);
};
上面三行代码实现了 导出一个名为coolMethod的方法 这个方法有三个参数 arg0为我们调用这个方法的时候参数,以及回调方法。
这个方法的内部是调用cordova插件的接口前两个是回调的方法,第三个是指定会响应到那个插件,第四个表面看是调用指定的方法,其实cordova插件的工作只是把coolMethod这个值传了过去(这个在android代码的时候说明) 以及后面的参数。
我们现在来看android的代码
public class bluetooths 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.");
}
}
}
这个插件类继承CordovaPlugin类,重写execute这个方法
action:触发的事件名 String类型
args:之前用户调用插件穿的值 JSONArray类型
callbackContext:回调类 CallbackContext类型
为什么说只是传了个方法的值过来,先看action是一个字符串类型的,在下面我们通过action.equals("coolMethod")来判断是否相等 这个类似于 ==。如果相等就调用this.coolMethod(message, callbackContext);这个内部方法可以随意更改。
当我们完成这插件后,我们就需要把这个插件应用到我们的项目中(ios因为没接触过暂时不讲)
cd ../
cordova plugin add ./bluetooths/
添加完成后点开plugins目录看到有bluetoots这个文件夹说明插件添加成功,调用在前面已经说了,下面写一下具体的用法
declare var cordova:any
...
cordova.plugins.bluetooths.coolMethod("message",(res)=>{
alert(res)
},(err)=>{
alert(err)
})
虽然cordvoa提供的插件库比较丰富,但是我们的业务需要监听蓝牙被用户手动在设置里更改后的信息,因为cordova提供的插件并没有这样的监听,所以踩这个坑了。
蓝牙监听插件js代码
js这边的代码js这边的代码非常简单
var arg0="message"
exports.registerReceiver = function(success,error){ //注册监听的js方法
exec(success,error,"bluetooths","registerReceiver",[arg0]);
}
exports.unregisterReceiver = function(success,error){
exec(success,error,"bluetooths","unregisterReceiver",[arg0]); //取消监听的js方法
}
蓝牙监听插件android代码
添加权限
在android代码里面基本都是纯android api里的方法,类。所以熟悉android的很容易写出下面的代码,当然我是个纯web前端开发出生的,android代码写的不好请见谅。
因为是要操作蓝牙所以需要取得权限在AndroidManifest.xml文件中的manifest标签里添加下面的代码
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
分发执行函数
在js代码中我们传递的是五个参数 但是经过CordovaPlugin这个类的接受处理后我们在adnroid看到的只有三个参数。
它把js这端的成功和失败回调通过CallbackContext处理,使得我们可以通过类型为CallbackContext传进来的参数,调用js端的方法,同时传参给js方法。第三个参数也是就“bluetooths”是给CordovaPlugin的标志,调用的是哪一个插件,最后两个参数分别为调用的方法名以及参数。对应到execute方法中的参数为action,args。
先匹配方法名调用不同的方法或者做不同的事代码如下
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
if(action.equals("registerReceiver")){ //如果action==“registerReceiver”注册
String message = args.getString(0);
this.registerReceiver(callbackContext); //自定义方法后面讲
return true;
}else if(action.equals("unregisterReceiver")){ 如果action==“unregisterReceiver” 取消
this.unregisterReceiver(callbackContext); //自定义方法后面讲
}
return true;
}
device对象转化为JSONObject
这段代码是从cordova-plugin-bluetooth-serial这个插件的328-338行代码拷贝过来的
private JSONObject deviceToJSON(BluetoothDevice device) throws JSONException {
JSONObject json = new JSONObject();
json.put("name", device.getName());
json.put("address", device.getAddress());
json.put("id", device.getAddress());
if (device.getBluetoothClass() != null) {
json.put("class", device.getBluetoothClass().getDeviceClass());
}
return json;
}
蓝牙显示通信构建方法(IntentFilter)
private IntentFilter makeFilter() {
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED); //添加连接action
filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);//添加断开action
return filter;
}
注册和注销
先在bluetooths对象中创建两个私有对象
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
BroadcastReceiver mReceiver =null;
创建私方法 registerReceiver
private void registerReceiver(final CallbackContext callbackContext) throws JSONException {
final JSONArray unpairedDevices = new JSONArray(); //new JSONArray对象
mReceiver = new BroadcastReceiver(){
public void onReceive(Context context, Intent intent) {
...
}
} //new广播对象
Activity activity = cordova.getActivity();
activity.registerReceiver(mReceiver, makeFilter());
}
在bluetooths对象中我们创建了一个蓝牙对象和一个广播对象,
在registerReceiver方法创建类型为JSONArray的对象unpairedDevices是为了等会储存device转化后的JSONArray类型的对象。
重点来了!!! 重点就在我们的mReceiver这个对象上,这是一个广播对象,这个对象需要实现onReceive方法,这个方法会在广播被接收到的时候调用。那么什么时候广播会被接受呢?
这个mReceiver接收到广播和 activity.registerReceiver(mReceiver, makeFilter()); 这一句代码有关。
这句代码是注册这个监听到指定广播,那些广播被指定了呢?看上面的 蓝牙显示通信构建方法(IntentFilter)
filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED); //添加连接action
filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);//添加断开action
就是这,添加了两个事件,这两个事件触发后都会被通过这个显示通信的注册的监听捕获到。
处理监听结果
if(intent.getAction().equals(BluetoothDevice.ACTION_ACL_CONNECTED)){
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);//获取设备对象
try {
JSONObject o = deviceToJSON(device,"CONNECTED"); //生成json格式的device信息
unpairedDevices.put(o);
callbackContext.success(o);
} catch (JSONException e) {}
}else if(intent.getAction().equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)){
...
}
这边只展示了监听连接后的处理结果,断开后的处理方式基本一样,不过这有一点要说:我们的监听又不是监听一次,我们需要一直监听下去,所以我找到pltform/android/CordovaLib/src/org/apache/cordova/CallbackContext.java这个Cordova回调的源文件结合cordova-plugin-bluetooth-serial这个插件的BluetoothSerial.java文件中discoverUnpairedDevices方法里面不断返回查找到的设备的信息
o
首先看一下PluginResult这个类简要属性
public class PluginResult {
private final int status; //当前结果的的状态
private final int messageType; 信息类型
private boolean keepCallback = false; 是否继续发送
private String encodedMessage;
...
public PluginResult(Status status, JSONObject message) { //构造函数
this.status = status.ordinal();
this.messageType = MESSAGE_TYPE_JSON;
encodedMessage = message.toString(); //JSONObject转化为Stirng储存
}
public void setKeepCallback(boolean b) {
this.keepCallback = b; //更改是否保持回调
}
}
再看一下 CallbackCintext.java的一个方法
public void sendPluginResult(PluginResult pluginResult) { //传入一个PluginResult类的实例对象
synchronized (this) {
if (finished) { //先判断是不是已经结束了
LOG.w(LOG_TAG, "Attempted to send a second callback for ID: " + callbackId + "\nResult was: " + pluginResult.getMessage());
return;
} else {
finished = !pluginResult.getKeepCallback();
//如果没有结束取出pluginResult.keepCallback作为下一轮的判断
}
}
webView.sendPluginResult(pluginResult, callbackId); //向js发送消息
}
通过上面一些分析android如何保持持续向js发送回调已经明了。
先判断回调是否还存在,如果存在说明需要回调 。new PluginResult类的实例,设置下一回合还需要发送信息,然后发送消息到js的回调,代码如下。
if (callbackContext != null) {
PluginResult res = new PluginResult(PluginResult.Status.OK, o);//将信息写入 同时设置后续还有返回信息
res.setKeepCallback(true);
callbackContext.sendPluginResult(res);
}
基本已经完成了,我相信如果有同学完整的学习完了这一篇,基本cordova插件的封装没有问题了,下面为完整android代码
package bluetooths;
import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.CallbackContext;
import android.app.Activity;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.widget.Toast;
import org.apache.cordova.PluginResult;
import org.json.JSONObject;
/**
* This class echoes a string called from JavaScript.
*/
public class bluetooths extends CordovaPlugin {
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
BroadcastReceiver mReceiver =null;
@Override
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
if(action.equals("registerReceiver")){
String message = args.getString(0);
this.registerReceiver(callbackContext);
return true;
}else if(action.equals("unregisterReceiver")){
this.unregisterReceiver(callbackContext);
}
return true;
}
private void registerReceiver(final CallbackContext callbackContext) throws JSONException {
final JSONArray unpairedDevices = new JSONArray(); //new JSONArray对象
mReceiver = new BroadcastReceiver() { //new广播对象
@Override
public void onReceive(Context context, Intent intent) {
// Toast.makeText(context,"BroadcastReceiver",Toast.LENGTH_SHORT).show();
if(intent.getAction().equals(BluetoothDevice.ACTION_ACL_CONNECTED)){
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);//获取设备对象
try {
JSONObject o = deviceToJSON(device,"CONNECTED"); //生成json格式的device信息
unpairedDevices.put(o);
if (callbackContext != null) {
PluginResult res = new PluginResult(PluginResult.Status.OK, o);//将信息写入 同时设置后续还有返回信息
res.setKeepCallback(true);
callbackContext.sendPluginResult(res);
}
} catch (JSONException e) {}
// Toast.makeText(context,"接受到已连接,消息为:"+device.getName()+"address: "+device.getAddress(),Toast.LENGTH_LONG).show();
}else if(intent.getAction().equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)){
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);//获取设备对象
try {
JSONObject o = deviceToJSON(device,"DISCONNECTED"); //生成json格式的device信息
unpairedDevices.put(o);
if (callbackContext != null) {
PluginResult res = new PluginResult(PluginResult.Status.OK, o);//将信息写入 同时设置后续还有返回信息
res.setKeepCallback(true);
callbackContext.sendPluginResult(res);
}
} catch (JSONException e) {}
// Toast.makeText(context,"接受到断开连接,消息为:"+device.getName()+"address: "+device.getAddress(),Toast.LENGTH_LONG).show();
}
}
};
Activity activity = cordova.getActivity();
activity.registerReceiver(mReceiver, makeFilter());
}
public void unregisterReceiver(final CallbackContext callbackContext){
Activity activity = cordova.getActivity();
activity.unregisterReceiver(mReceiver);
}
/*
@deviceToJSON 将收到的设备对象转化为JSONObject对象方便与js交互数据
@device 设备对象,当监听到设备变化后接受到的设备对象
@connectType如果是连接发出的消息值为 CONNECTED 如果是断开连接发出的消息为 DISCONNECTED
*/
private final JSONObject deviceToJSON(BluetoothDevice device,String connectType) throws JSONException {
JSONObject json = new JSONObject(); //创建JSONObject对象
json.put("name", device.getName()); //设备名字
json.put("address", device.getAddress()); //设备地址
json.put("id", device.getAddress()); //设备唯一编号使用地址表示
json.put("connectType",connectType);
if (device.getBluetoothClass() != null) {
json.put("class", device.getBluetoothClass().getDeviceClass()); //设备类型 主要分别设备是哪一种设备
}
return json;
}
private IntentFilter makeFilter() {
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
return filter;
}
}
网友评论