上節我們已經可以連接上藍牙設備了。
本節我們就要獲取手環的電池電量和計步啦。
在介紹這個之前我們需要先了解下什么是 服務 什么是 UUID
我們記得上節中我們item監聽事件的回調的返回值是BluetoothGatt 類型的,還記得么?嘿嘿。
返回的bluetoothgatt中包含一個或多個BluetoothGattService(服務)
每個service包含一個或多個characteristic(特征值)
每個特征值包含一個value 和多個 descriptor(注意看啊!是一個value)
這些對於本項目來說某個特征值的value中就包含了記錄的步數。(很遺憾我折騰了很久也沒找到電池電量,不得不舍棄展示電池電量的想法)
先熟悉下下面的圖片吧~~
(百度腦圖)
在了解下UUID 上圖中每個藍牙設備都有自己的MAC 地址有了這個地址 我們就可以連接這個設備。
連接上設備后會的到設備的服務就是上圖中的Gattservice 這每個service都有個固定的UUID以標識區分
每個service又包含多個的characteristic 這些特征值也有唯一的UUID 這些UUID就是我們編程需要的,
獲取到characteristic對應的UUID就意味着獲取到該characteristic提供的功能或者數據。
特征值對應的UUID所實現的功能是由硬件工程師決定的。
因為小米手環的硬件工程師我們沒有聯系方式,況且人家也肯定不會告訴我所有功能對應的UUID
因此我們只好自己對應數值來找了。
簡單說 上圖的每個characteristic 都對應一個UUID 這些UUID對應了設備的不同功能
手環為例:
控制手環震動的 UUID :00002a06-0000-1000-8000-00805f9b34fb 手機向該 UUID 寫入 0x01 或者 0x02 時手環都會震動,01強度弱於 02
計步的 UUID :0000ff06-0000-1000-8000-00805f9b34fb 讀取該UUID下的value數組 第0 個數據就是 步數
首先我們來獲取下計步數吧
經過一番折騰我終於找到了小米手環對應步數的 UUID :0000ff06-0000-1000-8000-00805f9b34fb
gattcallback 回調方法中找到 onServicesDiscovered
添加如下代碼:
@Override public void onServicesDiscovered(BluetoothGatt gatt, int status) { super.onServicesDiscovered(gatt, status); //尋找到服務時 if (status == bluetoothGatt.GATT_SUCCESS) { final List<BluetoothGattService> services = bluetoothGatt.getServices(); runOnUiThread(new Runnable() { @Override public void run() { //List<String> serlist = new ArrayList<>(); for (final BluetoothGattService bluetoothGattService : services) { bluetoothGattServices = bluetoothGattService; Log.i(TAG, "onServicesDiscovered: " + bluetoothGattService.getUuid()); List<BluetoothGattCharacteristic> charc = bluetoothGattService.getCharacteristics(); for (BluetoothGattCharacteristic charac : charc) { Log.i(TAG, "run: " + charac.getUuid()); //找到透傳特征值 // 00002a06-0000-1000-8000-00805f9b34fb 小米手環震動特征值 0x01震動 0x02強震 if (charac.getUuid().toString().equals("00002a06-0000-1000-8000-00805f9b34fb")) { //設備 震動特征值 characteristic_zd = charac; } else if (charac.getUuid().toString().equals("0000ff06-0000-1000-8000-00805f9b34fb")) { //設備 步數 characteristic_jb = charac; bluetoothGatt.readCharacteristic(characteristic_jb); Log.i(TAG, "run: 正在嘗試讀取步數"); } else if (charac.getUuid().toString().equals("")) { //設備 電量特征值 } } serviceslist.add(bluetoothGattService.getUuid().toString()); } } }); } }
注釋寫的很清楚
我用 characteristic_zd 記住震動的特征值
用 characteristic_jb 記住計步的
記住計步特征值后使用 bluetoothGatt 提供的read方法並傳人相應特征值。該方法為異步方法他的返回內容會由
回調中的 onCharacteristicRead 方法接收到
找到該方法添加如下代碼:
if (status == bluetoothGatt.GATT_SUCCESS) { final int sum = characteristic.getValue()[0]; runOnUiThread(new Runnable() { @Override public void run() { jibu.setText("走了" + sum + "步"); } }); Log.e(TAG, "onCharacteristicRead: " + characteristic.getValue()[0]); }
方法的參數中status表示狀態,為 bluetoothGatt.GATT_SUCCESS 時表示成功獲取返回
因為在異步方法中,想要操作UI線程,於是就用了 runOnUiThread 方法。顯示出來步數即可。
好了下面貼出完整主Activity代碼:
MainActivity.java:

package com.wbnq.shouhuan; 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.content.Intent; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.ListView; import android.widget.TextView; import android.widget.Toast; import java.util.ArrayList; import java.util.List; import java.util.UUID; public class MainActivity extends AppCompatActivity implements View.OnClickListener { private Button saomiao, duanzhen, changzhen, buting, tingxia; private TextView jibu, dianliang, lianjiezhuangtai; private ListView list; public static String TAG = "shouhuan-MainActivity"; BluetoothAdapter bluetoothAdapter; BluetoothGatt bluetoothGatt; List<BluetoothDevice> deviceList = new ArrayList<>(); List<String> serviceslist = new ArrayList<String>(); BluetoothDevice bluetoothDevice; BluetoothGattService bluetoothGattServices; BluetoothGattCharacteristic characteristic_zd, characteristic_jb; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); //藍牙管理,這是系統服務可以通過getSystemService(BLUETOOTH_SERVICE)的方法獲取實例 BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE); //通過藍牙管理實例獲取適配器,然后通過掃描方法(scan)獲取設備(device) bluetoothAdapter = bluetoothManager.getAdapter(); } private void initView() { saomiao = (Button) findViewById(R.id.saomiao); duanzhen = (Button) findViewById(R.id.zhendong); changzhen = (Button) findViewById(R.id.changzhen); buting = (Button) findViewById(R.id.buting); tingxia = (Button) findViewById(R.id.tingxia); list = (ListView) findViewById(R.id.list); jibu = (TextView) findViewById(R.id.jibu); dianliang = (TextView) findViewById(R.id.dianliang); lianjiezhuangtai = (TextView) findViewById(R.id.lianjiezhuangtai); saomiao.setOnClickListener(this); duanzhen.setOnClickListener(this); changzhen.setOnClickListener(this); buting.setOnClickListener(this); tingxia.setOnClickListener(this); //item 監聽事件 list.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) { bluetoothDevice = deviceList.get(i); //連接設備的方法,返回值為bluetoothgatt類型 bluetoothGatt = bluetoothDevice.connectGatt(MainActivity.this, false, gattcallback); lianjiezhuangtai.setText("連接" + bluetoothDevice.getName() + "中..."); } }); } @Override public void onClick(View view) { switch (view.getId()) { case R.id.saomiao: //開始掃描前開啟藍牙 Intent turn_on = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); startActivityForResult(turn_on, 0); Toast.makeText(MainActivity.this, "藍牙已經開啟", Toast.LENGTH_SHORT).show(); Thread scanThread = new Thread(new Runnable() { @Override public void run() { Log.i("TAG", "run: saomiao ..."); saomiao(); } }); scanThread.start(); lianjiezhuangtai.setText("正在掃描"); break; case R.id.zhendong: break; case R.id.changzhen: break; case R.id.buting: break; case R.id.tingxia: break; case R.id.list: break; } } public void saomiao() { deviceList.clear(); bluetoothAdapter.startLeScan(callback); } //掃描回調 public BluetoothAdapter.LeScanCallback callback = new BluetoothAdapter.LeScanCallback() { @Override public void onLeScan(final BluetoothDevice bluetoothDevice, int i, byte[] bytes) { Log.i("TAG", "onLeScan: " + bluetoothDevice.getName() + "/t" + bluetoothDevice.getAddress() + "/t" + bluetoothDevice.getBondState()); //重復過濾方法,列表中包含不該設備才加入列表中,並刷新列表 if (!deviceList.contains(bluetoothDevice)) { //將設備加入列表數據中 deviceList.add(bluetoothDevice); list.setAdapter(new MyAdapter(MainActivity.this, deviceList)); } } }; private BluetoothGattCallback gattcallback = new BluetoothGattCallback() { @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, final int newState) { super.onConnectionStateChange(gatt, status, newState); runOnUiThread(new Runnable() { @Override public void run() { String status; switch (newState) { //已經連接 case BluetoothGatt.STATE_CONNECTED: lianjiezhuangtai.setText("已連接"); bluetoothAdapter.stopLeScan(callback); //該方法用於獲取設備的服務,尋找服務 bluetoothGatt.discoverServices(); break; //正在連接 case BluetoothGatt.STATE_CONNECTING: lianjiezhuangtai.setText("正在連接"); break; //連接斷開 case BluetoothGatt.STATE_DISCONNECTED: lianjiezhuangtai.setText("已斷開"); break; //正在斷開 case BluetoothGatt.STATE_DISCONNECTING: lianjiezhuangtai.setText("斷開中"); break; } //pd.dismiss(); } }); } @Override public void onServicesDiscovered(BluetoothGatt gatt, int status) { super.onServicesDiscovered(gatt, status); //尋找到服務時 if (status == bluetoothGatt.GATT_SUCCESS) { final List<BluetoothGattService> services = bluetoothGatt.getServices(); runOnUiThread(new Runnable() { @Override public void run() { //List<String> serlist = new ArrayList<>(); for (final BluetoothGattService bluetoothGattService : services) { bluetoothGattServices = bluetoothGattService; Log.i(TAG, "onServicesDiscovered: " + bluetoothGattService.getUuid()); List<BluetoothGattCharacteristic> charc = bluetoothGattService.getCharacteristics(); for (BluetoothGattCharacteristic charac : charc) { Log.i(TAG, "run: " + charac.getUuid()); //找到透傳特征值 // 00002a06-0000-1000-8000-00805f9b34fb 小米手環震動特征值 0x01震動 0x02強震 if (charac.getUuid().toString().equals("00002a06-0000-1000-8000-00805f9b34fb")) { //設備 震動特征值 characteristic_zd = charac; } else if (charac.getUuid().toString().equals("0000ff06-0000-1000-8000-00805f9b34fb")) { //設備 步數 characteristic_jb = charac; bluetoothGatt.readCharacteristic(characteristic_jb); Log.i(TAG, "run: 正在嘗試讀取步數"); } else if (charac.getUuid().toString().equals("")) { //設備 電量特征值 } } serviceslist.add(bluetoothGattService.getUuid().toString()); } // ArrayAdapter<String> adapter = new ArrayAdapter<String>( // MainActivity.this, android.R.layout.simple_expandable_list_item_1, serviceslist); //list.setAdapter(adapter); } }); } } @Override public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { super.onCharacteristicRead(gatt, characteristic, status); if (status == bluetoothGatt.GATT_SUCCESS) { final int sum = characteristic.getValue()[0]; runOnUiThread(new Runnable() { @Override public void run() { jibu.setText("走了" + sum + "步"); } }); Log.e(TAG, "onCharacteristicRead: " + characteristic.getValue()[0]); } } @Override public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { super.onCharacteristicWrite(gatt, characteristic, status); } //獲取返回 數據 @Override public void onCharacteristicChanged(BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) { super.onCharacteristicChanged(gatt, characteristic); Log.i(TAG, "onCharacteristicChanged: 獲取回調方法"); Log.e("", "命令:" + HexUtil.encodeHexStr(characteristic.getValue())); final byte[] values = characteristic.getValue(); runOnUiThread(new Runnable() { @Override public void run() { Toast.makeText(MainActivity.this, HexUtil.encodeHexStr(values), Toast.LENGTH_SHORT).show(); } }); } }; private boolean enableNotification(boolean enable, BluetoothGattCharacteristic characteristic) { if (bluetoothGatt == null || characteristic == null) return false; if (!bluetoothGatt.setCharacteristicNotification(characteristic, enable)) return false; BluetoothGattDescriptor clientConfig = characteristic.getDescriptor(UUID.fromString("00002902-0000-1000-8000-00805f9b34fb")); if (clientConfig == null) return false; if (enable) { clientConfig.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE); } else { clientConfig.setValue(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE); } return bluetoothGatt.writeDescriptor(clientConfig); } }
今天就寫這些,下次要實現震動方法咯!是不是很激動呢~~
嘿嘿嘿~
大家加油啦~~