藍芽開發(二)掃描裝置
一、申請位置許可權
在Android6.0以後要掃描藍芽裝置,還需要請求位置許可權:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
位置許可權屬於危險許可權,因此需要動態獲取:
//判斷是否有許可權 if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) { //請求許可權 ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_COARSE_LOCATION},MY_PERMISSIONS_REQUEST_ACCESS_COARSE_LOCATION); //判斷是否需要 向用戶解釋,為什麼要申請該許可權 if(ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.READ_CONTACTS)) { Toast.makeText(this,"shouldShowRequestPermissionRationale", Toast.LENGTH_SHORT).show(); } } //許可權申請結果 @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); }
在實際開發的過程中你也可以設定只有支援藍芽的裝置才能執行該APP:
<uses-feature
android:name="android.hardware.bluetooth_le"
android:required="true" />
這裡設定required為true就是隻有在支援藍芽的裝置上才能執行,但是如果想讓你的app提供給那些不支援BLE的裝置,需要在manifest中包括上面程式碼並設定required="false"
,然後在執行時可以通過使用PackageManager.hasSystemFeature()
確定BLE的可用性。
// 使用此檢查確定BLE是否支援在裝置上,然後你可以有選擇性禁用BLE相關的功能
if (!getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
Toast.makeText(this, R.string.ble_not_supported, Toast.LENGTH_SHORT).show();
finish();
}
二、掃描方式
Android官方提供的藍芽掃描方式有三種,分別是:
- BluetoothAdapter.startDiscovery()//可以掃描經典藍芽和ble藍芽兩種
- BluetoothAdapter.startLeScan()//掃描低功耗藍芽,在api21已經棄用,不過還是可以使用
- BluetoothLeScanner.startScan()//新的ble掃描方法
startDiscovery()方法在大多數手機上是可以同時發現經典藍芽和低功耗藍芽(BLE)的,但是startDiscovery()的回撥無法返回BLE的廣播,所以無法通過廣播識別裝置,而且startDiscovery()掃描BLE效率比startLeScan()低很多。因此需要根據具體的需求去做適配,才能更高效的搜尋藍芽。PS: startLeScan()和startScan()有過載方法可以指定規則,引數去搜索。
市面上的藍芽裝置,現在一般都是低功耗的藍芽裝置,因此只需要用下面兩種掃描方式就OK了
2.1.startLeScan方式
官方提供的事例程式碼正是使用了這種方式,startLeScan方法有兩個:
public boolean startLeScan(LeScanCallback callback)
public boolean startLeScan(final UUID[] serviceUuids, final LeScanCallback callback)
檢視原始碼會發現一個引數的方法是呼叫了兩個引數的方法,只是serviceuuid[]傳入為null,也就是過濾條件為空。這裡我使用的是沒有過濾條件的方法:
// Stops scanning after 10 seconds.
private static final long SCAN_PERIOD = 10000;
...
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);
}
}, SCAN_PERIOD);
mScanning = true;
mBluetoothAdapter.startLeScan(mLeScanCallback);
} else {
mScanning = false;
mBluetoothAdapter.stopLeScan(mLeScanCallback);
}
...
}
// Device scan callback.
private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
@Override
public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
//掃描到的裝置資訊,將字元陣列轉成16進位制字串
String scanRecordStr = SysConvert.bytesToHex(scanRecord);
//在這裡可以做一下簡單的操作,避免多操作造成效能問題
}
};
因為在掃描藍芽是一個非常非常耗能的操作,因此一定要掃描一段時間就關閉,如果需要迴圈掃描,可以通過定時器實現掃描的開關操作
注意:開啟和關閉所使用的回撥必須是同一個,雖然關閉一個沒有開啟的回撥沒有錯誤返回。
2.2.startScan方式
我在專案中是使用startScan()掃描的,因為可以通過設定一些引數來靈活控制掃描的頻率等。
/**
* 掃描裝置
*/
public void scanDevices() {
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
mProgressDialog.dismiss();
if (mBluetoothLeScanner != null) {
mBluetoothLeScanner.stopScan(scanCallback);
}
}
}, 1000);
mBluetoothLeScanner.startScan(null, createScanSetting(), scanCallback);
}
/**
* 掃描廣播資料設定
*
* @return
*/
public ScanSettings createScanSetting() {
ScanSettings.Builder builder = new ScanSettings.Builder();
builder.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY);
//builder.setReportDelay(100);//設定延遲返回時間
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
builder.setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES);
}
return builder.build();
}
/**
* 回撥
*/
private ScanCallback scanCallback=new ScanCallback() {
@Override
public void onScanResult(int callbackType, ScanResult result) {
byte[] scanData=result.getScanRecord().getBytes();
//把byte陣列轉成16進位制字串,方便檢視
}
@Override
public void onBatchScanResults(List<ScanResult> results) {
super.onBatchScanResults(results);
}
@Override
public void onScanFailed(int errorCode) {
super.onScanFailed(errorCode);
}
};
ScanSettings :
setScanMode:有三種模式分別是
SCAN_MODE_LOW_POWER--------耗電最少,掃描時間間隔最短
SCAN_MODE_BALANCED---------平衡模式,耗電適中,掃描時間間隔一般,我使用這種模式來更新裝置狀態
SCAN_MODE_LOW_LATENCY---------最耗電,掃描延遲時間短,開啟掃描需要立馬返回結果可以使用
setReportDelay:設定掃描返回延遲時間,一般是大於零的毫秒值
如果設定了該屬性,掃描結果會以list形式在onBatchScanResults方法返回,在onScanResult中沒有返回,如果不設定則在onScanResult中返回,返回的資訊包含在result中,可以通過getBytes返回每一個裝置資訊的字元陣列,getDevice獲取BluetoothDevice裝置,在通過字元陣列和藍芽裝置物件獲取你想要的資訊
本人也是一個藍芽開發新手,如果上面有什麼地方有問題,請指出,共同探討,共同進步,不勝感激!