廣播機制
為了進行系統級别的消息通知,Android引入了一套廣播消息機制,就像是我們上國小的時候,每個教室都會有個喇叭,到時間就會播放眼保健操一樣。
廣播有兩種:
- 标準廣播
- 有序廣播
标準廣播是一種異步執行的廣播,廣播發出之後,所有的廣播接收器都可以幾乎在同一時間接收到這條廣播,沒有先後接收的順序,标準廣播也不可以被截斷。
有序廣播是一種同步執行的廣播,廣播接收器接收廣播是有先後順序的,優先級高的接收器可以先接受廣播并且進行截斷,之後的廣播接收器就無法接受到此條廣播,同一時間隻有一個廣播接收器能夠接收這條廣播。
接收廣播
Android内置了許多系統級别的廣播,例如手機開機會發送一條廣播,電量變化會發送一條廣播,wifi連接配接也會發送一條廣播,同時我們也可以自定義廣播,如果我們想要接收到這些廣播就需要使用
廣播接收器
。
廣播接收器
可以對自己感興趣的廣播進行注冊,注冊之後就可以接收該廣播,并且在接收器内部的
onReceive()
方法進行邏輯處理。注冊的方式有兩種:
- 動态注冊:在代碼中通過registerReceiver()
- 靜态注冊:在Manifest檔案中進行注冊
動态注冊:
- 建立一個類繼承BroadcastReceiver
- 重寫父類的onReceive()方法實作相應邏輯處理
- 建立一個IntentFliter并添加action,action就是我們需要監聽的廣播
- 通過registerReceiver()方法進行注冊
靜态注冊:
- 建立一個類繼承BroadcastReceiver
- 重寫父類的onReceive()方法實作相應邏輯處理
- 在AndroidManifest.xml通過标簽在中進行注冊
動态注冊接收系統廣播–wifi開關
package com.xiezhen.justwrite;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.NetworkInfo;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
public class MainActivity extends Activity {
private TestBroadCastReceiver tb;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
wifiListener();
}
private void wifiListener() {
IntentFilter intentFilter = new IntentFilter();
//添加的action就是我們要監聽的廣播,wifi開關的時候,系統都會發送這條廣播。
intentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
tb = new TestBroadCastReceiver();
//通過registerReceiver方法,将我們建立的Receiver以及 InterFilter傳入,完成注冊。
registerReceiver(tb, intentFilter);
}
class TestBroadCastReceiver extends BroadcastReceiver {
//重寫onReceive方法來進行一些接收到廣播後的邏輯處理,對使用者進行提醒。
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(action)) {
NetworkInfo networkInfo = intent.getParcelableExtra(
WifiManager.EXTRA_NETWORK_INFO);
//判斷目前wifi連接配接狀态
NetworkInfo.State state = networkInfo.getState();
if (state == NetworkInfo.State.DISCONNECTED) {
Log.d("xiezhen", "DISCONNECTED");
} else if (state == NetworkInfo.State.CONNECTING) {
Log.d("xiezhen", "CONNECTING");
} else if (state == NetworkInfo.State.CONNECTED) {
Log.d("xiezhen", "CONNECTED");
}
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(tb);
}
}
需要注意的是上面這個例子,我們通路了網絡連接配接狀态,是以需要在AndroidManifest.xml中添加相應的權限
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
。這樣我們就可以在切換wifi開關的時候,在logcat檢視輸出的資訊。
靜态注冊接收系統廣播–開機
package com.xiezhen.justwrite;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
public class StaticReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
Log.d("xiezhen","boot complete");
}
}
<receiver android:name=".StaticReceiver" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
建立一個類繼承BroadcastReceiver,之後在AndroidManifest.xml當中進行注冊,action就是需要監聽的廣播,也是系統開機後會發送的一條廣播。别忘了添加監聽開機廣播的權限
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
在模拟器上測試的話,我們隻需要重新開機模拟器就可以在logcat當中看到boot complete這條資訊。
動态注冊與靜态注冊對比
- 動态注冊的廣播要在onDestroy方法中銷毀
- 可以自由注冊,銷毀廣播,但是程式未啟動時無法監聽廣播
- 靜态注冊的廣播在程式安裝後就自動啟動,監聽廣播
發送自定義廣播
之前我們都是接收系統發送的廣播,現在我們将發送自己定義的廣播,之前我們說了廣播有兩種:标準廣播和有序廣播。
發送标準廣播:
- 定義一個接收器
- 進行注冊(動态靜态都可)
- 通過sendBroadcast方法發送廣播
package com.xiezhen.justwrite;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
public class CustomBroadCastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Log.d("xiezhen", "my custom receiver");
}
}
<receiver android:name=".CustomBroadCastReceiver" >
<intent-filter>
<action android:name="my.custom.receiver" />
</intent-filter>
/receiver>
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View view){
//建構一個Intent,然後将要發送的廣播傳入
Intent intent=new Intent("my.custom.receiver");
//調用sendBroadcast方法發送标準廣播
sendBroadcast(intent);
}
};
}
這樣所有監聽
my.custom.receiver
這條廣播的接收器都可以接收到這條廣播。
發送有序廣播:
我們通過sendBroadcast發送标準廣播。
隻需要将sendBroadcast方法換成sendOrderBroadcast方法即可發送有序廣播,此方法接收兩個參數,第一個參數就是建構廣播的Intent,第二個參數是一個與權限有關的字元串,傳入null即可。
package com.xiezhen.justwrite;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
public class FirstOrderReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
Log.d("xiezhen","first");
//abortBroadcast();
}
}
<receiver android:name=".FirstOrderReceiver" >
<intent-filter>
<action android:name="order.broadcast" />
</intent-filter>
</receiver>
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View view){
//建構一個Intent,然後将要發送的廣播傳入
Intent intent=new Intent("order.broadcast");
//調用sendBroadcast方法發送有序廣播
sendOrderBroadcast(intent,null);
}
};
}
我們重新建立一個項目,然後定義一個廣播接收器,同樣接收
order.broadcast
這條廣播
package com.xiezhen.justwrite;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
public class SecondOrderReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Log.d("xiezhen","second");
}
}
<receiver android:name=".SecondOrderReceiver" >
<intent-filter>
<action android:name="order.broadcast" />
</intent-filter>
</receiver>
FirstOrderReceiver和SecondOrderReceiver兩個廣播接收器都可以接收到我們發送的自定義有序廣播,既然是有序廣播那麼就存在接收的先後順序,我們可以在注冊FirstOrderReceiver的時候,通過android:priority屬性設定優先級:
<receiver android:name=".FirstOrderReceiver" >
<intent-filter android:priority="100">
<action android:name="order.broadcast" />
</intent-filter>
</receiver>
同時在FirstOrderReceiver的
onReceive
方法中,調用
abortBroadcast()
方法對這條廣播進行截斷,這樣就隻有FirstOrderReceiver可以接受到這條廣播,之後的SecondOrderReceiver無法接受到這條廣播。
使用本地廣播
廣播是跨程序的,我們可以發送的廣播可以被其他應用程式接收,我們也可以接受其他應用程式發送的廣播,這些廣播有可能是垃圾廣播,這種跨程序的全局性質的廣播會帶來一系列的安全問題,Android系統提供了一套本地廣播機制用來解決可能的安全性問題:用LocalBroadcastManager對廣播進行管理,提供注冊以及發送廣播的方法,使用LocalBroadcastManager發送的廣播隻能在本應用程式内傳遞,同樣的廣播接收器也隻能接收本應用程式内的廣播。
package com.xiezhen.justwrite;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.NetworkInfo;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;
import android.view.View;
public class MainActivity extends Activity {
private LocalBroadCastReceiver lb;
private LocalBroadcastManager localBroadcastManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
localBroadcastManager = LocalBroadcastManager.getInstance(this);
lb = new LocalBroadCastReceiver();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("local.broadcast");
localBroadcastManager.registerReceiver(lb, intentFilter);
}
public void btnSend(View view) {
Intent intent = new Intent("local.broadcast");
localBroadcastManager.sendBroadcast(intent);
}
class LocalBroadCastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Log.d("xiezhen","local broadcast");
}
}
@Override
protected void onDestroy() {
super.onDestroy();
localBroadcastManager.unregisterReceiver(lb);
}
}
布局檔案:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<Button
android:onClick="btnSend"
android:layout_centerInParent="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="send local broadcast" />
</RelativeLayout>
基本上和我們發送全局廣播的方式一樣,不同的是,通過
LocalBroadcastManager.getInstance(this)
擷取一個執行個體,然後調用
LocalBroadcastManager
的
registerReceiver()
方法濟甯注冊,調用
LocalBroadcastManager
的
sendBroadcast()
方法來發送廣播而已。同樣别忘了在
onDestroy()
方法中通過
localBroadcastManager.unregisterReceiver()
方法登出廣播,
附加
- 本地廣播隻能通過動态注冊的方式進行注冊
- 使用本地廣播不用擔心洩露資料
- 不用擔心接收到垃圾資訊,不用擔心安全隐患
- 比全局廣播更加高效
幾點注意事項
- onReceive()方法中不能進行耗時操作,也不能開線程進行異步操作,因為廣播接收器僅當onReceive這個回調方法執行的時候被認為是活躍的,onReceive方法執行完畢後,接收器是不活躍的,這個時候廣播接收器所在的程序很有可能會被殺死,如果進行異步操作的話,有可能程序已經被殺死,異步操作還沒有結束。可以選擇在onReceive方法中開啟一個服務去進行耗時的操作。
- 在onReceive中彈出一個Dialog可能會報錯
,你可以将Unable to add window [email protected] -- permission denied for this window type
中的context,改成AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(context)
;或者将Dialog設定為系統對話框xxx.this
并且在AndroidManifest.xml中設定權限dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
- 在BroadcastReceiver的onReceive(Context context, Intent intent)方法當中通過context.startActivity來啟動一個Activity的時候,需要設定啟動模式為
,否則會報錯,因為不設定這個NEW_TASK啟動模式的話,就沒有棧來存放啟動的Activity。FLAG_ACTIVITY_NEW_TASK