首先有Ionic2以及Cordova環(huán)境
如果沒有在命令行執(zhí)行以下命令
npm install -g ionic cordova //全局安裝ionic 和cordova指令
ionic start myApp tabs --v1 創(chuàng)建ionic2項(xiàng)目之前文章講到了 “--v1&&--v2代表ionic不同版本”
npm install -g plugman //全局安裝cordova插件命令
cordova platform add android //添加android平臺(tái) 默認(rèn)創(chuàng)建的項(xiàng)目只包含ios平臺(tái) 其他的平臺(tái)需要使用命令添加
現(xiàn)在基本基本開發(fā)環(huán)境已經(jīng)就緒如果需要具體安裝環(huán)境請(qǐng)?zhí)D(zhuǎn)到Ionic2探索總結(jié)
首先我們查看一下plugman的幫助命令 直接輸入plugman回車就能看到幫助,從幫助中找到下面這一句話
plugman create --name <pluginName> --plugin_id <pluginID> --plugin_version <version> [--path <directory>] [--variable NAME=VALUE]
這句話就是創(chuàng)建插件的命令,下面我給出一個(gè)創(chuàng)建的事例代碼 最好是到剛才創(chuàng)建好的項(xiàng)目跟目錄執(zhí)行
plugman create --name bluetooths --plugin_id bluetooths --plugin_version 0.0.1
/*
[--path <directory>]這個(gè)是可選的枯夜,如果你想創(chuàng)建到別的目錄下的話可以通過這個(gè)可選的指定路徑喜最,如果僅僅想創(chuàng)建到當(dāng)前路徑下的話 可以省略這個(gè)可選的指令
*/
來看一下插件創(chuàng)建完畢后的目錄以及文件
.
├── plugin.xml
├── src
└── www
└── bluetooths.js
這是當(dāng)前的目錄聘裁,然而感覺缺點(diǎn)什么?缺的是各平臺(tái)的代碼铜邮。我們?cè)倩仡^看看plugman這個(gè)命令的幫助 有這樣的命令
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
看到這應(yīng)該就知道了 我們添加平臺(tái)
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, 這個(gè)行指定了這個(gè)插件的id 版本
2, 插件名字
3, 4昔搂,5, 這個(gè)是插件的js部分 src說明js插件的文件的位置 target代表在怎么在全局中引用這個(gè)插件如果在es5中可以直接使用cordova.plugins.bluetooths這個(gè)對(duì)象上的各個(gè)方法,如果在es6以及typescript中使用的時(shí)候得先在代碼的最上邊加入這個(gè)
declare var cordova:any
...
cordova.plugins.bluetooths.function...
6, android平臺(tái)的配置
7, ios平臺(tái)的配置
bluetooths.js
var exec = require('cordova/exec'); //引入cordova內(nèi)部已經(jīng)實(shí)現(xiàn)與原生交互的接口
exports.coolMethod = function(arg0, success, error) {
exec(success, error, "bluetooths", "coolMethod", [arg0]);
};
上面三行代碼實(shí)現(xiàn)了 導(dǎo)出一個(gè)名為coolMethod的方法 這個(gè)方法有三個(gè)參數(shù) arg0為我們調(diào)用這個(gè)方法的時(shí)候參數(shù)树叽,以及回調(diào)方法阿弃。
這個(gè)方法的內(nèi)部是調(diào)用cordova插件的接口前兩個(gè)是回調(diào)的方法,第三個(gè)是指定會(huì)響應(yīng)到那個(gè)插件缀雳,第四個(gè)表面看是調(diào)用指定的方法渡嚣,其實(shí)cordova插件的工作只是把coolMethod這個(gè)值傳了過去(這個(gè)在android代碼的時(shí)候說明) 以及后面的參數(shù)。
我們現(xiàn)在來看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.");
}
}
}
這個(gè)插件類繼承CordovaPlugin類,重寫execute這個(gè)方法
action:觸發(fā)的事件名 String類型
args:之前用戶調(diào)用插件穿的值 JSONArray類型
callbackContext:回調(diào)類 CallbackContext類型
為什么說只是傳了個(gè)方法的值過來,先看action是一個(gè)字符串類型的识椰,在下面我們通過action.equals("coolMethod")來判斷是否相等 這個(gè)類似于 ==绝葡。如果相等就調(diào)用this.coolMethod(message, callbackContext);這個(gè)內(nèi)部方法可以隨意更改。
當(dāng)我們完成這插件后,我們就需要把這個(gè)插件應(yīng)用到我們的項(xiàng)目中(ios因?yàn)闆]接觸過暫時(shí)不講)
cd ../
cordova plugin add ./bluetooths/
添加完成后點(diǎn)開plugins目錄看到有bluetoots這個(gè)文件夾說明插件添加成功腹鹉,調(diào)用在前面已經(jīng)說了藏畅,下面寫一下具體的用法
declare var cordova:any
...
cordova.plugins.bluetooths.coolMethod("message",(res)=>{
alert(res)
},(err)=>{
alert(err)
})
雖然cordvoa提供的插件庫比較豐富,但是我們的業(yè)務(wù)需要監(jiān)聽藍(lán)牙被用戶手動(dòng)在設(shè)置里更改后的信息功咒,因?yàn)閏ordova提供的插件并沒有這樣的監(jiān)聽愉阎,所以踩這個(gè)坑了。
藍(lán)牙監(jiān)聽插件js代碼
js這邊的代碼js這邊的代碼非常簡單
var arg0="message"
exports.registerReceiver = function(success,error){ //注冊(cè)監(jiān)聽的js方法
exec(success,error,"bluetooths","registerReceiver",[arg0]);
}
exports.unregisterReceiver = function(success,error){
exec(success,error,"bluetooths","unregisterReceiver",[arg0]); //取消監(jiān)聽的js方法
}
藍(lán)牙監(jiān)聽插件android代碼
添加權(quán)限
在android代碼里面基本都是純android api里的方法力奋,類榜旦。所以熟悉android的很容易寫出下面的代碼,當(dāng)然我是個(gè)純web前端開發(fā)出生的景殷,android代碼寫的不好請(qǐng)見諒溅呢。
因?yàn)槭且僮魉{(lán)牙所以需要取得權(quán)限在AndroidManifest.xml文件中的manifest標(biāo)簽里添加下面的代碼
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
分發(fā)執(zhí)行函數(shù)
在js代碼中我們傳遞的是五個(gè)參數(shù) 但是經(jīng)過CordovaPlugin這個(gè)類的接受處理后我們?cè)赼dnroid看到的只有三個(gè)參數(shù)。
它把js這端的成功和失敗回調(diào)通過CallbackContext處理,使得我們可以通過類型為CallbackContext傳進(jìn)來的參數(shù)滨彻,調(diào)用js端的方法藕届,同時(shí)傳參給js方法。第三個(gè)參數(shù)也是就“bluetooths”是給CordovaPlugin的標(biāo)志亭饵,調(diào)用的是哪一個(gè)插件,最后兩個(gè)參數(shù)分別為調(diào)用的方法名以及參數(shù)梁厉。對(duì)應(yīng)到execute方法中的參數(shù)為action辜羊,args。
先匹配方法名調(diào)用不同的方法或者做不同的事代碼如下
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
if(action.equals("registerReceiver")){ //如果action==“registerReceiver”注冊(cè)
String message = args.getString(0);
this.registerReceiver(callbackContext); //自定義方法后面講
return true;
}else if(action.equals("unregisterReceiver")){ 如果action==“unregisterReceiver” 取消
this.unregisterReceiver(callbackContext); //自定義方法后面講
}
return true;
}
device對(duì)象轉(zhuǎn)化為JSONObject
這段代碼是從cordova-plugin-bluetooth-serial這個(gè)插件的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;
}
藍(lán)牙顯示通信構(gòu)建方法(IntentFilter)
private IntentFilter makeFilter() {
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED); //添加連接action
filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);//添加斷開action
return filter;
}
注冊(cè)和注銷
先在bluetooths對(duì)象中創(chuàng)建兩個(gè)私有對(duì)象
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
BroadcastReceiver mReceiver =null;
創(chuàng)建私方法 registerReceiver
private void registerReceiver(final CallbackContext callbackContext) throws JSONException {
final JSONArray unpairedDevices = new JSONArray(); //new JSONArray對(duì)象
mReceiver = new BroadcastReceiver(){
public void onReceive(Context context, Intent intent) {
...
}
} //new廣播對(duì)象
Activity activity = cordova.getActivity();
activity.registerReceiver(mReceiver, makeFilter());
}
在bluetooths對(duì)象中我們創(chuàng)建了一個(gè)藍(lán)牙對(duì)象和一個(gè)廣播對(duì)象词顾,
在registerReceiver方法創(chuàng)建類型為JSONArray的對(duì)象unpairedDevices是為了等會(huì)儲(chǔ)存device轉(zhuǎn)化后的JSONArray類型的對(duì)象八秃。
重點(diǎn)來了!H忭铩昔驱! 重點(diǎn)就在我們的mReceiver這個(gè)對(duì)象上,這是一個(gè)廣播對(duì)象上忍,這個(gè)對(duì)象需要實(shí)現(xiàn)onReceive方法骤肛,這個(gè)方法會(huì)在廣播被接收到的時(shí)候調(diào)用。那么什么時(shí)候廣播會(huì)被接受呢窍蓝?
這個(gè)mReceiver接收到廣播和 activity.registerReceiver(mReceiver, makeFilter()); 這一句代碼有關(guān)腋颠。
這句代碼是注冊(cè)這個(gè)監(jiān)聽到指定廣播,那些廣播被指定了呢吓笙?看上面的 藍(lán)牙顯示通信構(gòu)建方法(IntentFilter)
filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED); //添加連接action
filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);//添加斷開action
就是這淑玫,添加了兩個(gè)事件,這兩個(gè)事件觸發(fā)后都會(huì)被通過這個(gè)顯示通信的注冊(cè)的監(jiān)聽捕獲到。
處理監(jiān)聽結(jié)果
if(intent.getAction().equals(BluetoothDevice.ACTION_ACL_CONNECTED)){
BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);//獲取設(shè)備對(duì)象
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)){
...
}
這邊只展示了監(jiān)聽連接后的處理結(jié)果絮蒿,斷開后的處理方式基本一樣尊搬,不過這有一點(diǎn)要說:我們的監(jiān)聽又不是監(jiān)聽一次,我們需要一直監(jiān)聽下去,所以我找到pltform/android/CordovaLib/src/org/apache/cordova/CallbackContext.java這個(gè)Cordova回調(diào)的源文件結(jié)合cordova-plugin-bluetooth-serial這個(gè)插件的BluetoothSerial.java文件中discoverUnpairedDevices方法里面不斷返回查找到的設(shè)備的信息
o
首先看一下PluginResult這個(gè)類簡要屬性
public class PluginResult {
private final int status; //當(dāng)前結(jié)果的的狀態(tài)
private final int messageType; 信息類型
private boolean keepCallback = false; 是否繼續(xù)發(fā)送
private String encodedMessage;
...
public PluginResult(Status status, JSONObject message) { //構(gòu)造函數(shù)
this.status = status.ordinal();
this.messageType = MESSAGE_TYPE_JSON;
encodedMessage = message.toString(); //JSONObject轉(zhuǎn)化為Stirng儲(chǔ)存
}
public void setKeepCallback(boolean b) {
this.keepCallback = b; //更改是否保持回調(diào)
}
}
再看一下 CallbackCintext.java的一個(gè)方法
public void sendPluginResult(PluginResult pluginResult) { //傳入一個(gè)PluginResult類的實(shí)例對(duì)象
synchronized (this) {
if (finished) { //先判斷是不是已經(jīng)結(jié)束了
LOG.w(LOG_TAG, "Attempted to send a second callback for ID: " + callbackId + "\nResult was: " + pluginResult.getMessage());
return;
} else {
finished = !pluginResult.getKeepCallback();
//如果沒有結(jié)束取出pluginResult.keepCallback作為下一輪的判斷
}
}
webView.sendPluginResult(pluginResult, callbackId); //向js發(fā)送消息
}
通過上面一些分析android如何保持持續(xù)向js發(fā)送回調(diào)已經(jīng)明了土涝。
先判斷回調(diào)是否還存在佛寿,如果存在說明需要回調(diào) 。new PluginResult類的實(shí)例,設(shè)置下一回合還需要發(fā)送信息回铛,然后發(fā)送消息到j(luò)s的回調(diào)狗准,代碼如下。
if (callbackContext != null) {
PluginResult res = new PluginResult(PluginResult.Status.OK, o);//將信息寫入 同時(shí)設(shè)置后續(xù)還有返回信息
res.setKeepCallback(true);
callbackContext.sendPluginResult(res);
}
基本已經(jīng)完成了茵肃,我相信如果有同學(xué)完整的學(xué)習(xí)完了這一篇腔长,基本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對(duì)象
mReceiver = new BroadcastReceiver() { //new廣播對(duì)象
@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);//獲取設(shè)備對(duì)象
try {
JSONObject o = deviceToJSON(device,"CONNECTED"); //生成json格式的device信息
unpairedDevices.put(o);
if (callbackContext != null) {
PluginResult res = new PluginResult(PluginResult.Status.OK, o);//將信息寫入 同時(shí)設(shè)置后續(xù)還有返回信息
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);//獲取設(shè)備對(duì)象
try {
JSONObject o = deviceToJSON(device,"DISCONNECTED"); //生成json格式的device信息
unpairedDevices.put(o);
if (callbackContext != null) {
PluginResult res = new PluginResult(PluginResult.Status.OK, o);//將信息寫入 同時(shí)設(shè)置后續(xù)還有返回信息
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 將收到的設(shè)備對(duì)象轉(zhuǎn)化為JSONObject對(duì)象方便與js交互數(shù)據(jù)
@device 設(shè)備對(duì)象,當(dāng)監(jiān)聽到設(shè)備變化后接受到的設(shè)備對(duì)象
@connectType如果是連接發(fā)出的消息值為 CONNECTED 如果是斷開連接發(fā)出的消息為 DISCONNECTED
*/
private final JSONObject deviceToJSON(BluetoothDevice device,String connectType) throws JSONException {
JSONObject json = new JSONObject(); //創(chuàng)建JSONObject對(duì)象
json.put("name", device.getName()); //設(shè)備名字
json.put("address", device.getAddress()); //設(shè)備地址
json.put("id", device.getAddress()); //設(shè)備唯一編號(hào)使用地址表示
json.put("connectType",connectType);
if (device.getBluetoothClass() != null) {
json.put("class", device.getBluetoothClass().getDeviceClass()); //設(shè)備類型 主要分別設(shè)備是哪一種設(shè)備
}
return json;
}
private IntentFilter makeFilter() {
IntentFilter filter = new IntentFilter();
filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED);
filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
return filter;
}
}