Android 8.0 进程拉活 --- 蓝牙唤醒
前言:
IPhone 可以通過 ibeacon 設(shè)備發(fā)出的藍牙廣播來喚醒應(yīng)用,但android有沒有類似的機制來進行喚醒app呢?
很開心的告訴你,在 android 8.0(android 0) 以上的系統(tǒng)已經(jīng)支持了!!!
說明:
在android 8.0 的 API中,藍牙庫中的android.bluetooth.le.BluetoothLeScanner類增加了一個新方法,看下圖
注:api level 26 即 android 8.0
該方法是用于掃描手機周邊的藍牙設(shè)備。在8.0以前,google提供的藍牙掃描方法都是需要app進程還活。但該方法只要調(diào)用成功,無論app進程是否還活著,系統(tǒng)都會在后臺持續(xù)執(zhí)行藍牙掃描。如果手機靠近指定的藍牙設(shè)備附近,app就能被喚醒接收藍牙的掃描結(jié)果。
用途:
該機制雖然只能在特定的區(qū)域?qū)pp進行喚醒,但在很多業(yè)務(wù)場景上非常實用,舉幾個栗子:
1.室內(nèi)定位
2.進入商場用戶立馬能收到商場的活動信息
3.運動手環(huán)需要即時上報數(shù)據(jù)(如:電量不足等)
....
開發(fā):
支持該機制的拉活,實際上就是讓app去掃描一個特定的藍牙廣播,等待系統(tǒng)返回結(jié)果。相信有弄過藍牙開發(fā)的小伙伴都知道藍牙開發(fā)這個坑有多深,而下面是一個跳坑的教程。
一.權(quán)限:
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
注:ACCESS_FINE_LOCATION在android 6.0以上需要運行獲取(見6.0以上運行時權(quán)限申請)。
app除獲取了上述權(quán)限,還需要確保藍牙開啟,以下是代碼開啟藍牙的方法:
//判斷如果藍牙沒有開啟的話,則進行提示用戶開啟
BluetoothManager bluetoothManager = (BluetoothManager)getSystemService(Context.BLUETOOTH_SERVICE);
BluetoothAdapter bluetoothAdapter = bluetoothManager.getAdapter();
if (bluetoothAdapter == null || !bluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
二.API調(diào)用
@TargetApi(26)
public void onOpen(View view){
//BluetoothManager是向藍牙設(shè)備通訊的入口
BluetoothManager bluetoothManager = (BluetoothManager)getSystemService(Context.BLUETOOTH_SERVICE);
BluetoothAdapter bluetoothAdapter = bluetoothManager.getAdapter();
//指定需要識別到的藍牙設(shè)備
List<ScanFilter> scanFilterList = new ArrayList<>();
ScanFilter.Builder builder = new ScanFilter.Builder();
builder.setServiceUuid(ParcelUuid.fromString(UUID_SERVICE));
ScanFilter scanFilter = builder.build();
scanFilterList.add(scanFilter);
//指定藍牙的方式,這里設(shè)置的ScanSettings.SCAN_MODE_LOW_LATENCY是比較高頻率的掃描方式
ScanSettings.Builder settingBuilder = new ScanSettings.Builder();
settingBuilder.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY);
settingBuilder.setMatchMode(ScanSettings.MATCH_MODE_AGGRESSIVE);
settingBuilder.setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES);
settingBuilder.setLegacy(true);
ScanSettings settings = settingBuilder.build();
//指定掃描到藍牙后是以什么方式通知到app端,這里將以可見服務(wù)的形式進行啟動
PendingIntent callbackIntent = PendingIntent.getForegroundService(
this,
1,
new Intent("com.hungrytree.receiver.BleService").setPackage(getPackageName()),
PendingIntent.FLAG_UPDATE_CURRENT );
//啟動藍牙掃描
bluetoothAdapter.getBluetoothLeScanner().startScan(scanFilterList,settings,callbackIntent);
}
這是開啟藍牙掃描的代碼,此處不做過多的說明,有想對android藍牙開發(fā)有更深入了解的小伙伴,可以前往這里的傳送門。不過需要提醒一下大家要注意兩個點:
1.builder.setServiceUuid(ParcelUuid.fromString("填入您需要掃描的設(shè)備uuid"));
此處的uuid就相當于是你藍牙設(shè)備的標志,是一定要與你的設(shè)備匹配上的,不能隨便亂填。
2.PendingIntent建議以前臺服務(wù)(getForegroundService)方式create。經(jīng)過測試,如果以getService,getBroadcast方法進行創(chuàng)建PendingIntent的話在拉活方面存在比較多的兼容問題.而getForegroundService方式在國內(nèi)主流機型上可正常拉活進程(經(jīng)過測試的機型有:華為,小米,oppo,vivo等)
PendingIntent.getForegroundService(
this,
1,
new Intent("com.hungrytree.receiver.BleService").setPackage(getPackageName()),
PendingIntent.FLAG_UPDATE_CURRENT );
上面代碼已經(jīng)實現(xiàn)藍牙掃描觸發(fā),而下面是實現(xiàn)藍牙廣播的接收:
//AndroidManifest中的服務(wù)聲明
<service android:name=".TestService"> <intent-filter> <action android:name="com.hungrytree.receiver.BleService"/> <category android:name="android.intent.category.DEFAULT"/> </intent-filter> </service>
@TargetApi(26)
public class TestService extends Service {
@Override
public void onCreate() {
super.onCreate();
//以前臺服務(wù)的方式啟動,要調(diào)用startForeground,否則會出現(xiàn)arn異常
Notification notification = new Notification.Builder(this, NotificationChannel.DEFAULT_CHANNEL_ID)
.setLargeIcon(BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher))
.build();
startForeground(110, notification);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent.getAction() == null) {
return super.onStartCommand(intent, flags, startId);
}
//獲取返回的錯誤碼
int errorCode = intent.getIntExtra(BluetoothLeScanner.EXTRA_ERROR_CODE, -1);//ScanSettings.SCAN_FAILED_*
//獲取到的藍牙設(shè)備的回調(diào)類型
int callbackType = intent.getIntExtra(BluetoothLeScanner.EXTRA_CALLBACK_TYPE, -1);//ScanSettings.CALLBACK_TYPE_*
if (errorCode == -1) {
//掃描到藍牙設(shè)備信息
List<ScanResult> scanResults = (List<ScanResult>) intent.getSerializableExtra(BluetoothLeScanner.EXTRA_LIST_SCAN_RESULT);
if (scanResults != null) {
for (ScanResult result : scanResults) {
//打印所有設(shè)備的地址
String address = result.getDevice().getAddress();
Log.i("haha", "device address " + address);
}
}
} else {
//此處為掃描失敗的錯誤處理
}
return super.onStartCommand(intent, flags, startId);
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onDestroy() {
super.onDestroy();
}
}
此處是一個的ForegroundService的創(chuàng)建,他和普通服務(wù)的創(chuàng)建沒什么大區(qū)別,不同的地方是在服務(wù)創(chuàng)建后要即時調(diào)用startForeground()方法,否則會出現(xiàn)anr異常。正常調(diào)用了startForeground()方法,手機通知欄會出現(xiàn)相應(yīng)的通知提示,但國內(nèi)很多機型是不會顯示通知欄的,所以按這種方式很多用戶也看不到進程被拉活了( ̄▽ ̄)。
通過intent回調(diào)過來的參數(shù)見下表:
|
常量(BluetoothLeScanner.java中) |
值 | 描述 |
| EXTRA_CALLBACK_TYPE | 見 android.bluetooth.le.ScanSettings.CALLBACK_TYPE_* | 回調(diào)的內(nèi)容類型 |
| EXTRA_ERROR_CODE | 見android.bluetooth.le.ScanCallback.SCAN_FAILED_* | 掃描失敗的錯誤碼,若成為,則該字段為空 |
| EXTRA_LIST_SCAN_RESULT | 見 List<android.bluetooth.le.ScanResult> | 掃描成功之后獲得的藍牙設(shè)備息 |
有啟動掃描,當然有關(guān)閉方法,見以下代碼:
@TargetApi(26)
public void onClose(View view){
PendingIntent callbackIntent = PendingIntent.getBroadcast(
this,
1,
new Intent("com.hungrytree.receiver.BleReceiver")
.setPackage(getPackageName()),
PendingIntent.FLAG_UPDATE_CURRENT );
BluetoothManager bluetoothManager = (BluetoothManager)getSystemService(Context.BLUETOOTH_SERVICE);
BluetoothAdapter bluetoothAdapter = bluetoothManager.getAdapter();
bluetoothAdapter.getBluetoothLeScanner().stopScan(callbackIntent);
}
三、注意事項
以上的api調(diào)用在主流機型親測是沒有問題,但如果遇到以下問題可償試用以下方式解決
1.藍牙無法拉活
大多數(shù)國內(nèi)的手機里面都有一個很神奇的權(quán)限----自啟動權(quán)限,它的意思不僅僅包含手機啟動的時候啟動app,同時還有其它應(yīng)用(包含系統(tǒng)應(yīng)用)能否拉活你的應(yīng)用。簡單的說,如果發(fā)現(xiàn)不能被拉活,那就可能是系統(tǒng)的限制,試一下打開app的自啟動權(quán)限。
2.無論怎么樣都無法掃描到藍牙
在android 6.0以上的系統(tǒng),部分手機如果想掃描到藍牙設(shè)備,還要檢查位置服務(wù)或定位服務(wù)是否開啟!!!在位置服務(wù)打開之后是有其它選項要求的。一般有三種選項,分別是 1.高精確度(GPS+網(wǎng)絡(luò))2.低耗電量(網(wǎng)絡(luò)) 3.權(quán)限設(shè)備(GPS)。需要用到藍牙掃描的話就只能選1或者2,選3是沒有用的。以下小米的位置服務(wù)為例,看下圖。
3.用戶關(guān)閉藍牙后再打開,會停止原先所有的藍牙掃描,也意味著終止了app喚醒
假設(shè)進程還活著的時候,監(jiān)測藍牙開關(guān)再觸發(fā)藍牙掃描是沒有問題的。但如果進程死后,用戶關(guān)閉藍牙,那暫時還沒找到合適的辦法。
參考資料:
https://developer.android.com/reference/android/bluetooth/le/ScanSettings(需要科學上網(wǎng))
結(jié)論:
android 8.0以上也可以支持類似像ios ibeacon方式的喚醒,同時不會像蘋果局限于只能用ibeacon,能給許多的業(yè)務(wù)帶來更好的擴展。
如果該拉活方式只是為了提供給用戶在特定的場景有更好的體驗,那我是建議的。但如果要用來讓app持續(xù)性后臺運行,這種方式就有點濫用了。哪天google或者國內(nèi)的產(chǎn)商一不開心就......
最后,感謝各位大牛的閱讀,如果有不對的地方希望能幫忙提醒改正,在此先謝過。
總結(jié)
以上是生活随笔為你收集整理的Android 8.0 进程拉活 --- 蓝牙唤醒的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 子午书简——电子书网站
- 下一篇: 播布客视频PIT专用播放器MBOO201