##藍牙問題總結
####本文檔會隨時更新。如有轉載還請注意保留原鏈接。
> 開發公司票務驗票APP要求連接藍牙打印小票。但是HTML5+并沒有現成的API只能自己動手了。
本人不是native開發者,所以問題描述和解決方案可能并不是最優或準確的。如果問題描述有誤或您有更好的解決方法。歡迎指正,共同學習!由于本人對ios不懂。且ios藍牙資料缺少。所以APP藍牙打印僅安卓版本的支持。iOS支持請等待后續更新
聯系本人:wzhec@foxmail.com
關于service端的APP設計請參考我的另外一本筆記:[聯合票務系統對接文檔說明](http://www.hmoore.net/iwzh/lianhepiaowu/168108)
重點來了,藍牙問題說明:
涉及到,藍牙搜索,藍牙配對,藍牙自動開啟。藍牙傳輸打印功能。
github項目地址:[https://github.com/iwzh/lianhepiaowuVerifyCode/blob/master/js/pr.js](https://github.com/iwzh/lianhepiaowuVerifyCode/blob/master/js/pr.js)
代碼已基本解決上述問題。
貼代碼:(代碼可能已更新,但大改動應該沒有)
~~~
(function($, BT) {
/**
* 獲取應用本地配置
**/
function getSettings() {
var settingsText = localStorage.getItem('$settings') || "{}";
return JSON.parse(settingsText);
}
var device = null,
BluetoothAdapter = null,
BAdapter = null,
UUID = null,
uuid = null,
main = null,
bluetoothSocket = null,
BluetoothDevice = null,
IntentFilter=null;
var agintry=0;
var defaultpage="tab-webview-subpage-bluetooth.html";
//定義 Bluetooth 類
var Bluetooth = $.Bluetooth = $.Class.extend({
/**
* 構造函數
* */
init: function(options) {
var self = this;
options = options || {};
self.options = options;
self.device = self.options.device || device;
if(self.device==null){
main = self.options.main = self.options.main || plus.android.runtimeMainActivity();
BluetoothAdapter = self.options.BluetoothAdapter = self.options.BluetoothAdapter || BluetoothAdapter || plus.android.importClass("android.bluetooth.BluetoothAdapter");
BAdapter=self.options.BAdapter = self.options.BAdapter || BAdapter || BluetoothAdapter.getDefaultAdapter();
//self.device = self.options.device || device || self.BAdapter.getRemoteDevice(self.mac_address);
//plus.android.importClass(self.device);
}
if(self.openBluetooth(self.BAdapter)){
/* //搜索藍牙時候用到
if(self.IntentFilter==null){
self.IntentFilter = self.options.BluetoothAdapter || plus.android.importClass('android.content.IntentFilter') || self.IntentFilter;
}*/
}else{
$.toast('開啟藍牙失敗,無法使用藍牙打印機;請手動設置權限后,重新打開');
}
},
/**
* 獲取本地存儲的藍牙列表
**/
getBlueList : function() {
var stateText = localStorage.getItem('$bluetoothlist') || "{}";
return JSON.parse(stateText);
},
/**
* 設置本地存儲的藍牙列表
**/
setBlueList:function(state) {
state = state || {};
localStorage.setItem('$bluetoothlist', JSON.stringify(state));
},
/*
* 清除本地存儲的藍牙列表
*/
clearBlueList:function(){
this.setBlueList({});
},
/**
* 獲取本地存儲的搜索藍牙列表
**/
setBTSearchList:function(state) {
var stateText = localStorage.getItem('$btseachlist') || '{"on":"[]","un":"[]"}';
return JSON.parse(stateText);
},
/**
* 設置本地存儲的搜索藍牙列表
**/
setBTSearchList:function(state) {
state = state || '{"on":"[]","un":"[]"}';
localStorage.setItem('$btseachlist', JSON.stringify(state));
},
/*
* 獲取保存的藍牙連接地址
*/
getMacAddress:function(){
var settings=getSettings();
settings.bluestate=1;
if(settings.bluestate){
var address = localStorage.getItem('$bluetoothMacAddress') || "";
return address;
}else{
console.log('未開啟藍牙打印設置');
return false;
}
},
/*
* 設置保存藍牙連接的地址
*/
setMacAddress:function(address){
address=address||'';
localStorage.setItem('$bluetoothMacAddress', address);
},
/*
* 測試打印
*/
testprint:function(mac_address,teststr,device,bluetoothSocket){
teststr=teststr||"這是測試打印的。內容可以忽略 ";
this.print(mac_address,teststr,device,bluetoothSocket);
},
/**
* 打印
*/
print: function(mac_address, printstring, device, bluetoothSocket) {
var self = this;
if(!mac_address) {
var mac_address=self.getMacAddress();
if(!mac_address){
$.toast('請選擇藍牙打印機');
return;
}
}
self.initParam({"mac_address":mac_address},1);
bluetoothSocket=self.options.bluetoothSocket;
self.connectedBT(bluetoothSocket,mac_address);//檢測藍牙是否連接;
if(bluetoothSocket.isConnected()) {
var outputStream = bluetoothSocket.getOutputStream();
plus.android.importClass(outputStream);
var bytes = plus.android.invoke(printstring, 'getBytes', 'gbk');
var clearFormat = [0x1b, 0x40]; //復位打印機
outputStream.write(clearFormat);
/*start *** 文字加粗*/
/*
outputStream.write([0x1b,0x45,1]);
var title=plus.android.invoke("【聯合票務】", 'getBytes', 'gbk');
outputStream.write(title);
outputStream.write([0x1b,0x45,0]);
*/
/*end *** 文字加粗*/
outputStream.write(bytes);
outputStream.write([0x1b,0x64,4]);//多走紙n行
/*outputStream.write([0x1b,0x2a,1,6,0,10,0,122,222,22,54]);//位圖模式*/
outputStream.flush();
/*//device = null //清空連接設備 如果持續驗票的情況下,不能每次都初始化設備。只需要關閉藍牙與手機APP的socket即可。無需情況設備*/
/*bluetoothSocket.close(); //必須關閉藍牙連接否則意外斷開的話打印錯誤*/
}else{
$.toast("藍牙未連接,無法打印");
}
},
/*
* 成功的提示
*/
confirm:function(mac_address){
var self=this;
var confirm_title='測試打印';
var confirm_tips='藍牙已連接成功,是否測試打印?';
if($.os.plus) {
mac_address=mac_address||self.options.mac_address||self.getMacAddress();
var btnArray = ['是', '否'];
$.confirm(confirm_tips, confirm_title, btnArray, function(e) {
if(e.index == 0) {
self.print(mac_address)
}else{
console.log('取消');
}
});
} else {
alert("成功");
}
},
/*
* 開啟藍牙
*/
openBluetooth:function(BAdapter){
var self=this;
BAdapter=self.options.BAdapter || BAdapter;
if(!BAdapter.isEnabled()) {
console.log('檢測到未打開藍牙,嘗試打開中....');
$.toast('檢測到未打開藍牙,嘗試打開中....');
var status = BAdapter.enable();
if(status){
console.log('已為您開啟藍牙...');
$.toast('已成功為您開啟藍牙');
}else{
console.log('開啟藍牙失敗,請手動開啟藍牙');
$.toast('開啟藍牙失敗,請手動開啟藍牙');
return false;
}
}
return true;
},
initParam:function(options,checkSocket,checkDevice){
var self = this;
checkSocket=checkSocket||0;
checkDevice=checkDevice||0;
device = self.options.device = options.device||self.options.device ||device ;
if(self.device==null){
main = self.options.main = options.main||self.options.main||main||plus.android.runtimeMainActivity();
BluetoothAdapter =self.options.BluetoothAdapter =options.BluetoothAdapter||self.options.BluetoothAdapter ||BluetoothAdapter||plus.android.importClass("android.bluetooth.BluetoothAdapter");
BAdapter=self.options.BAdapter=options.BAdapter||self.options.BAdapter || BAdapter || BluetoothAdapter.getDefaultAdapter();
}
if(options.mac_address){
device =self.options.device= BAdapter.getRemoteDevice(options.mac_address);
plus.android.importClass(device);
}
if(checkDevice){
//藍牙信息
BluetoothDevice=self.options.BluetoothDevice =options.BluetoothDevice||self.options.BluetoothDevice||plus.android.importClass("android.bluetooth.BluetoothDevice");
}
//藍牙連接或者uuid為空時,需要重新導入藍牙socket
if(checkSocket){
/*檢查Socket時,要么導入Socket要么給Socket初始值*/
UUID=self.options.UUID =options.UUID||self.options.UUID||plus.android.importClass("java.util.UUID");
uuid=self.options.uuid =options.uuid||self.options.uuid||UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
bluetoothSocket=self.options.bluetoothSocket = options.bluetoothSocket||device.createInsecureRfcommSocketToServiceRecord(uuid);
plus.android.importClass(bluetoothSocket);
}
return self;
},
/*
* 連接藍牙
*/
connectedBT: function(bluetoothSocket,mac_address) {
var self = this;
if(!bluetoothSocket.isConnected()) {
if(agintry>1){
console.log('重試兩次了,不再嘗試');
bluetoothSocket=self.options.bluetoothSocket=null;
UUID=self.options.UUID=null;
uuid=self.options.uuid=null;
return false;
}
try {
bluetoothSocket.connect();
} catch(e) {
console.log('Bluetooth Connect Error!'+e);
bluetoothSocket.close();
self.initParam({"mac_address":mac_address},1,1);
bluetoothSocket =self.options.bluetoothSocket;
console.log('設備連接出錯了,重新連接');
agintry++;
bluetoothSocket.connect();
}
if(!bluetoothSocket.isConnected()) {
$.toast('設備未連接,重新連接失敗,請確保設備開啟。');
return false;
} else {
console.log('設備已連接。');
}
console.log('設備已連接 bluetoothSocket.isConnected()=' + bluetoothSocket.isConnected());
}
agintry=0;
return true;
},
contactBT:function(mac_address){
var self=this;
self.initParam({"mac_address":mac_address},1,1);
var bdevice = new BluetoothDevice();
if(device.getBondState()==bdevice.BOND_NONE){
//地址一樣,執行配對
if(mac_address == device.getAddress()){
console.log('正在執行配對');
if(device.createBond()) {
console.log("配對成功");
//第一次配對成功將配對成功的數據放入已配對藍牙列表
var _bluelist=self.getBlueList();
var item={"id":d.getAddress(),"name":d.getName()};
_bluelist.push(item);
self.setBlueList(_bluelist);
}
}
}
self.connectedBT(self.options.bluetoothSocket,mac_address);
},
/**
* 掃描已配對藍牙
*/
scan: function() {
var self = this;
BAdapter=self.options.BAdapter || self.BAdapter || BAdapter;
var lists = BAdapter.getBondedDevices(); //獲取配對的設備列表
plus.android.importClass(lists);
var iterator = lists.iterator();
plus.android.importClass(iterator);
var bluetoothList=new Array();
while(iterator.hasNext()) {
var d = iterator.next();
plus.android.importClass(d);
var item={"id":d.getAddress(),"name":d.getName()};
bluetoothList.push(item);
}
self.setBlueList(bluetoothList);
return bluetoothList;
},
/*
* 取消藍牙配對
*/
removeBlueDevices:function(mac_address,mac_name){
var self=this;
if(!mac_address) {
$.toast('請選擇成功配對的藍牙設備');
return;
}
self.initParam({"mac_address":mac_address},1,1);
device=self.options.device;
BluetoothDevice=self.options.BluetoothDevice;
var bdevice = new BluetoothDevice();
if(device.getBondState()==bdevice.BOND_BONDED){
//地址一樣,執行刪除配對
if(mac_address == device.getAddress()){
var btnArray = ['是', '否'];
$.confirm('是否刪除已配對的設備:' + mac_name + '?', '刪除設備', btnArray, function(e) {
if(e.index == 0) {
if(device.removeBond()) {
var _bluelist=self.getBlueList();
var item={"id":mac_address,"name":mac_name};
var rebluelist=_bluelist.map(function(listiteam){
if(JSON.stringify(item)!=JSON.stringify(listiteam)){
return listiteam;
}
});
self.setBlueList(rebluelist);
console.log("刪除配對藍牙設備:\n" + mac_name + " 成功");
return true;
}
} else {
console.log('取消刪除設備操作');
return false;
}
});
}
}else{
console.log("未配對設備,不能刪除");
return false;
}
},
/*
* 搜索藍牙設備,并創建處理HTML藍牙列表
*/
seachBT:function(address){
var self=this;
if(address){
self.initParam({"mac_address":address},0,1);
}else{
self.initParam({},0,1);
}
//檢查藍牙是否開啟
self.openBluetooth(BAdapter);
IntentFilter=self.options.IntentFilter=self.options.IntentFilter||IntentFilter || plus.android.importClass('android.content.IntentFilter');
BluetoothDevice=self.options.BluetoothDevice=self.options.BluetoothDevice||BluetoothDevice || plus.android.importClass("android.bluetooth.BluetoothDevice");
var filter = new IntentFilter();
var bdevice = new BluetoothDevice();
var on = new Array(),un = new Array(),onstr=null,unstr=null;
var BTSeatchList=new Object();
BAdapter.startDiscovery(); //開啟搜索
var receiver;
receiver = plus.android.implements('io.dcloud.android.content.BroadcastReceiver', {
onReceive: function(context, intent) {
//實現onReceiver回調函數
plus.android.importClass(intent);
//通過intent實例引入intent類,方便以后的‘.’操作
//獲取action
console.log(intent.getAction());
if(intent.getAction() == "android.bluetooth.adapter.action.DISCOVERY_FINISHED") {
console.log("搜索結束,本地保存未配對設備和已配對設備");
BTSeatchList.on=on;
BTSeatchList.un=un;
main.unregisterReceiver(receiver); //取消監聽
//事件數據
var eventData = {
sender: self,
btsearchlist: BTSeatchList
};
//觸發聲明的DOM的自定義事件(暫定 done 為事件名,可以考慮更有針對的事件名 )
var firepage = plus.webview.getWebviewById(defaultpage);
$.fire(firepage,'done', eventData);
} else {
BleDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
//判斷是否配對
if(BleDevice.getBondState() == bdevice.BOND_NONE) {
//判斷防止重復添加
if(BleDevice.getName()!=unstr){
unstr=BleDevice.getName();
console.log("未配對藍牙設備:" + unstr + ' ' + BleDevice.getAddress());
var unitem={"id":BleDevice.getAddress(),"name":unstr};
un.push(unitem);
}
} else {
//判斷防止重復添加
if(onstr!=BleDevice.getName()){
onstr=BleDevice.getName();
console.log("已配對藍牙設備:" + onstr + ' ' + BleDevice.getAddress());
var onitem={"id":BleDevice.getAddress(),"name":onstr};
on.push(onitem);
}
}
}
}
});
filter.addAction(bdevice.ACTION_FOUND);//搜索設備
filter.addAction(BAdapter.ACTION_DISCOVERY_STARTED);
filter.addAction(BAdapter.ACTION_DISCOVERY_FINISHED);
filter.addAction(BAdapter.ACTION_STATE_CHANGED);
filter.addAction(BAdapter.ACTION_STATE_CHANGED); //監聽藍牙開關
main.registerReceiver(receiver, filter); //注冊監聽
},
/*
* 數組去重
*/
arrayUnique:function(a) {
var seen = {};
return a.filter(function(item) {
item=JSON.stringify(item);
return seen.hasOwnProperty(item) ? false : (seen[item] = true);
});
}
});
//添加 Bluetooth 插件
$.fn.bluetooth = function(options) {
var self = this[0];
var bluetooth = null;
bluetooth = new Bluetooth(options);
return bluetooth;
}
})(mui, window);
~~~
- 說明
- PHP
- 數組操作
- file操作的常用方法
- PHP字符串輸出之Heredoc說明
- require(_once)和include(_once)的理解
- file_get_contents和curl
- PHP的json問題
- PHP提高效率的幾點
- PHP/異步任務隊列處理
- HTTP_AUTHORIZATION
- php中 intval和string的一些轉換問題
- 變量在 PHP7 內部的實現
- 關于exit和die
- php獲取微秒
- php高性能日志擴展seaslog的使用
- Curl使用說明
- echo的數據自動增加換行或其他
- php-memcache
- 根據18位數校驗前17位身份證號是否正確
- 將一個老項目升級到php7
- ord獲取ASCII碼
- 框架
- thinkphp5
- THINKPHP5常見問題
- Laravel5學習筆記
- homestead總結
- easywechat學習筆記
- wechat公眾號
- 獲取用戶信息的實現方式
- 前端
- HTML
- CSS
- 偽類和偽元素的區別
- Bootstrap使用
- Javascript
- Javascript梳理總結
- 巧用history.pushState無刷新改變頁面url
- jquery日期時間選擇器組件datepicker的使用說明
- 基本操作
- 比較
- 后端/Nodejs
- 多nodejs版本管理
- 小功能
- URLAPI
- 二維碼轉換
- 地圖URL直接實現導航
- 字體生成
- 網址長短互轉
- 百度短網址
- Fiddle模擬測試百度短網址api
- Composer備忘錄
- composer安裝配置
- Composer 錯誤集錦
- Composer使用自己的庫
- GIT簡單操作命令
- Git記住密碼
- git 顯示錯誤詳情和請求信息
- 工具軟件
- PHPStorm
- VI簡單操作命令
- ATOM
- browser提示shockwave false加載失敗
- Cmder使用說明
- Windows軟件總結
- 瀏覽器插件
- 支付
- 微信支付
- 支付寶
- 銀聯支付
- Ping++
- Beecloud支付
- Map
- 百度地圖BaiduMap
- RESTfulAPI設計實踐
- HTTP
- GET/POST 的請求大小
- 常見狀態碼說明
- Ubuntu
- 命令行中的>>和>的區別
- 筆記
- 正則
- Hybrid的使用記錄
- H5+和mui
- HTML5+和mui使用
- APP開發過程中藍牙問題總結
- Cordova的使用
- 服務器
- URL重寫
- 目錄限制訪問
- 軟鏈接和硬鏈接
- 票務系統對接資料和總結
- qunar去哪兒門票對接
- qunar對接case介紹及錯誤代碼
- tuniu門票對接總結
- 途牛小結
- 聯合票務對接途牛文檔說明
- 途牛簽名流程
- meituan美團
- 大眾點評
- 聯合票務
- 安卓使用技巧
- 安卓清理電池信息
- 安卓6.0使用時發現的問題
- 數據庫
- 數據庫mysql
- 分頁數據優化
- 手機號用不到索引的問題分析
- mysql配置的localhost和127.0.0.1的區別
- mysql5.5升級mysql5.7
- 數據庫MongoDB
- Redis使用說明
- phpredis使用說明
- 環境變量
- Twig使用
- 經典的文章
- 用超人的故事講解 IoC(控制反轉) 和 DI(依賴注入)