Andorid 8.0 對廣播的使用做了變更。
當廣播接收器使用靜態注冊方式使用時,除了一些例外,這個接收器接收不到隱式廣播。 注意這個“隱式”是重點。
看了網上幾篇文章,對這個變更理解有誤。錯誤的理解是:8.0后,廣播接收器使用靜態注冊,是無法使用的。
實時並非如此。
先看一個例子:
首先,定義一個簡單的廣播接收器:
public class MyReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Toast.makeText(context,context.toString(),Toast.LENGTH_SHORT).show(); } }
它對接收到廣播的行為就是打印一句話。
第二,我們將他注冊到Manifest文件中。
<receiver android:name=".broadcast.MyReceiver"> <intent-filter> <action android:name="com.demo.recriver"/> </intent-filter> </receiver>
最后,在Activity中發送一個廣播,intent通過設置Action為com.demo.recriver的形式發送隱式廣播。
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Intent intent = Intent intent = new Intent("com.demo.recriver");//隱式intent,發送隱式廣播 sendBroadcast(intent); } }
運行這個demo,發現在8.0以下的手機上,會有Toast顯示,8.0以上的手機不會彈出,說明沒有接收到廣播。
原因在於這個廣播 是“隱式” 發送的,8.0中,靜態注冊的廣播接收者無法接受 隱式 廣播。
為了解決這個問題,有兩個方法:
1 在Activity或其他組件中動態注冊廣播
2 發送顯示廣播
對於1 ,如果想廣播讓接收者工作,必須要在某個Activity或者其他組件中調用registerReceiver()進行注冊,在onDestroy()時還要反注冊,代碼稍顯復雜,而且靜態注冊的廣播接收者仍處於不可用的狀態。不合理。
而后一種方法 ,因為sendBroaccast是自己主動發送的,明顯知道要哪個broadcastReceiver來進行處理,直接發送顯示廣播即可。
具體的代碼如下,將MainActivity做修改:
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Intent intent = new Intent(); intent.setComponent(new ComponentName(MainActivity.this,MyReceiver.class));//顯示指定組件名稱 sendBroadcast(intent); } }
實際上,這種寫法與更常見的以下寫法相同:
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Intent intent = new Intent(MainActivity.this,MyReceiver.class);//顯示指定組件名 sendBroadcast(intent); } }
運行,發現可以彈出Toast,靜態注冊的BroadcastReceiver接收到了廣播。證明靜態注冊是可以接收到廣播的。
順便插一句:
Intent指定action, 這個Intent則為隱式Intent,使用它發送的廣播則為隱式廣播。隱式廣播接收者是通過IntentFilter去查找的。
Intent指定了組件名稱,這個Intent為顯式Intent,用他發送的廣播為顯式廣播。廣播接收者直接就是指定的組件名稱對應的廣播接收者。
猜測顯式Intent不使用IntentFilter去查找組件(Activtiy,Service,BroadcastReceiver),這點讀者有興趣可以驗證是否正確。
再來看一下Intent的setComponent()方法:
Intent設置了組件名稱(比如 new Intent(MainActivity.this,MyReceiver.class);)則通過IntentFilter匹配所需要的action,data,type,category信息會被忽略。
如果剛才的例子做如下改動:
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Intent intent = new Intent(); intent.setAction("com.abcdefg.hijklmn");//指定action--不存在的action intent.setComponent(new ComponentName(MainActivity.this,MyReceiver.class));//同時顯式指定組件名 sendBroadcast(intent); } }
同時指定了隱式的action 以及顯式的組件名稱,action是一個不存在的action。這時仍可以接收到廣播。
雖然通過action不可能匹配到一個廣播接收者,但顯示設置了組件,action就被忽略了。
扯遠了。回到最初的那個話題。8.0以后,靜態注冊的廣播接受者是可以接收到廣播的,只要廣播是通過顯示方式發送的。