android 藍芽 ble
阿新 • • 發佈:2019-01-23
如果不瞭解androd ble就先學ble整個得大致通訊流程,如果大致通訊流程瞭解了,就利用封裝好得 ble庫,應該對你幫助很大。
android ble連線資料大致也沒幾個步驟,但是對於剛涉水藍芽的小夥伴可能會一臉矇蔽,怎麼弄就是不成功,下邊講解,直接從程式碼中講解
1.封裝號的核心ble通訊層,這個可以解決,完整流程通訊和長時間通訊
package com.reformer.bles; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothGatt; import android.bluetooth.BluetoothGattCallback; import android.bluetooth.BluetoothGattCharacteristic; import android.bluetooth.BluetoothGattDescriptor; import android.bluetooth.BluetoothGattService; import android.bluetooth.BluetoothManager; import android.bluetooth.BluetoothProfile; import android.content.Context; import android.os.Handler; import android.os.Message; import com.reformer.utils.commen.LogUtil; import java.util.UUID; public class Ble extends BluetoothGattCallback { private UUID UUID_SERVICE = UUID.fromString("0000fde6-0000-1000-8000-00805f9b34fb"); private UUID UUID_SERVICE2 = UUID.fromString("0000fdeb-0000-1000-8000-00805f9b34fb");//梯控 private UUID UUID_WRITE = UUID.fromString("0000fde7-0000-1000-8000-00805f9b34fb"); private UUID UUID_INDICATE = UUID.fromString("0000fde8-0000-1000-8000-00805f9b34fb"); private UUID UUID_DESCRIPTOR = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb"); private Context mCtx; private BluetoothAdapter mBtAdapter; private String mAddress; private OnBleListener mListner; private BluetoothGattCharacteristic mWriteChar; private BluetoothGatt mBluetoothGatt; private byte[] tempCmd2 = null; private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case 1: removeMessages(1); break; } } }; public Ble(Context ctx) { final BluetoothManager bluetoothManager = (BluetoothManager) ctx.getSystemService(Context.BLUETOOTH_SERVICE); mBtAdapter = bluetoothManager.getAdapter(); mCtx = ctx; } public boolean scanStart() { LogUtil.d("掃描"); return mBtAdapter != null && mBtAdapter.startLeScan(null, m18LeScanCallback); } public void scanStop() { if (mBtAdapter != null)//華為:G7-Ul20;CHM-00;G7-TL00;藍芽4.0,會出現空指標異常 mBtAdapter.stopLeScan(m18LeScanCallback); } public boolean writeChar(byte[] bytes) { LogUtil.d("資料寫入___" + Utils.bytes2String(bytes)); if (mWriteChar == null || mBluetoothGatt == null || bytes == null) return false; if (bytes.length <= 20) { return writeCharInner(bytes); } else { byte[] tempCmd1 = new byte[20]; for (int i = 0; i < 20; i++) tempCmd1[i] = bytes[i]; tempCmd2 = new byte[bytes.length - 20]; for (int i = 0; i < bytes.length - 20; i++) tempCmd2[i] = bytes[i + 20]; return writeCharInner(tempCmd1); } } private boolean writeCharInner(byte[] bytes) {//寫特徵值 LogUtil.d("資料寫入_分包__" + Utils.bytes2String(bytes)); return mWriteChar.setValue(bytes) && mBluetoothGatt.writeCharacteristic(mWriteChar); } public int connect(String address) { LogUtil.d("連線"); isOk = true; return connectGatt(address); } public void close() { LogUtil.d("關閉"); isOk = false; closeGatt(); } private void closeGatt() { if (mBluetoothGatt != null) { mBluetoothGatt.disconnect(); if (mBluetoothGatt != null) { mBluetoothGatt.close(); if (mBluetoothGatt != null) mBluetoothGatt = null; } } if (mBtAdapter != null) { if (mAddress != null && !mAddress.equals("")) { BluetoothDevice device = mBtAdapter.getRemoteDevice(mAddress); if (device != null) { if (device.getBondState() == BluetoothDevice.BOND_BONDED) { try { Utils.removeBond(BluetoothDevice.class, device);//適配魅族某款手機 } catch (Exception e) { e.printStackTrace(); } } } } } } private int connectGatt(String address) { if (!isOk) return 400; mAddress = address; if (mListner != null) mListner.onConnectStatus(1); closeGatt(); if (mBtAdapter == null) { return 201; } BluetoothDevice device = mBtAdapter.getRemoteDevice(mAddress); if (device == null) { return 202; } mBluetoothGatt = device.connectGatt(mCtx, false, this);//第一步:連線gatt:獲取gatt管道 return 400; } private boolean isOk = false; @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { if (newState == BluetoothProfile.STATE_CONNECTED) { LogUtil.d("連線狀態成功"); gatt.discoverServices();//第二步:gatt連線成功,發現服務 } else { LogUtil.d("連線狀態失敗"); connect(mAddress); } } public void onServicesDiscovered(BluetoothGatt gatt, int status) { if (status == 0) {//第三步:發現服務成功;獲取服務,訂閱特徵值 LogUtil.d("連線服務成功"); BluetoothGattService service = gatt.getService(UUID_SERVICE); mWriteChar = service.getCharacteristic(UUID_WRITE); BluetoothGattCharacteristic mIndicateChar = service.getCharacteristic(UUID_INDICATE); gatt.setCharacteristicNotification(mIndicateChar, true);//寫入特徵值改變時,通知 BluetoothGattDescriptor descriptor = mIndicateChar.getDescriptor(UUID_DESCRIPTOR);//獲取修飾 descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);//修飾配置 gatt.writeDescriptor(descriptor); } else { LogUtil.d("連線服務失敗"); connect(mAddress); } } @Override public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) { super.onDescriptorWrite(gatt, descriptor, status); if (status == 0 && mListner != null) {//第四步:訂閱特徵值成功;傳送資料;交換隨即金鑰 LogUtil.d("訂閱成功"); mListner.onReady(); mListner.onConnectStatus(0); } else { connect(mAddress); } } public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { if (status == 0 && mListner != null) {//第六步:寫入第一包資料後睡眠,傳送第二包資料 if (tempCmd2 != null) { try { Thread.sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } byte[] a = tempCmd2; tempCmd2 = null; writeCharInner(a); LogUtil.d("第二包資料寫入" + Utils.bytes2String(a)); } } else { connect(mAddress); } } public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { LogUtil.d("資料接受成功"); if (mListner != null) mListner.onReceive(characteristic.getValue()); } public void setOnBleListener(OnBleListener bleListener) { mListner = bleListener; } public abstract static class OnBleListener { public void onConnectStatus(int status) { } public void onReady() { } public abstract void onReceive(byte[] value); public abstract void onScanResult(String name, String address, int rssi, byte[] mac); } private BluetoothAdapter.LeScanCallback m18LeScanCallback = new BluetoothAdapter.LeScanCallback() { @Override public void onLeScan(BluetoothDevice device, int rssi, byte[] bytes) { LogUtil.d("device__" + device.getAddress().toString()); if (mListner == null) return; byte[] macTemp = new byte[9]; System.arraycopy(bytes, 18, macTemp, 0, 9); mListner.onScanResult(device.getName(), device.getAddress(), rssi, macTemp); } }; } // private ScanCallback m21ScanCallback = new ScanCallback() { // @Override // public void onScanResult(int callbackType, ScanResult result) { // byte[] scanRecord = result.getScanRecord().getBytes(); // BluetoothDevice device = result.getDevice(); // int rssi = result.getRssi(); //// foundNewDevice(scanRecord, device, rssi); // } // };
2.通過ble傳送資料,這個類,接受資料處理資料
3.也就是外部呼叫的層,方便其他人呼叫,也可以保證隔離開核心程式碼,尤其涉及保險櫃,門禁,等祕密裝置的加密程式碼,可以完整混淆package com.reformer.bles; import android.content.Context; import android.os.Handler; import android.os.Message; import com.reformer.bles.encrypt.KeyBase; import com.reformer.bles.encrypt.KeyKeyfree; import com.reformer.utils.commen.LogUtil; import java.util.ArrayList; public class Presenter { private byte[] mMac; private OnStateListener mOnStateListener; private OnScanListener mOnScanListener; private ArrayList<BleBean> mScans = new ArrayList<BleBean>(); private Ble mBle; public int mTimeOut = 6000;//超時時間 private KeyBase mKey; private String bytes2HexString(byte[] src) {//位元組陣列轉16進位制字串 StringBuffer sb = new StringBuffer(); if (src == null || src.length <= 0) { return null; } for (int i = 0; i < src.length; i++) { int v = src[i] & 0xFF; String hv = Integer.toHexString(v); if (hv.length() < 2) { sb.append(0); } sb.append(hv); } return sb.toString(); } public Presenter(Context ctx) { mKey = new KeyKeyfree(); mBle = new Ble(ctx); mBle.setOnBleListener(new Ble.OnBleListener() { @Override public void onReady() { mBle.writeChar(mKey.get1040()); } @Override public void onReceive(final byte[] receiveDatas) { switch (receiveDatas[3]) { case (byte) 4://取開門命令相關裝置引數 byte[] devRand = new byte[4];//隨機數 System.arraycopy(receiveDatas, 6, devRand, 0, 4);//取出隨機數 mBle.writeChar(mKey.get1050(mMac, devRand)); break; case (byte) 8: case (byte) 9: case (byte) 5://輸出口控制 close(); if (mOnStateListener != null) mOnStateListener.onState(receiveDatas[5]); break; case (byte) 6://查詢外設資訊 System.arraycopy(receiveDatas, 6, mMac, 0, 9);//MAC_LENGTH=9 break; case (byte) 7://設定裝置初始金鑰 (預留,只是配置工具使用) close(); if (mOnStateListener != null) mOnStateListener.onState(300); break; default: close(); break; } } @Override public void onScanResult(String name, String address, int rssi, byte[] mac) { if (mOnScanListener != null) { if (!Utils.containBytesFromDevLst(mac, mScans)) {//是否為掃描記錄列表中的裝置 final BleBean bean = new BleBean(name, address, rssi, mac); mScans.add(bean); mHandler.post(new Runnable() { @Override public void run() { mOnScanListener.onScan(mScans, bean); } }); } } } }); } public void setOnScanListener(OnScanListener obdll) { this.mOnScanListener = obdll; } public void setOnStateListener(OnStateListener osl) { this.mOnStateListener = osl; } public void close() { mBle.close(); mHandler.removeMessages(1); } private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { case 1://超時 removeMessages(1); if (mOnStateListener != null) mOnStateListener.onState(200); close(); break; case 2: scanStop(); break; } } }; public boolean scanStart(int time) { scanStop(); mBle.close(); mScans.clear();//清空掃描記錄列表 mHandler.removeMessages(2); mHandler.sendEmptyMessageDelayed(2, time); return mBle.scanStart(); } public void scanStop() { mBle.scanStop(); } public void init(byte[] devPassword, byte[] phone, int time) { mKey.init(devPassword, phone, time); } public void verifyData(byte[] macs) { mHandler.sendEmptyMessageDelayed(1, mTimeOut); //超時 scanStop(); mMac = macs; //校驗mac String address = Utils.getAddressFromMac(macs, mScans); if (address.equals("") && mOnStateListener != null) { mOnStateListener.onState(102);//mac不再搜尋列表 return; } if (mOnStateListener != null) {//設定密碼的動作,無需再校驗其他資料 mOnStateListener.onState(mBle.connect(address)); } } }
4.提供外部的掃描監聽package com.reformer.bles; import android.content.Context; public class BleKey { private Presenter mPresenter; public BleKey(Context ctx) { mPresenter = new Presenter(ctx); } public void scanStart() { mPresenter.scanStart(5000); } //掃描開始 public void scanStart(int time) { mPresenter.scanStart(time); } public void scanStop() { mPresenter.scanStop(); } /** * 釋放對應樓層許可權 */ public void ctrlTK(String mac, String phone, int floor) { // mPresenter.verifyData(Utils.mac2Bytes(mac, 18), "", 0, Utils.mac2Bytes("0" + phone, 12), 1080, floor); } /** * 釋放呼梯控制權限 0是up,1是down */ public void ctrlHT(String mac, String phone, int up) { // mPresenter.verifyData(Utils.mac2Bytes(mac, 18), "", 0, Utils.mac2Bytes("0" + phone, 12), 1090, up); } public void openDoor(String mac, int time) { byte[] macs = Utils.stringToBytes(mac, 18); // byte[] passwords = Utils.mac2Bytes("3131313131313131" + "D67D67966DA21300", 32); // int mOutputTime = time != 0x0a ? time : 0x0a; //設定輸出口時間 //不等於預設值時,對其賦值 mPresenter.init(null,null,time); mPresenter.verifyData(macs); } /** * 設定密碼 */ public void setPassword(String mac, String password) { // mPresenter.verifyData(Utils.mac2Bytes(mac, 18), password, 0, null, 1070, 0); } /** * 開門完成監聽 */ public void setOnStateListener(OnStateListener ocl) { mPresenter.setOnStateListener(ocl); } /** * 裝置列表實時監聽 */ public void setOnScanListener(OnScanListener oblc) { mPresenter.setOnScanListener(oblc); } }
package com.reformer.bles;
import java.util.ArrayList;
public interface OnScanListener {
public void onScan(ArrayList<BleBean> mScans, BleBean bean);
}
5.提供給外部的狀態監聽
package com.reformer.bles;
public interface OnStateListener {
public void onState(int step);
}
6.涉及部分工具類
package com.reformer.bles;
import android.bluetooth.BluetoothDevice;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
public class Utils {
public static String bytes2String(byte[] msg) {
StringBuffer s = new StringBuffer();
for (int i = 0; i < msg.length; i++) {
s.append(Integer.toHexString(((int) msg[i])));
s.append(",");
}
return s.toString();
}
/**
* 5 * byte陣列轉換成16進位制字串
* 6 * @param src
* 7 * @return
* 8
*/
public static String bytesToHexString(byte[] src) {
StringBuilder stringBuilder = new StringBuilder();
if (src == null || src.length <= 0) {
return null;
}
for (int i = 0; i < src.length; i++) {
int v = src[i] & 0xFF;
String hv = Integer.toHexString(v);
if (hv.length() < 2) {
stringBuilder.append(0);
}
stringBuilder.append(hv);
}
return stringBuilder.toString();
}
public static byte[] stringToBytes(String outStr, int Length) {
if (outStr.length() != Length)
return null;
int len = outStr.length() / 2;
byte[] mac = new byte[len];
String s = null;
for (int i = 0; i < len; i++) {
s = outStr.substring(i * 2, i * 2 + 2);
if (Integer.valueOf(s, 16) > 0x7F) {
mac[i] = (byte) (Integer.valueOf(s, 16) - 0xFF - 1);
} else {
mac[i] = Byte.valueOf(s, 16);
}
}
return mac;
}
public static String bytesToString(byte[] mac) {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < mac.length; i++) {
if (mac[i] < 0)
mac[i] += 256;
sb.append(padLeft(Integer.toHexString(mac[i]), 2));
}
return sb.toString().toUpperCase();
}
public static String padLeft(String str, int len) {
if (str.length() > 2)
str = str.substring(str.length() - 2);
String pad = "0000000000000000";
return len > str.length() && len <= 16 && len >= 0 ? pad.substring(0, len - str.length()) + str : str;
}
public static boolean containBytesFromBytesLst(byte[] bytes, ArrayList<byte[]> arrlt) {
if (arrlt == null)
return true;
for (byte[] mac : arrlt) {
if (Arrays.equals(mac, bytes))
return true;
}
return false;
}
public static boolean containBytesFromDevLst(byte[] bytes, ArrayList<BleBean> arrlt) {
if (arrlt == null)
return false;
for (BleBean dev : arrlt) {
if (Arrays.equals(dev.mac, bytes))
return true;
}
return false;
}
public static String getAddressFromMac(byte[] mac, ArrayList<BleBean> list) {
for (BleBean devContext : list) {
if (Arrays.equals(mac, devContext.mac))
return devContext.address;
}
return "";
}
public static byte[] verifyPhone(String phone) {
if (phone != null) {
//校驗電話號碼
try {
Integer.parseInt(phone);
} catch (Exception e) {
e.printStackTrace();
return null;//手機號碼不是整數
}
StringBuffer sbPhone = new StringBuffer();
if (phone.length() != 12) {
if (phone.length() == 11) {
sbPhone.append("0").append(phone);
} else {
return null;//手機號碼長度不對
}
} else {
sbPhone.append(phone);
}
byte[] bPhone = Utils.stringToBytes(sbPhone.toString(), 12);//校驗手機號長度
if (bPhone == null) {
return null;//105手機號碼長度不對
}
return bPhone;
}
return null;
}
public static boolean createBond(Class btClass, BluetoothDevice btDevice) throws Exception {
Method createBondMethod = btClass.getMethod("createBond");
Boolean returnValue = (Boolean) createBondMethod.invoke(btDevice);
return returnValue.booleanValue();
}
public static boolean removeBond(Class btClass, BluetoothDevice btDevice) throws Exception {
Method removeBondMethod = btClass.getMethod("removeBond");
Boolean returnValue = (Boolean) removeBondMethod.invoke(btDevice);
return returnValue.booleanValue();
}
}
7.通訊的裝置物件
package com.reformer.bles;
public class BleBean {
public String name;
public String address;
public int rssi = 0;
public byte[] mac = new byte[9];
public BleBean() {
}
public BleBean(String name, String address, int rssi, byte[] mac) {
this.name = name;
this.address = address;
this.rssi = rssi;
this.mac = mac;
}
public String getMacStr() {
if (mac == null) {
return null;
}
return Utils.bytesToString(mac);
}
}