Android開(kāi)發(fā)并非我擅長(zhǎng)偿警,最近剛好又做回藍(lán)牙業(yè)務(wù)了负间,iOS搞完了看了下Android的,似乎區(qū)別不大隙姿,唯一比iOS多了一個(gè)descriptor
梅垄,那就順便把Android藍(lán)牙也搞搞吧,本文僅做學(xué)習(xí)記錄完成過(guò)程以及中途遇到的坑。代碼是否規(guī)范不重要哈队丝!
1.申請(qǐng)權(quán)限
首先在第一步我就遇到了坑靡馁,Android藍(lán)牙開(kāi)發(fā)需要申請(qǐng)地理位置權(quán)限,位置權(quán)限是敏感權(quán)限机久,高版本Android需要?jiǎng)討B(tài)申請(qǐng)臭墨,最開(kāi)始我只在AndroidManifest
添加了權(quán)限申請(qǐng),結(jié)果就是不管用舊的API還是最新的API掃描膘盖,都不走掃描結(jié)果回調(diào)胧弛!注意避坑!O琅稀结缚!
在AndroidManifest
中申請(qǐng)以下權(quán)限
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-feature android:name="android.hardware.location.gps" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
然后在代碼中動(dòng)態(tài)申請(qǐng),這里寫一個(gè)requestPermission
方法申請(qǐng)
private void requestPermission() {
//動(dòng)態(tài)申請(qǐng)
if (Build.VERSION.SDK_INT < 23) {
return;
}
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, 1);
}
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, 1);
}
}
2.初始化藍(lán)牙
private void initBluetooth() {
mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = mBluetoothManager.getAdapter();
mBluetoothLeScanner = mBluetoothAdapter.getBluetoothLeScanner();
if (mBluetoothAdapter == null) {
Log.d(TAG, "藍(lán)牙不支持");
} else {
int status = mBluetoothAdapter.getState();
if (status == BluetoothAdapter.STATE_OFF) {
mBluetoothAdapter.enable();
}else {
Log.d(TAG, "藍(lán)牙可用");
}
}
}
3.開(kāi)始掃描
掃描的方式可以用BluetoothAdapter.startLeScan
掃描软棺,但谷歌已經(jīng)不建議用該方法掃描了红竭,那就用最新的BluetoothLeScanner
的方式掃描吧,在需要掃描的地方添加下面的代碼喘落,掃描可以根據(jù)服務(wù)UUID掃描固定一類藍(lán)牙設(shè)備茵宪,也可以不過(guò)濾掃描所有設(shè)備,我這里根據(jù)服務(wù)UUID過(guò)濾(Android除了UUID過(guò)濾掃描瘦棋,還有其他的稀火,比如name等,沒(méi)仔細(xì)研究赌朋,iOS過(guò)濾條件目前就只有UUID)
List<ScanFilter> bleScanFilters = new ArrayList<>();
bleScanFilters.add(new ScanFilter.Builder().setServiceUuid(ParcelUuid.fromString("你的藍(lán)牙設(shè)備UUID")).build());
ScanSettings scanSetting = new ScanSettings.Builder().setScanMode(SCAN_MODE_LOW_LATENCY).setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES).setMatchMode(ScanSettings.MATCH_MODE_STICKY).build();
mBluetoothLeScanner.startScan(bleScanFilters, scanSetting, callback);
Log.d(TAG, "開(kāi)始掃描");
4.掃描回調(diào)并連接
因?yàn)槲沂歉鶕?jù)UUID只掃描一類設(shè)備凰狞,為了省電掃描到后我直接停止掃描,然后連接第一個(gè)掃描到的設(shè)備
final ScanCallback stopCallback = new ScanCallback() {
};
final ScanCallback callback = new ScanCallback() {
@Override
public void onScanResult(int callbackType, ScanResult result) {
super.onScanResult(callbackType, result);
Log.d(TAG, "掃描到設(shè)備");
BluetoothDevice btDevice = result.getDevice();
mBluetoothLeScanner.stopScan(stopCallback);
btDevice.connectGatt(MainActivity.this,true, connectCallBack);
}
@Override
public void onBatchScanResults(List<ScanResult> results) {
super.onBatchScanResults(results);
Log.d(TAG, "onBatchScanResults");
}
@Override
public void onScanFailed(int errorCode) {
super.onScanFailed(errorCode);
Log.d(TAG, "掃描出錯(cuò)");
}
};
5.連接回調(diào)箕慧,發(fā)現(xiàn)服務(wù)服球,發(fā)現(xiàn)特征值,監(jiān)聽(tīng)藍(lán)牙發(fā)過(guò)來(lái)的數(shù)據(jù)颠焦,發(fā)送數(shù)據(jù)
final BluetoothGattCallback connectCallBack = new BluetoothGattCallback(){
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
super.onConnectionStateChange(gatt, status, newState);
if (newState == BluetoothProfile.STATE_CONNECTED){
Log.d(TAG, "連接成功");
gatt.discoverServices();
}
}
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
super.onServicesDiscovered(gatt, status);
Log.d(TAG, "發(fā)現(xiàn)服務(wù)");
BluetoothGattService service = gatt.getService(UUID.fromString("藍(lán)牙設(shè)備服務(wù)uuid"));
if (service != null){
Log.d(TAG, "有服務(wù)");
BluetoothGattCharacteristic notyChara = service.getCharacteristic(UUID.fromString("藍(lán)牙設(shè)備可監(jiān)聽(tīng)征值uuid"));
if (notyChara != null){
Log.d(TAG, "有特征值");
boolean success = gatt.setCharacteristicNotification(notyChara,true);
Log.i("監(jiān)聽(tīng)結(jié)果: "+success);
BluetoothGattDescriptor descriptor = notyChara.getDescriptor(UUID.fromString("藍(lán)牙設(shè)備描述uuid"));
if (descriptor != null){
descriptor.setValue(BluetoothGattDescriptor.ENABLE_INDICATION_VALUE);
gatt.writeDescriptor(descriptor);
}
}
//發(fā)送數(shù)據(jù)
BluetoothGattCharacteristic writeChar = service.getCharacteristic(UUID.fromString("藍(lán)牙設(shè)備可寫特征值的UUID"));
writeChar.setValue(TAG.getBytes());
gatt.writeCharacteristic(writeChar);
}
}
@Override
public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
super.onCharacteristicChanged(gatt, characteristic);
Log.d(TAG, "收到數(shù)據(jù)");
}
};
值得一提的是斩熊,為了省電,在不需要監(jiān)聽(tīng)數(shù)據(jù)后需要設(shè)置停止監(jiān)聽(tīng)伐庭,也可以用readCharacteristic
讀取單條數(shù)據(jù)
gatt.setCharacteristicNotification(chara,false);
descriptor.setValue(BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE);
在不需要藍(lán)牙后應(yīng)該調(diào)用gatt的disconnect
方法斷開(kāi)設(shè)備粉渠,因?yàn)橛凶畲筮B接數(shù)限制,還需要調(diào)用gatt的close
方法釋放當(dāng)前連接個(gè)數(shù)計(jì)數(shù)圾另。