App 和裝置通過藍芽連線收發資料
阿新 • • 發佈:2019-01-29
一、Android 中進行藍芽開發需要用到的類和執行過程
1,使用BluetoothAdapter.startLeScance來掃描裝置
2,在掃描到裝置的回撥函式中的得到BluetoothDevice 物件,並使用Bluetooth.stopLeScan停止掃描
3,使用BluetoothDevice.connectGatt來獲取到BluetoothGatt物件
4,執行BluetoothGatt.discoverServices,這個方法是非同步操作,在回撥函式onServicesDiscovered中得到status,通過判斷status是否等於BluetoothGatt.GATT_SUCCESS來 判斷查詢Service是否成功
5,如果成功了,則通過BluetoothGatt.getService來獲取BluetoothGattService
6,接著通過BluetoothGattService.getCharacteristic獲取BluetoothGattCharacteristic
7,然後通過BluetoothGattCharacteristic.getDescriptor獲取BluetoothGattDescriptor
二、收發資料
收發資料是通過GATT,GATT是通過藍芽收發資料的一種協議,包含Characteristic,Descriptor,Service 三個屬性值,BLE 分為三個部分Service , Characteristic ,Descriptor 這三個部分都是由UUID做為唯一的識別符號,一個藍芽終端可以包含多個Service ,一個Service 可以包含多個Characteristic ,一個Characteristic 包含一個Value 和多個Descriptor,一個Descriptor包含一個Value三、上程式碼(有些工具類是在其他檔案裡面,我這裡沒有在改了,直接複製上來了)
MainActivice.java:
BluetoothManager bluetoothManager; BluetoothAdapter mBluetoothAdapter;
Intent intentBluetoothLeService = new Intent();
//低功耗藍芽的檔名 public static String BLUETOOTHSERVICE = "com.boe.navapp.remote.BluetoothLeService";
bluetoothManager = (BluetoothManager) MainActivity.this.getSystemService(Context.BLUETOOTH_SERVICE); mBluetoothAdapter = bluetoothManager.getAdapter(); if (mBluetoothAdapter != null && mBluetoothAdapter.isEnabled()) { if (!(CommonUtil.isServiceRunning(MainActivity.this, Constants.BLUETOOTHSERVICE))) { intentBluetoothLeService = new Intent(MainActivity.this, BluetoothLeService.class); startService(intentBluetoothLeService); } }
// 判斷當前服務是否啟動 public static boolean isServiceRunning(Context mContext, String className) { boolean isRunning = false; ActivityManager activityManager = (ActivityManager) mContext .getSystemService(Context.ACTIVITY_SERVICE); List<ActivityManager.RunningServiceInfo> serviceList = activityManager .getRunningServices(70); if (!(serviceList.size() > 0)) { return false; } for (int i = 0; i < serviceList.size(); i++) { if (serviceList.get(i).service.getClassName().equals(className) == true) { isRunning = true; break; } } return isRunning; }
BluetoothLeService.java:(這裡我講下主要程式碼,檔案我會傳上去)
public final static String REMOTE_CONNECT_STATUS = "REMOTE_CONNECT_STATUS";// 當前選擇的遙控器連線狀態
public final static String REMOTE_CONNECTED_DEVICES = "REMOTECONNECTEDDEVICES";// 儲存已經連線的裝置列表
public final static String REMOTE_DEVICE_INFO = "REMOTEDEVICEINFO";
public final static String REMOTE_CURR_ADDR = "REMOTECURRADDR";// 當前連線裝置的地址
public final static String ACTION_GATT_REMOTESEVEN_DISCONNECTED = "com.infisight.hudprojector.bluetooth.le.ACTION_GATT_REMOTESEVEN_DISCONNECTED";
private static final String SP_NAME = "address";
@Override public void onCreate() { super.onCreate(); LogTools.i(TAG, "BluetoothLeService onCreate"); prefs = PreferenceManager.getDefaultSharedPreferences(this); telMgr = (TelephonyManager) getSystemService(TELEPHONY_SERVICE); prefs.edit().putBoolean(Constants.REMOTE_CONNECT_STATUS, false) .commit(); LogTools.i(TAG, "BluetoothLeService before SaveUtils.loadArray"); addressList = SaveUtils.loadArray(this, SP_NAME); //存放連線的地址 GetDeviceInfo(); LogTools.i(TAG, "BluetoothLeService GetDeviceInfo"); }
/** * 獲取裝置資訊 lstNeedConnectDevices */ private void GetDeviceInfo() { String connectedinfo = prefs.getString( Constants.REMOTE_CONNECTED_DEVICES, ""); if (!connectedinfo.equals("")) { Gson gson = new Gson(); try { lstNeedConnectDevices = gson.fromJson(connectedinfo, new TypeToken<List<BleConnectDevice>>() { }.getType()); for (BleConnectDevice bleconnectdevice : lstNeedConnectDevices) { if (!lstNeedConnectAddress.contains(bleconnectdevice .getAddress())) lstNeedConnectAddress .add(bleconnectdevice.getAddress()); } } catch (Exception e) { } } }
public boolean initialize() { // 獲取藍芽管理 if (mBluetoothManager == null) { mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); if (mBluetoothManager == null) { return false; } } // 獲取介面卡 mBluetoothAdapter = mBluetoothManager.getAdapter(); return mBluetoothAdapter != null; }
@Override public int onStartCommand(Intent intent, int flags, int startId) { LogTools.d(TAG, "onStartCommand:flags=" + flags + ",startId=" + startId); if (mBluetoothManager == null) { if (!initialize()) { LogTools.i(TAG, "Unable to initialize Bluetooth"); } } if (isFirstRun) { try { handler.postDelayed(runnable, 2000); } catch (Exception e) { } } else { LogTools.i(TAG, "Constants.REMOTE_DEVICE_INFO:"); String address = getSharedPreferences(Constants.REMOTE_DEVICE_INFO, 0).getString(Constants.REMOTE_CURR_ADDR, "0"); connect(address); } isFirstRun = false; return START_STICKY; }
/** * 啟動連線執行緒 */ Runnable runnable = new Runnable() { @Override public void run() { // 在這裡啟動連線,如果連線失敗,就再重新試圖連線 LogTools.i(TAG, "try connect runnable"); try { mBluetoothAdapter.stopLeScan(mLeScanCallback); mBluetoothAdapter.startLeScan(mLeScanCallback); //掃描低功耗藍芽裝置 } catch (Exception e) { // TODO Auto-generated catch block LogTools.i(TAG, "try connect runnable failed:" + e.getMessage()); e.printStackTrace(); } handler.postDelayed(this, 10000); } };
// 裝置查找回調得到BluetoothDevice物件 LeScanCallback mLeScanCallback = new LeScanCallback() { @Override public void onLeScan(final BluetoothDevice device, int rssi, byte[] scanRecord) { LogTools.d(TAG, "device.getName:" + device.getName() + ",device.address:" + device.getAddress()); if ("KeyCtrl".equals(device.getName())) { close(mGattMap.get(device.getAddress())); //獲取BluetoothGatt 物件 BluetoothGatt mBluetoothGatt = device.connectGatt( BluetoothLeService.this, false, mGattCallback); mGattMap.put(device.getAddress(), mBluetoothGatt); } else if ("infisight_r".equals(device.getName()))//對藍芽OBD功能進行連線 { try { close(mGattMap.get(device.getAddress())); BluetoothGatt mBluetoothGatt = device.connectGatt( BluetoothLeService.this, false, mGattCallback); mGattMap.put(device.getAddress(), mBluetoothGatt); } catch (Exception e) { LogTools.e(TAG, "mBluetoothGatt failed:" + e.getMessage()); } } } };
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() { @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) { // 連線狀態發生變更 String intentAction = Constants.ACTION_GATT_REMOTESEVEN_DISCONNECTED; LogTools.e(TAG, "onConnectionStateChange:name:" + gatt.getDevice().getName() + "Addr:" + gatt.getDevice().getAddress() + ",newState:" + newState); if (newState == BluetoothProfile.STATE_CONNECTED) { hmIsConnected.put(gatt.getDevice().getAddress(), true); SaveUtils.saveArray(BluetoothLeService.this, SP_NAME, addressList); if ("KeyCtrl".equals(gatt.getDevice().getName())) { intentAction = Constants.ACTION_GATT_REMOTE_CONNECTED; } else if ("infisight_r".equals(gatt.getDevice().getName())) { LogTools.e(TAG, "ble ACTION_GATT_REMOTE_CONNECTED"); intentAction = Constants.ACTION_GATT_REMOTESEVEN_CONNECTED; } prefs.edit().putBoolean(Constants.REMOTE_CONNECT_STATUS, true) .commit(); broadcastUpdate(intentAction); LogTools.e(TAG, "ble gatt.discoverServices"); gatt.discoverServices(); SaveDeviceInfo(gatt.getDevice()); } else if (newState == BluetoothProfile.STATE_DISCONNECTED) { LogTools.d(TAG, "BluetoothProfile.STATE_DISCONNECTED"); // 開啟執行緒,不斷時的試圖連線裝置 hmIsConnected.put(gatt.getDevice().getAddress(), false); if ("KeyCtrl".equals(gatt.getDevice().getName())) { intentAction = Constants.ACTION_GATT_REMOTE_DISCONNECTED; } else if ("infisight_r".equals(gatt.getDevice().getName())) { LogTools.d(TAG, "BLE BluetoothProfile.STATE_DISCONNECTED"); intentAction = Constants.ACTION_GATT_REMOTESEVEN_DISCONNECTED; } prefs.edit().putBoolean(Constants.REMOTE_CONNECT_STATUS, false) .commit(); // close(gatt); disconnect(gatt); broadcastUpdate(intentAction); } } //通過status 是否等於BluetoothGatt.GATT_SUCCESS來判斷查詢Service是否成功 @Override public void onServicesDiscovered(BluetoothGatt gatt, int status) { // 發現服務的廣播 LogTools.e(TAG, "onServicesDiscovered:" + gatt.getDevice().getAddress()); if (status == BluetoothGatt.GATT_SUCCESS) { displayGattServices(getSupportedGattServices(gatt), gatt); broadcastUpdate(Constants.ACTION_GATT_SERVICES_DISCOVERED); } } @Override public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { LogTools.d(TAG, "onCharacteristicRead"); if (status == BluetoothGatt.GATT_SUCCESS) { broadcastUpdate(Constants.ACTION_DATA_AVAILABLE, gatt, characteristic); } } @Override public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) { LogTools.d(TAG, "onCharacteristicChanged"); broadcastUpdate(Constants.ACTION_DATA_AVAILABLE, gatt, characteristic); } @Override public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { } };
public static boolean saveArray(Context context, String spname, ArrayList<String> list) { mySharedPreferences = context.getSharedPreferences(spname, Context.MODE_PRIVATE); Editor edit = mySharedPreferences.edit(); edit.putInt("Status_size", list.size()); /*sKey is an array*/ for (int i = 0; i < list.size(); i++) { edit.putString("Status_" + i, list.get(i) + ""); } // LogTools.i("saveArray", list+""); return edit.commit(); }
private void broadcastUpdate(final String action) { final Intent intent = new Intent(action); sendBroadcast(intent); }
/** * 儲存裝置資訊 */ private void SaveDeviceInfo(BluetoothDevice bluetoothdevice) { List<BleConnectDevice> lstConnectDevices = new ArrayList<BleConnectDevice>(); String connectedinfo = prefs.getString( Constants.REMOTE_CONNECTED_DEVICES, ""); Gson gson = new Gson(); if (!connectedinfo.equals("")) { try { lstConnectDevices = gson.fromJson(connectedinfo, new TypeToken<List<BleConnectDevice>>() { }.getType()); } catch (Exception e) { } } if (!lstNeedConnectAddress.contains(bluetoothdevice.getAddress())) { lstNeedConnectAddress.add(bluetoothdevice.getAddress()); BleConnectDevice bledevice = new BleConnectDevice(); //儲存裝置的資訊 bledevice.setAddress(bluetoothdevice.getAddress()); bledevice.setName(bluetoothdevice.getName()); lstConnectDevices.add(bledevice); try { String newaddr = gson.toJson(lstConnectDevices); prefs.edit() .putString(Constants.REMOTE_CONNECTED_DEVICES, newaddr) .commit(); } catch (Exception e) { } } else if ("KeyCtrl".equals(bluetoothdevice.getName())) { mTypeMap.put(bluetoothdevice.getAddress(), "remote"); // }else if (bluetoothdevice.getName().contains("BLE"))//對藍芽OBD功能進行連線 } else if ("infisight_r".equals(bluetoothdevice.getName()))//對藍芽OBD功能進行連線 { mTypeMap.put(bluetoothdevice.getAddress(), "remote"); } }
public boolean connect(final String address) { // 確認已經獲取到介面卡並且地址不為空 if (mBluetoothAdapter == null || address == null || address.equals("0")) { return false; } final BluetoothDevice device = mBluetoothAdapter .getRemoteDevice(address); // 通過裝置地址獲取裝置 if (device == null) { LogTools.e(TAG, "Device not found. Unable to connect."); return false; } // LogTools.i(TAG, "device.connectGatt(this, false, mGattCallback)"); if (mGattMap.keySet().contains(address)) { BluetoothGatt mBluetoothGatt = mGattMap.get(address); if (mBluetoothGatt != null) { // close(mBluetoothGatt); disconnect(mBluetoothGatt); } } BluetoothGatt mBluetoothGatt = device.connectGatt(this, false, mGattCallback); mGattMap.put(address, mBluetoothGatt); addressList.add(address); // mConnectionState = STATE_CONNECTING; return true; }
public void disconnect(BluetoothGatt mBluetoothGatt) { // LogTools.i("LIFECYCLE", "disconnect"); if (mBluetoothAdapter == null || mBluetoothGatt == null) { LogTools.e(TAG, "BluetoothAdapter not initialized disconnect"); return; } mBluetoothGatt.disconnect(); // 斷開連線 }
// 從特徵中分析返回的資料並表示為action後進行廣播 private void broadcastUpdate(final String action, BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) { if ("KeyCtrl".equals(gatt.getDevice().getName())) { final byte[] data = characteristic.getValue(); if (data != null && data.length > 0) { final StringBuilder stringBuilder = new StringBuilder(data.length); for (byte byteChar : data) stringBuilder.append(String.format("%02X ", byteChar)); if (stringBuilder.toString().trim().equals("01")) { // 上 int operFlag = 1; Message msg = sendKeyHandler.obtainMessage(operFlag); msg.what = operFlag; msg.sendToTarget(); LogTools.i(TAG, "keys01"); } else if (stringBuilder.toString().trim().equals("02")) { LogTools.i(TAG, "isPhoneIng:"+ProcessMsgService.isPhoneIng); if(ProcessMsgService.isPhoneIng == true) { try { PhoneUtils.getITelephony(telMgr) .answerRingingCall(); } catch (RemoteException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (Exception e) { LogTools.i(TAG, "error:"+e.getMessage()); CommonUtil.answerRingingCall(this); } return; } // 確定 int operFlag = 5; Message msg = sendKeyHandler.obtainMessage(operFlag); msg.what = operFlag; msg.sendToTarget(); // ProcessBroadcast(Constants.GESTURE_INFO, // Constants.GESTURE_SINGLETAP); LogTools.i(TAG, "keys02"); } else if (stringBuilder.toString().trim().equals("04")) { // 喚醒 MainActivity.svr.controlToWake(true); LogTools.i(TAG, "keys04"); } else if (stringBuilder.toString().trim().equals("08")) { LogTools.i(TAG, "isPhoneIng:"+ProcessMsgService.isPhoneIng); LogTools.i(TAG, "keys08"); if(ProcessMsgService.isPhoneIng == true) { try { PhoneUtils.getITelephony(telMgr).endCall(); } catch (Exception e) { e.printStackTrace(); } return; } // 返回 int operFlag = 0; Message msg = sendKeyHandler.obtainMessage(operFlag); msg.what = operFlag; msg.sendToTarget(); // ProcessBroadcast(Constants.GESTURE_INFO, // Constants.GETSTURE_BACK); LogTools.i(TAG, "keys08"); } else if (stringBuilder.toString().trim().equals("10")) { // 下 int operFlag = 2; Message msg = sendKeyHandler.obtainMessage(operFlag); msg.what = operFlag; msg.sendToTarget(); // ProcessBroadcast(Constants.GESTURE_INFO, // Constants.GESTURE_TODOWN); LogTools.i(TAG, "keys10"); } // intent.putExtra(EXTRA_DATA, new String(data) + "\n" + // stringBuilder.toString()); } // }else if (gatt.getDevice().getName().contains("BLE")) } else if ("infisight_r".equals(gatt.getDevice().getName())) { final byte[] data = characteristic.getValue(); if (data != null && data.length > 0) { final StringBuilder stringBuilder = new StringBuilder(data.length); for (byte byteChar : data) stringBuilder.append(String.format("%02X ", byteChar)); if (stringBuilder.toString().trim().equals("15")) { // 上 int operFlag = 1; Message msg = sendKeyHandler.obtainMessage(operFlag); msg.what = operFlag; msg.sendToTarget(); } else if (stringBuilder.toString().trim().equals("12")) { //如果在通話中,接聽電話 if(ProcessMsgService.isPhoneIng == true) { try { PhoneUtils.getITelephony(telMgr) .answerRingingCall(); } catch (RemoteException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (Exception e) { CommonUtil.answerRingingCall(this); } return; } // 確定 int operFlag = 5; Message msg = sendKeyHandler.obtainMessage(operFlag); msg.what = operFlag; msg.sendToTarget(); // ProcessBroadcast(Constants.GESTURE_INFO, // Constants.GESTURE_SINGLETAP); } else if (stringBuilder.toString().trim().equals("14")) { // 喚醒 MainActivity.svr.controlToWake(true); } else if (stringBuilder.toString().trim().equals("16")) { //如果已經在通話中,直接結束通話電話 LogTools.i(TAG, "key:16"); LogTools.i(TAG, "isPhoneIng:"+ProcessMsgService.isPhoneIng); if(ProcessMsgService.isPhoneIng == true) { try { PhoneUtils.getITelephony(telMgr).endCall(); } catch (Exception e) { e.printStackTrace(); } return; } // 返回 int operFlag = 0; Message msg = sendKeyHandler.obtainMessage(operFlag); msg.what = operFlag; msg.sendToTarget(); // ProcessBroadcast(Constants.GESTURE_INFO, // Constants.GETSTURE_BACK); } else if (stringBuilder.toString().trim().equals("11")) { // 下 int operFlag = 2; Message msg = sendKeyHandler.obtainMessage(operFlag); msg.what = operFlag; msg.sendToTarget(); // ProcessBroadcast(Constants.GESTURE_INFO, // Constants.GESTURE_TODOWN); } else if (stringBuilder.toString().trim().equals("13")) { // 左 int operFlag = 3; Message msg = sendKeyHandler.obtainMessage(operFlag); msg.what = operFlag; msg.sendToTarget(); // ProcessBroadcast(Constants.GESTURE_INFO, // Constants.GESTURE_TOLEFT); } else if (stringBuilder.toString().trim().equals("17")) { // 右 int operFlag = 4; Message msg = sendKeyHandler.obtainMessage(operFlag); msg.what = operFlag; msg.sendToTarget(); // ProcessBroadcast(Constants.GESTURE_INFO, // Constants.GESTURE_TORIGHT); } } } } BluetoothService 下載地址http://download.csdn.net/detail/pigseesunset/9690371