很久沒記錄東西了,前段時間研究了一哈android4.0控制多個外設的情況,注意,需要使用android版本4.3以上,藍牙4.0及以上。
我這裏使用的控制藍牙燈泡,使用android4.3的手機,手機上的藍牙是4.0.
記得在manifest文件中加入權限:
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
先拿到BluetoothManager和BluetoothAdapter的對象。
// 初始化 Bluetooth adapter, 通過藍牙管理器得到一個參考藍牙適配器(API必須在以上android4.3或以上和版本)
final BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = bluetoothManager.getAdapter();
// 檢查設備上是否支持藍牙
if (mBluetoothAdapter == null) {
Toast.makeText(this, R.string.error_bluetooth_not_supported,Toast.LENGTH_SHORT).show();
finish();
return;
}
看下是否開啓了藍牙,如果沒有開啓,跳轉到設置去開啓藍牙。
// 爲了確保設備上藍牙能使用, 如果當前藍牙設備沒啓用,彈出對話框向用戶要求授予權限來啓用
if (!mBluetoothAdapter.isEnabled()) {
if (!mBluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
}
調用startScan方法開始掃描,stopScan方法停止掃描,掃描到的設備都在回調函數裏。
private void scanLeDevice(final boolean enable) {
if (enable) {
// Stops scanning after a pre-defined scan period.
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
invalidateOptionsMenu();
}
}, SCAN_PERIOD);
mScanning = true;
mBluetoothAdapter.startLeScan(mLeScanCallback);
} else {
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
invalidateOptionsMenu();
}
// Device scan callback.
private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
@Override
public void onLeScan(final BluetoothDevice device, int rssi,
byte[] scanRecord) {
runOnUiThread(new Runnable() {
@Override
public void run() {
mLeDeviceListAdapter.addDevice(device);
// mLeDeviceListAdapter.notifyDataSetChanged();
}
});
}
};
掃描後的BluetoothDevice加入到列表中,這時列表中就會有設備,通過getName可以獲取設備的藍牙名字,getAddress獲取設備的藍牙地址。
列表出來了之後,點擊某個設備進行連接。
注意這裏的連接跟2.0的藍牙的連接不一樣,通過設備的connectGatt方法進行連接,連接完成後會獲得一個BluetoothGatt的對象,這個對象中有連接的一些重要信息,切記要保存好。
public boolean connect(final String address) {
if (mBluetoothAdapter == null || address == null) {
Log.w(TAG, "BluetoothAdapter not initialized or unspecified address.");
return false;
}
if (mBluetoothDeviceAddress != null
&& address.equals(mBluetoothDeviceAddress)
&& mBluetoothGatt != null) {
Log.d(TAG,
"Trying to use an existing mBluetoothGatt for connection.");
if (mBluetoothGatt.connect()) {
return true;
} else {
return false;
}
}
final BluetoothDevice device = mBluetoothAdapter
.getRemoteDevice(address);
if (device == null) {
Log.w(TAG, "Device not found. Unable to connect.");
return false;
}
// We want to directly connect to the device, so we are setting the
// autoConnect
// parameter to false.
mBluetoothGatt = device.connectGatt(this, true, mGattCallback);
Log.d(TAG, "Trying to create a new connection.");
mBluetoothDeviceAddress = address;
bluetoothGatts.add(mBluetoothGatt);
return true;
}
連接時有一個回調函數mGattCallback,這個函數中有很多設備的相關信息,比如設備的狀態啊,設備中的通道哈,一些服務啊之類的。
// Implements callback methods for GATT events that the app cares about. For
// example,
// connection change and services discovered.
private final BluetoothGattCallback mGattCallback = new BluetoothGattCallback() {
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status,
int newState) {
System.out.println("onConnectionStateChange");
String intentAction;
if (status == BluetoothProfile.STATE_DISCONNECTED
&& newState == BluetoothProfile.STATE_CONNECTED) {
intentAction = ACTION_GATT_CONNECTED;
Log.i(TAG, "Connected to GATT server.");
// Attempts to discover services after successful connection.
Log.i(TAG, "Attempting to start service discovery:"
+ mBluetoothGatt.discoverServices());
Log.e("shang", "連接成功");
mHandler.sendEmptyMessage(MESSAGE_START_SUCCESS);
} else if (status == BluetoothProfile.STATE_DISCONNECTED
&& newState == BluetoothProfile.STATE_DISCONNECTED) {
intentAction = ACTION_GATT_DISCONNECTED;
setConnStatus(false);
Log.e("shang", "連接失敗");
Log.i(TAG, "Disconnected from GATT server.");
mHandler.sendEmptyMessage(STATE_CONNECTFAILED);
}
}
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
System.out.println("onServicesDiscovered-----");
if (status == BluetoothGatt.GATT_SUCCESS) {
broadcastUpdate(ACTION_GATT_SERVICES_DISCOVERED);
displayGattServices(getSupportedGattServices());
mHandler.sendEmptyMessage(MESSAGE_BLUETOOTH_INIT);
} else {
Log.w(TAG, "onServicesDiscovered received: " + status);
}
}
@Override
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic, int status) {
System.out.println("onCharacteristicRead");
if (status == BluetoothGatt.GATT_SUCCESS) {
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
}
}
@Override
public void onDescriptorWrite(BluetoothGatt gatt,
BluetoothGattDescriptor descriptor, int status) {
System.out.println("onDescriptorWriteonDescriptorWrite = " + status
+ ", descriptor =" + descriptor.getUuid().toString());
}
@Override
public void onCharacteristicChanged(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic) {
broadcastUpdate(ACTION_DATA_AVAILABLE, characteristic);
if (characteristic.getValue() != null) {
System.out.println(characteristic.getStringValue(0));
}
System.out.println("--------onCharacteristicChanged-----");
}
@Override
public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
}
public void onCharacteristicWrite(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic, int status) {
System.out.println("--------write success----- status:" + status);
};
};
連接時會走這個方法onConnectionStateChange,傳過來的新狀態是連接狀態,這時在這個方法中調用一下這句:mBluetoothGatt.discoverServices(),
mBluetoothGatt是連接完成時的對象,還記得吧,調用這句後,會走回調函數的onServicesDiscovered方法。在這個方法中去獲取設備的一些服務,藍牙通道,然後通過這些通道去發送數據給外設。
查看該外設中支持的一些服務通道:
public List<BluetoothGattService> getSupportedGattServices() {
if (mBluetoothGatt == null)
return null;
return mBluetoothGatt.getServices();
}
// Demonstrates how to iterate through the supported GATT
// Services/Characteristics.
// In this sample, we populate the data structure that is bound to the
// ExpandableListView
// on the UI.
private void displayGattServices(List<BluetoothGattService> gattServices) {
if (gattServices == null)
return;
String uuid = null;
ArrayList<HashMap<String, String>> gattServiceData = new ArrayList<HashMap<String, String>>();
ArrayList<ArrayList<HashMap<String, String>>> gattCharacteristicData = new ArrayList<ArrayList<HashMap<String, String>>>();
mGattCharacteristics = new ArrayList<ArrayList<BluetoothGattCharacteristic>>();
// Loops through available GATT Services.
for (BluetoothGattService gattService : gattServices) {
HashMap<String, String> currentServiceData = new HashMap<String, String>();
uuid = gattService.getUuid().toString();
gattServiceData.add(currentServiceData);
ArrayList<HashMap<String, String>> gattCharacteristicGroupData = new ArrayList<HashMap<String, String>>();
List<BluetoothGattCharacteristic> gattCharacteristics = gattService
.getCharacteristics();
ArrayList<BluetoothGattCharacteristic> charas = new ArrayList<BluetoothGattCharacteristic>();
// Loops through available Characteristics.
for (BluetoothGattCharacteristic gattCharacteristic : gattCharacteristics) {
charas.add(gattCharacteristic);
HashMap<String, String> currentCharaData = new HashMap<String, String>();
uuid = gattCharacteristic.getUuid().toString();
gattCharacteristicGroupData.add(currentCharaData);
}
mGattCharacteristics.add(charas);
gattCharacteristicData.add(gattCharacteristicGroupData);
}
bluetoothGattChacteristics.add(mGattCharacteristics);
}
然後就可以通過Gatt這個對像,就是藍牙連接完成後獲取到的對象,通過這個對象設置好指定的通道向設備中寫入和讀取數據。
寫數據:
public void wirteCharacteristic(BluetoothGattCharacteristic characteristic) {
if (mBluetoothAdapter == null || mBluetoothGatt == null) {
Log.w(TAG, "BluetoothAdapter not initialized");
return;
}
mBluetoothGatt.writeCharacteristic(characteristic);
}
讀數據:
/**
* Request a read on a given {@code BluetoothGattCharacteristic}. The read
* result is reported asynchronously through the
* {@code BluetoothGattCallback#onCharacteristicRead(android.bluetooth.BluetoothGatt, android.bluetooth.BluetoothGattCharacteristic, int)}
* callback.
*
* @param characteristic
* The characteristic to read from.
*/
public void readCharacteristic(BluetoothGattCharacteristic characteristic) {
if (mBluetoothAdapter == null || mBluetoothGatt == null) {
Log.w(TAG, "BluetoothAdapter not initialized");
return;
}
mBluetoothGatt.readCharacteristic(characteristic);
}
注意:BluetoothGattCharacteristic這個是指定的通道,藍牙服務:
BluetoothGattCharacteristic characteristic = null;
characteristic = mGattCharacteristics.get(4).get(4);
這兩個數字就是從指定的服務中找到你要發送數據的那個服務。
最後,如果要進行多個連接,每次連接完成後可以將BluetoothGatt的對象放到一個list裏面,獲取到的服務也放到一個List裏面,然後發送數據的時候調用不同的Gatt發送不同的通道數據即可。