Andorid 8.0 對(duì)廣播的使用做了變更。
當(dāng)廣播接收器使用靜態(tài)注冊(cè)方式使用時(shí),除了一些例外,這個(gè)接收器接收不到隱式廣播。 注意這個(gè)“隱式”是重點(diǎn)。
看了網(wǎng)上幾篇文章,對(duì)這個(gè)變更理解有誤。錯(cuò)誤的理解是:8.0后,廣播接收器使用靜態(tài)注冊(cè),是無法使用的。
實(shí)時(shí)并非如此。
先看一個(gè)例子:
首先,定義一個(gè)簡(jiǎn)單的廣播接收器:
public class MyReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
// TODO: This method is called when the BroadcastReceiver is receiving
// an Intent broadcast.
Toast.makeText(context,"收到廣播了~",Toast.LENGTH_LONG).show();
//throw new UnsupportedOperationException("Not yet implemented");
}
}
它對(duì)接收到廣播的行為就是打印一句話。
第二,我們將他注冊(cè)到Manifest文件中。
<receiver
android:name=".MyReceiver"
android:enabled="true"
android:exported="false">
<intent-filter>
<action android:name="test.example.com" />
</intent-filter>
</receiver>
最后,在Activity中發(fā)送一個(gè)廣播,intent通過設(shè)置Action為com.demo.recriver的形式發(fā)送隱式廣播。
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button=(Button)findViewById(R.id.button) ;
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// MyReceiver myReceiver=new MyReceiver();
// IntentFilter intentFilter=new IntentFilter();
// intentFilter.addAction("test.example.com");
// registerReceiver(myReceiver,intentFilter);
Intent intent=new Intent("test.example.com");
//intent.setComponent(new ComponentName(MainActivity.this,MyReceiver.class));
sendBroadcast(intent);
}
});
}
}
運(yùn)行這個(gè)demo,發(fā)現(xiàn)在8.0以下的手機(jī)上,會(huì)有Toast顯示,8.0以上的手機(jī)不會(huì)彈出,說明沒有接收到廣播。
原因在于這個(gè)廣播 是“隱式” 發(fā)送的,8.0中,靜態(tài)注冊(cè)的廣播接收者無法接受 隱式 廣播。
為了解決這個(gè)問題,有兩個(gè)方法:
1 在Activity或其他組件中動(dòng)態(tài)注冊(cè)廣播
2 發(fā)送顯示廣播
對(duì)于1 ,如果想廣播讓接收者工作,必須要在某個(gè)Activity或者其他組件中調(diào)用registerReceiver()進(jìn)行注冊(cè),在onDestroy()時(shí)還要反注冊(cè),代碼稍顯復(fù)雜,而且靜態(tài)注冊(cè)的廣播接收者仍處于不可用的狀態(tài)。不合理。
而后一種方法 ,因?yàn)閟endBroaccast是自己主動(dòng)發(fā)送的,明顯知道要哪個(gè)broadcastReceiver來進(jìn)行處理,直接發(fā)送顯示廣播即可。
具體的代碼如下,將MainActivity做修改:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button=(Button)findViewById(R.id.button) ;
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// MyReceiver myReceiver=new MyReceiver();
// IntentFilter intentFilter=new IntentFilter();
// intentFilter.addAction("test.example.com");
// registerReceiver(myReceiver,intentFilter);
Intent intent=new Intent();
intent.setComponent(new ComponentName(MainActivity.this,MyReceiver.class));
sendBroadcast(intent);
}
});
}
}
實(shí)際上,這種寫法與更常見的以下寫法相同:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button=(Button)findViewById(R.id.button) ;
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent=new Intent(MainActivity.this,MyReceiver.class);
sendBroadcast(intent);
}
});
}
}
運(yùn)行,發(fā)現(xiàn)可以彈出Toast,靜態(tài)注冊(cè)的BroadcastReceiver接收到了廣播。證明靜態(tài)注冊(cè)是可以接收到廣播的。
順便插一句:
Intent指定action, 這個(gè)Intent則為隱式Intent,使用它發(fā)送的廣播則為隱式廣播。隱式廣播接收者是通過IntentFilter去查找的。
Intent指定了組件名稱,這個(gè)Intent為顯式Intent,用他發(fā)送的廣播為顯式廣播。廣播接收者直接就是指定的組件名稱對(duì)應(yīng)的廣播接收者。
猜測(cè)顯式Intent不使用IntentFilter去查找組件(Activtiy,Service,BroadcastReceiver),這點(diǎn)讀者有興趣可以驗(yàn)證是否正確。
再來看一下Intent的setComponent()方法:

ntent設(shè)置了組件名稱(比如 new Intent(MainActivity.this,MyReceiver.class);)則通過IntentFilter匹配所需要的action,data,type,category信息會(huì)被忽略。
如果剛才的例子做如下改動(dòng):
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button=(Button)findViewById(R.id.button) ;
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent=new Intent("test.example.com");
intent.setComponent(new ComponentName(MainActivity.this,MyReceiver.class));
sendBroadcast(intent);
}
});
}
}
同時(shí)指定了隱式的action 以及顯式的組件名稱,action是一個(gè)不存在的action。這時(shí)仍可以接收到廣播。
雖然通過action不可能匹配到一個(gè)廣播接收者,但顯示設(shè)置了組件,action就被忽略了。
扯遠(yuǎn)了?;氐阶畛醯哪莻€(gè)話題。8.0以后,靜態(tài)注冊(cè)的廣播接受者是可以接收到廣播的,只要廣播是通過顯示方式發(fā)送的。
Android 8.0新特性-取消大部分靜態(tài)注冊(cè)廣播
解決方法:
(1)使用動(dòng)態(tài)廣播代替靜態(tài)廣播。
(2)保留原來的靜態(tài)廣播,但是加入組件參數(shù)。
(3)發(fā)送廣播的時(shí)候攜帶intent.addFlags(0x01000000); 即能讓廣播突破隱式廣播限制。
8.0還可以接受的隱式廣播:
隱式廣播例外
作為Android 8.0(API級(jí)別26)后臺(tái)執(zhí)行限制的一部分,針對(duì)API級(jí)別26或更高級(jí)別的應(yīng)用程序無法再在其清單中為隱式廣播注冊(cè)廣播接收器。但是,目前有幾個(gè)廣播免于這些限制。無論應(yīng)用程序所針對(duì)的API級(jí)別如何,應(yīng)用程序都可以繼續(xù)為以下廣播注冊(cè)監(jiān)聽器。
注意:即使這些隱式廣播仍然在后臺(tái)工作,您應(yīng)該避免為它們注冊(cè)偵聽器。
ACTION_LOCKED_BOOT_COMPLETED, ACTION_BOOT_COMPLETED
免除,因?yàn)檫@些廣播僅在首次啟動(dòng)時(shí)發(fā)送一次,并且許多應(yīng)用需要接收此廣播以安排作業(yè),警報(bào)等。
ACTION_USER_INITIALIZE,“android.intent.action.USER_ADDED”,“android.intent.action.USER_REMOVED”
這些廣播受特權(quán)權(quán)限保護(hù),因此大多數(shù)普通應(yīng)用程序無論如何都無法接收它們。
“android.intent.action.TIME_SET”,ACTION_TIMEZONE_CHANGED,ACTION_NEXT_ALARM_CLOCK_CHANGED
當(dāng)時(shí)間,時(shí)區(qū)或警報(bào)發(fā)生變化時(shí),時(shí)鐘應(yīng)用可能需要接收這些廣播以更新警報(bào)。
ACTION_LOCALE_CHANGED
僅在區(qū)域設(shè)置更改時(shí)發(fā)送,這不常見。應(yīng)用可能需要在區(qū)域設(shè)置更改時(shí)更新其數(shù)據(jù)。
ACTION_USB_ACCESSORY_ATTACHED,ACTION_USB_ACCESSORY_DETACHED,ACTION_USB_DEVICE_ATTACHED,ACTION_USB_DEVICE_DETACHED
如果應(yīng)用程序需要了解這些與USB相關(guān)的事件,目前還沒有一個(gè)很好的替代方案來注冊(cè)廣播。
ACTION_CONNECTION_STATE_CHANGED,ACTION_CONNECTION_STATE_CHANGED,ACTION_ACL_CONNECTED,ACTION_ACL_DISCONNECTED
如果應(yīng)用接收這些藍(lán)牙事件的廣播,則用戶體驗(yàn)不太可能受到影響。
ACTION_CARRIER_CONFIG_CHANGED,TelephonyIntents.ACTION_*_SUBSCRIPTION_CHANGED,“TelephonyIntents.SECRET_CODE_ACTION”,ACTION_PHONE_STATE_CHANGED,ACTION_PHONE_ACCOUNT_REGISTERED,ACTION_PHONE_ACCOUNT_UNREGISTERED
OEM電話應(yīng)用可能需要接收這些廣播。
LOGIN_ACCOUNTS_CHANGED_ACTION
某些應(yīng)用需要了解登錄帳戶的更改,以便他們可以為新帳戶和已更改帳戶設(shè)置計(jì)劃操作。
ACTION_ACCOUNT_REMOVED
刪除帳戶后,可以看到帳戶的應(yīng)用會(huì)收到此廣播。如果這是應(yīng)用程序需要執(zhí)行的唯一帳戶更改,則強(qiáng)烈建議應(yīng)用程序使用此廣播 而不是已棄用LOGIN_ACCOUNTS_CHANGED_ACTION。
ACTION_PACKAGE_DATA_CLEARED
僅在用戶明確清除“設(shè)置”中的數(shù)據(jù)時(shí)發(fā)送,因此廣播接收器不太可能顯著影響用戶體驗(yàn)。
ACTION_PACKAGE_FULLY_REMOVED
某些應(yīng)用可能需要在刪除其他包時(shí)更新其存儲(chǔ)的數(shù)據(jù); 對(duì)于這些應(yīng)用程序,注冊(cè)此廣播沒有其他好的選擇。
注意:其他與包相關(guān)的廣播(例如ACTION_PACKAGE_REPLACED)不受新限制的豁免這些廣播很常見,對(duì)豁免它們有潛在的性能影響。
ACTION_NEW_OUTGOING_CALL
響應(yīng)用戶撥打電話而采取措施的應(yīng)用需要接收此廣播。
ACTION_DEVICE_OWNER_CHANGED
這種廣播不經(jīng)常發(fā)送; 一些應(yīng)用需要接收它,以便他們知道設(shè)備的安全狀態(tài)已經(jīng)改變。
ACTION_EVENT_REMINDER
由日歷提供商發(fā)送,以向日歷應(yīng)用發(fā)布活動(dòng)提醒。由于日歷提供程序不知道日歷應(yīng)用程序是什么,因此該廣播必須是隱含的。
ACTION_MEDIA_MOUNTED,ACTION_MEDIA_CHECKING,ACTION_MEDIA_UNMOUNTED,ACTION_MEDIA_EJECT,ACTION_MEDIA_UNMOUNTABLE,ACTION_MEDIA_REMOVED,ACTION_MEDIA_BAD_REMOVAL
這些廣播是由于用戶與設(shè)備的物理交互(安裝或刪除存儲(chǔ)卷)或作為啟動(dòng)初始化的一部分(因?yàn)榭捎镁硪寻惭b)而發(fā)送的,因此它們不常見,通常由用戶控制。
SMS_RECEIVED_ACTION, WAP_PUSH_RECEIVED_ACTION
短信收件人應(yīng)用程序依賴這些廣播