使用Android手機(jī)的時(shí)候,我們的手機(jī)管家中經(jīng)常會(huì)出現(xiàn)開(kāi)機(jī)自啟動(dòng)某某app,那么對(duì)于這個(gè)某某APP來(lái)說(shuō),他是怎么知道系統(tǒng)什么時(shí)候開(kāi)機(jī)的呢?還有,系統(tǒng)短信怎么知道收到了短信?以及屏幕點(diǎn)亮與關(guān)閉、應(yīng)用卸載與安裝等等。
這就講到了Android四大組件之一:BroadcastReceiver,翻譯是廣播接收者。意思就是接收廣播用的。他可以接收到系統(tǒng)開(kāi)機(jī)完成的廣播,以及系統(tǒng)電量不足的廣播,以及系統(tǒng)收到短信的廣播,等等。我們收到廣播后就可以做我們想做的事了。現(xiàn)實(shí)中使用廣播時(shí),有發(fā)送廣播的電臺(tái),接收廣播的收音機(jī)以及廣播傳遞的媒介電磁波。而在Android中的廣播機(jī)制與現(xiàn)實(shí)中一樣,發(fā)送廣播的是Broadcast,接收廣播的BroadcastReceiver及廣播之間傳遞數(shù)據(jù)的Intent。
注冊(cè)BroadcastReceiver接收廣播
** 繼承BroadcastReceiver**這是一個(gè)抽象類(lèi),
public abstract class BroadcastReceiver {
-
實(shí)現(xiàn)抽象方法
public abstract void onReceive(Context context, Intent intent);* 當(dāng)收到注冊(cè)的廣播時(shí),onReceive方法會(huì)被調(diào)用。 - context是上下文,Intent就是廣播攜帶的數(shù)據(jù)。
public class MyBroadcastReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();//獲取到收到的廣播的名稱(chēng)
Log.e("hui", "收到的廣播的Action是:"+action); }}
-
注冊(cè)BroadcastReceiver,作為四大組件之一,當(dāng)然需要注冊(cè)。
BroadcastReceiver有兩種注冊(cè)方式: - 靜態(tài)注冊(cè)(在AndroidManifest.xml清單文件中注冊(cè))
- 動(dòng)態(tài)注冊(cè)(在代碼中注冊(cè))
廣播接收者靜態(tài)注冊(cè)方式
當(dāng)我們需要一直接收某種廣播時(shí),可以使用靜態(tài)注冊(cè)方式。
以監(jiān)聽(tīng)手機(jī)打電話為例子。
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>
<receiver android:name=".MyBroadcastReceiver">
<intent-filter>
<action android:name="android.intent.action.NEW_OUTGOING_CALL" />
</intent-filter>
</receiver>
上面的receiver 表示這個(gè)MyBroadcastReceiver是廣播接收者。action表示要監(jiān)聽(tīng)的廣播類(lèi)型,這里的表示開(kāi)機(jī)完成的廣播。 因?yàn)楸O(jiān)聽(tīng)用戶(hù)的電話狀態(tài)屬于侵犯用戶(hù)隱私,所以需要添加android.permission.PROCESS_OUTGOING_CALLS權(quán)限。
下圖是接收打電話廣播:
實(shí)戰(zhàn)開(kāi)機(jī)自啟動(dòng)APP:鏈接
廣播接收者動(dòng)態(tài)注冊(cè)方式
當(dāng)我們不需要一直接收某種廣播時(shí),可以使用動(dòng)態(tài)注冊(cè)廣播接收者的方式。
以監(jiān)聽(tīng)屏幕點(diǎn)亮與關(guān)閉為例子。
public class MainActivity extends Activity {
private MyBroadcastReceiver receiver ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
registerMyReceiver();//在activity創(chuàng)建的時(shí)候進(jìn)行注冊(cè)監(jiān)聽(tīng)
}
private void registerMyReceiver() {
receiver = new MyBroadcastReceiver();
IntentFilter filter = new IntentFilter();//創(chuàng)建IntentFilter對(duì)象
filter.addAction(Intent.ACTION_SCREEN_OFF);//IntentFilter對(duì)象中添加要接收的關(guān)屏廣播
filter.addAction(Intent.ACTION_SCREEN_ON);//添加點(diǎn)亮屏幕廣播
registerReceiver(receiver, filter);
}
private void unRegisterMyReceiver(){
if(receiver != null){
unregisterReceiver(receiver);//反注冊(cè)廣播,也就是注銷(xiāo)廣播接收者,使其不起作用
}
}
}
下圖是接收屏幕點(diǎn)亮與關(guān)閉廣播:
下圖是退出APP的狀況:
可以看到,退出APP后,接收打電話廣播任然起作用,但是接收屏幕點(diǎn)亮與關(guān)閉的廣播卻沒(méi)效果。為什么呢?看下述差異:
實(shí)戰(zhàn)短信驗(yàn)證碼自動(dòng)填入:鏈接在這
廣播接收者靜態(tài)注冊(cè)方式與靜態(tài)注冊(cè)方式差異
- 靜態(tài)注冊(cè) 靜態(tài)注冊(cè)依附于清單文件,只要APP啟動(dòng)過(guò)一次,所靜態(tài)注冊(cè)的廣播就會(huì)生效,無(wú)論當(dāng)前的APP處于停止使用還是正在使用狀態(tài)。只要相應(yīng)的廣播事件發(fā)生,系統(tǒng)就會(huì)遍歷所有的清單文件,通知相應(yīng)的廣播接收者接收廣播,然后調(diào)用廣播接收者的onReceiver方法。
- 動(dòng)態(tài)注冊(cè)動(dòng)態(tài)注冊(cè)方式依賴(lài)于所注冊(cè)的組件,當(dāng)APP關(guān)閉后,組件對(duì)象都不在了動(dòng)態(tài)注冊(cè)的代碼都不存在了,所動(dòng)態(tài)注冊(cè)監(jiān)聽(tīng)的action自然不在生效。
- 靜態(tài)注冊(cè)的廣播傳播速度要遠(yuǎn)遠(yuǎn)慢于動(dòng)態(tài)注冊(cè)的廣播。
對(duì)廣播接收者同時(shí)使用靜態(tài)與動(dòng)態(tài)注冊(cè)
上面例子中MyBroadcastReceiver使用靜態(tài)注冊(cè)監(jiān)聽(tīng)用戶(hù)打電話,使用動(dòng)態(tài)注冊(cè)監(jiān)聽(tīng)用戶(hù)屏幕點(diǎn)亮與關(guān)閉。所以,監(jiān)聽(tīng)到屏幕的開(kāi)關(guān)只有在APP運(yùn)行的狀態(tài)才可以,但是監(jiān)聽(tīng)打電話的狀態(tài)無(wú)論此時(shí)app是否在運(yùn)行,都可以監(jiān)聽(tīng)到。
需要注意:動(dòng)態(tài)注冊(cè)的廣播的優(yōu)先級(jí)大于靜態(tài)注冊(cè)的廣播。至于這個(gè)是為什么呢?額(⊙o⊙)…谷歌寫(xiě)的源代碼的時(shí)候先對(duì)動(dòng)態(tài)廣播進(jìn)行處理然后在對(duì)靜態(tài)廣播進(jìn)行處理。后面我們了解到廣播的優(yōu)先級(jí)后會(huì)實(shí)例證明的。
BroadcastReceiver分類(lèi)
廣播的發(fā)送,可以分為有序廣播、無(wú)序廣播、本地廣播以及sticky廣播。
有序廣播
有序廣播 是一種分先后廣播接收器的廣播,廣播接收者的優(yōu)先級(jí)越高,越先接收廣播。優(yōu)先級(jí)高的廣播先收到廣播,收到廣播后可以修改廣播的內(nèi)容,也可以攔截廣播不讓廣播向下傳遞。就像皇上通知知府每人賞金100兩,知府通知知縣每人賞金100兩,最后才是農(nóng)民知道了賞金的事,一旦知府或者知縣不告訴下級(jí)賞金的的事,那么農(nóng)民就不知道賞金的事了,這就是有序廣播的攔截廣播;當(dāng)然知府或者知縣也可以向下級(jí)通知只有賞金10兩的事,這就是有序廣播的修改廣播內(nèi)容。
無(wú)序廣播
無(wú)序廣播 指所有與之匹配的廣播接收者都能收到廣播,沒(méi)有先后順序,直到?jīng)]有廣播接收者接收廣播為止才會(huì)停止廣播的傳遞。就像皇上貼告示,昭告天下每人賞金100兩銀子一樣,那么所有的農(nóng)民都知道了這件事,沒(méi)有先后之分,當(dāng)農(nóng)民直到了錢(qián)的事之后這件事就算了結(jié)了。
前文講過(guò),有廣播發(fā)送時(shí),系統(tǒng)會(huì)遍歷全部APP的receiver。如果想使得本APP的廣播不被外界的廣播所干擾,可以在receiver節(jié)點(diǎn)添加android:exported="false"屬性 ,這樣系統(tǒng)遍歷全部APP清單文件的廣播接收者時(shí)不會(huì)對(duì)本receiver進(jìn)行判斷及處理。
這個(gè)值為FALSE表示不予其他APP相交互。
本地廣播
與有序和無(wú)序廣播的全局廣播(任何一方發(fā)出廣播本手機(jī)的任何一個(gè)程序都能收到對(duì)應(yīng)的廣播)相比,本地廣播是局部的廣播基于本程序的廣播,其他的程序無(wú)法收到這個(gè)廣播。本地廣播就類(lèi)似當(dāng)?shù)氐闹h單獨(dú)給農(nóng)民發(fā)一兩銀子,只有當(dāng)?shù)厝瞬胖?,其他的人不知道。這個(gè)廣播是API 21的V4包中新增的,用來(lái)保證廣播是獨(dú)家私有的。這種廣播是安全的,外界不會(huì)干擾他,廣播也不會(huì)被其他進(jìn)程所收到。
sticky廣播
sticky粘性的意思。這種廣播一般不會(huì)終止,只要有符合條件的廣播接收者能接收廣播,那么就會(huì)發(fā)送給他廣播。永遠(yuǎn)不會(huì)終止發(fā)送廣播,除非某個(gè)廣播接收者告訴它不要再發(fā)送廣播了。
發(fā)送自定義廣播
實(shí)例演練:創(chuàng)建兩個(gè)廣播接收者:ZhiFuReceiver/ZhiXianReceiver
創(chuàng)建:
public class ZhiXianReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Log.d("hui", "ZhiXianReceiver = " + intent.getStringExtra("qian"));//取出廣播中攜帶的數(shù)據(jù),因?yàn)槲掖鏀?shù)據(jù)的時(shí)候是intent.putExtra("qian", "100");存入的。遵循如何存如何取得原則取數(shù)據(jù)
}}
public class ZhiFuReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Log.d("hui", "ZhiFuReceiver = " + intent.getStringExtra("qian"));
}}
清單文件如下配置:
<receiver android:name=".ZhiFuReceiver">
<intent-filter >
<action android:name="my.broadcast.faqian"/>//自定義的廣播接收者接收的廣播名稱(chēng)
</intent-filter>
</receiver>
<receiver android:name=".ZhiXianReceiver">
<intent-filter >
<action android:name="my.broadcast.faqian"/>
</intent-filter>
</receiver>
發(fā)送無(wú)序廣播
public void sendCustomBroadcast(View view){
Intent intent = new Intent("my.broadcast.faqian");//action是my.broadcast.faqian,清單文件中的action與之一致方可收到廣播
intent.putExtra("qian", "100");//廣播中攜帶的數(shù)據(jù)
sendBroadcast(intent);//發(fā)送無(wú)序廣播
}
雖然這里打印順序有先后但是這個(gè)先后順序是無(wú)意義的,總體來(lái)看還是無(wú)序的。
發(fā)送有序廣播
發(fā)送方式一:
public void sendCustomBroadcast(View view){
Intent intent = new Intent("my.broadcast.faqian");//action是my.broadcast.faqian,清單文件中的action與之一致方可收到廣播
intent.putExtra("qian", "100");//廣播中攜帶的數(shù)據(jù)
/** * sendOrderedBroadcast(Intent intent, String receiverPermission); */ sendOrderedBroadcast(intent, null);//發(fā)送有序廣播
}
清單文件配置
<receiver android:name=".ZhiFuReceiver">
<intent-filter android:priority="100">//設(shè)置優(yōu)先級(jí),為整數(shù),越大優(yōu)先級(jí)越高
<action android:name="my.broadcast.faqian"/>
</intent-filter>
</receiver>
<receiver android:name=".ZhiXianReceiver">
<intent-filter android:priority="200" >
<action android:name="my.broadcast.faqian"/>
</intent-filter>
</receiver>
ZhiXianReceiver優(yōu)先級(jí)大于ZhiFuReceiver優(yōu)先級(jí),故ZhiXianReceiver先收到廣播。
發(fā)送方式二:
sendOrderedBroadcast的另一個(gè)重載方法如下。
sendOrderedBroadcast(
Intent intent,//封裝了action及其他數(shù)據(jù)
String receiverPermission, //廣播接收者需要的權(quán)限
BroadcastReceiver resultReceiver,//
Handler scheduler,
int initialCode,
String initialData,
Bundle initialExtras);
參數(shù)解釋?zhuān)?/p>
- intent 封裝了action及其他數(shù)據(jù)
- receiverPermission, //廣播接收者需要的權(quán)限
- resultReceiver 有序廣播是支持?jǐn)r截的,一旦被攔截可以修改廣播中數(shù)據(jù)甚至直接終止廣播,這個(gè)resultReceiver表示無(wú)論當(dāng)廣播傳播結(jié)束以后我任然會(huì)受到廣播。(下面會(huì)有栗子演示)
- initialCode 發(fā)送廣播的時(shí)候默認(rèn)攜帶的數(shù)據(jù)
- initialData 發(fā)送廣播的時(shí)候默認(rèn)攜帶的數(shù)據(jù)
- initialExtras 發(fā)送廣播的時(shí)候默認(rèn)攜帶的數(shù)據(jù) 實(shí)例:將上面例子中的發(fā)送廣播的方法修改如下
public void sendCustomBroadcast(View view){
Intent intent = new Intent("my.broadcast.faqian");//action是my.broadcast.faqian,清單文件中的action與之一致方可收到廣播
Bundle bundle = new Bundle();
bundle.putString("qian","100");//廣播中攜帶的bundle數(shù)據(jù)
sendOrderedBroadcast(
intent,
null, //permission為null
new ZhiFuReceiver(), //這里的new ZhiFuReceiver()為最終的廣播接收者,也就是說(shuō)無(wú)論他曾經(jīng)有沒(méi)有收到廣播都會(huì)再次收到廣播。
null,
666,//initCode
"我是initialData",//initData
bundle);//bundle //以上所有入?yún)⒍紩?huì)攜帶在廣播中,如何取出呢?
}
接收廣播
public class ZhiXianReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
/***********獲取數(shù)據(jù)*************/
int initCode = getResultCode();//獲取傳遞過(guò)來(lái)的initCode
String initData = getResultData();//獲取傳遞過(guò)來(lái)的initData
Bundle initBundle = getResultExtras(true);//獲取傳遞過(guò)來(lái)的Bundle
Log.d("hui", "ZhiXianReceiver = " +"initCode = "+initCode +" ,initdata = " +initData +" ,bundle = " +initBundle.getString("qian"));
}}
public class ZhiFuReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
/***********獲取數(shù)據(jù)*************/
int initCode = getResultCode();//獲取傳遞過(guò)來(lái)的initCode
String initData = getResultData();//獲取傳遞過(guò)來(lái)的initData
Bundle initBundle = getResultExtras(true);//獲取傳遞過(guò)來(lái)的Bundle
Log.d("hui", "ZhiFuReceiver = " +"initCode = "+initCode +" ,initdata = " +initData +" ,bundle = " +initBundle.getString("qian"));
}}
結(jié)果:
ZhiXianReceiver = initCode = 666 ,initdata = 我是initialData ,bundle = 100ZhiFuReceiver = initCode = 666 ,initdata = 我是initialData ,bundle = 100ZhiFuReceiver = initCode = 666 ,initdata = 我是initialData ,bundle = 100```

有一點(diǎn)需要說(shuō)明,這里ZhiFuReceiver 收到了兩次數(shù)據(jù)。為什么呢?ZhiXianReceiver 得優(yōu)先級(jí)大于ZhiFuReceiver ,同時(shí)ZhiXianReceiver 未攔截廣播,所以會(huì)先ZhiXianReceiver 一次后ZhiFuReceiver 一次,而發(fā)送廣播的時(shí)候聲明了ZhiFuReceiver 為最終接受者,所以無(wú)論他曾經(jīng)有沒(méi)有收到廣播都會(huì)再次收到廣播。
圖示:
下面我們看看攔截后會(huì)有什么效果。
###有序廣播的攔截與修改數(shù)據(jù)####
攔截廣播將上面例子中的ZhiXianReceiver 添加一行攔截廣播的代碼,看看結(jié)果。
public class ZhiXianReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
/***********獲取數(shù)據(jù)*************/
int initCode = getResultCode();//獲取傳遞過(guò)來(lái)的initCode
String initData = getResultData();//獲取傳遞過(guò)來(lái)的initData
Bundle initBundle = getResultExtras(true);//獲取傳遞過(guò)來(lái)的Bundle
Log.d("hui", "ZhiXianReceiver = " +"initCode = "+initCode +" ,initdata = " +initData +" ,bundle = " +initBundle.getString("qian"));
abortBroadcast();//攔截廣播,廣播被終止,以后不會(huì)有其他廣播接收者再收到廣播了。
}}

這里abortBroadcast()攔截了有序廣播,不是說(shuō)每人能再收到廣播了么?為什么ZhiFuReceiver 還能收到廣播呢?這是因?yàn)閆hiFuReceiver 是廣播的最終接受者,廣播從優(yōu)先級(jí)高的廣播接收者優(yōu)先接收,一層一層向優(yōu)先級(jí)較低的傳送。當(dāng)廣播被攔截后,廣播部分的層層發(fā)送這里鏈路發(fā)送完畢,但是有最終廣播接收者,故最終廣播接收者會(huì)收到最后的廣播。故ZhiFuReceiver 會(huì)收到廣播。
下圖理解:
####修改廣播中內(nèi)容
public class ZhiXianReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
/***********獲取數(shù)據(jù)*************/
int initCode = getResultCode();//獲取傳遞過(guò)來(lái)的initCode
String initData = getResultData();//獲取傳遞過(guò)來(lái)的initData
Bundle initBundle = getResultExtras(true);//獲取傳遞過(guò)來(lái)的Bundle
Log.d("hui", "ZhiXianReceiver = " +"initCode = "+initCode +" ,initdata = " +initData +" ,bundle = " +initBundle.getString("qian"));
/**************修改數(shù)據(jù)****************/
setResultCode(8989);//修改initCode
setResultData("ZhiXianReceiver修改了數(shù)據(jù)"); //修改initData
//修改bundle數(shù)據(jù)
Bundle bundle = new Bundle();
bundle.putString("qian", "10");
setResultExtras(bundle); }}

上面例子中我把幾個(gè)廣播接收者都寫(xiě)在一個(gè)APP中了,如果把每個(gè)廣播接收者分別放在不同的app中一樣都能收到廣播(如果廣播不被攔截)。如果我只想發(fā)送的廣播給我自己APP種的廣播接收到,可以使用本地廣播,這種廣播是安全的,外界不會(huì)干擾他,廣播也不會(huì)被其他進(jìn)程所收到。
###發(fā)送本地廣播
本地廣播的使用是寫(xiě)在代碼中的,因?yàn)楸镜貜V播發(fā)送廣播時(shí)是直接在代碼中注冊(cè)的廣播中進(jìn)行匹配從而調(diào)用其onReceiver的。
簡(jiǎn)單看下源碼:
public void sendBroadcastSync(Intent intent) {
if (sendBroadcast(intent)) {
executePendingBroadcasts();
} }
private void executePendingBroadcasts() {
while (true) {
BroadcastRecord[] brs = null;
synchronized (mReceivers) {
final int N = mPendingBroadcasts.size();
if (N <= 0) {
return;
}
brs = new BroadcastRecord[N];
mPendingBroadcasts.toArray(brs);
mPendingBroadcasts.clear();
}
for (int i=0; i<brs.length; i++) {
BroadcastRecord br = brs[i];
for (int j=0; j<br.receivers.size(); j++) {
//在這里直接調(diào)用其onReceiver方法了
br.receivers.get(j).receiver.onReceive(mAppContext, br.intent);
}
}
}
}
使用localBroadcastManager.registerReceiver( myBroadCastReceiver, intentFilter)注冊(cè):
/** * 本地廣播接收者進(jìn)行注冊(cè),必須在代碼中注冊(cè),清單文件注冊(cè)是無(wú)效的 */
public void registerMyAPPReceiver(View view) { //創(chuàng)建廣播接收者
MyBroadCastReceiver myBroadCastReceiver = new MyBroadCastReceiver();
MyBroadcastReceiver2 myBroadCastReceiver2 = new MyBroadcastReceiver2(); //封裝要接收的廣播類(lèi)型
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("my.broadcast.faqian2"); //拿到LocalBroadcastManager對(duì)象,對(duì)固定的Receiver進(jìn)行注冊(cè),成為本地廣播接收者
LocalBroadcastManager localBroadcastManager =LocalBroadcastManager.getInstance(MainActivity.this);
localBroadcastManager.registerReceiver( myBroadCastReceiver, intentFilter);
localBroadcastManager.registerReceiver(myBroadCastReceiver2, intentFilter); }
>注意:
>1. registerReceiver注冊(cè)一個(gè)廣播接收者可以多次執(zhí)行,比如:我把‘ocalBroadcastManager.registerReceiver( myBroadCastReceiver, intentFilter);’寫(xiě)兩遍,那么myBroadCastReceiver的onReceiver會(huì)被調(diào)用兩次,不建議這樣寫(xiě)。
>2. 本地廣播不能攔截
>3. registerReceiver對(duì)應(yīng)的還有unregisterReceiver(receiver)
/** * 發(fā)送本地廣播 * @param view */
public void sendMyAPPBroadcat(View view){
Intent intent = new Intent("my.broadcast.faqian2");//action是my.broadcast.faqian,清單文件中的action與之一致方可收到廣播
Bundle bundle = new Bundle();
bundle.putString("qian","100");//廣播中攜帶的bundle數(shù)據(jù)
intent.putExtra("bundle_data", bundle); //使用LocalBroadcastManager發(fā)送廣播
LocalBroadcastManager.getInstance(MainActivity.this).sendBroadcastSync(intent);//發(fā)送
}
### 發(fā)送sticky廣播
添加權(quán)限:```<user-permission android:name="android.permission.BROADCAST_STICKY"/>```
發(fā)送```context.sendStickyBroadcast()```
停止使用```context.removeStickyBroadcast()```
如有錯(cuò)誤,不吝賜教啊
累死了,北京時(shí)間:2016.12.5 凌晨 0:27