詳解廣播機制

在一個IP網(wǎng)絡范圍中,最大的ip地址是被保留用作廣播地址來使用的,比如某個IP范圍是192.168.0.xxx,子網(wǎng)掩碼是255.255.255.0,那么這個網(wǎng)絡的廣播地址就是192.168.0.255.廣播數(shù)據(jù)包會被發(fā)送到同一個網(wǎng)絡上的所有端口,這樣在該網(wǎng)絡中的每臺主機都會接收到這條廣播.

1.廣播機制簡介:

發(fā)送廣播方法是之前學的intent 接收廣播的方法需要一個廣播接收器(BroadcastReceiver)

什么是標準廣播和有序廣播?

標準廣播是一種異步廣播,即在廣播發(fā)出后,幾乎所有的廣播接收器都會在同一時刻接收到這條廣播信息,他們之間沒有順序可言,廣播效率比較高,但是不能截斷.

有序廣播是一種同步執(zhí)行的廣播,在廣播發(fā)出后,同一時刻只會有一個廣播接收器接收到這條廣播的消息,當這個廣播接收器中的邏輯執(zhí)行完畢后,廣播才會繼續(xù)傳遞.

2.接收系統(tǒng)廣播

什么是系統(tǒng)廣播?

系統(tǒng)廣播比如手機開機之后會發(fā)出一條廣播,電池電量發(fā)生狀態(tài)變化會發(fā)出一條廣播,時間或時區(qū)發(fā)生改變也會發(fā)出一條廣播.

動態(tài)監(jiān)聽網(wǎng)絡變化

注冊廣播的方式分為動態(tài)注冊和靜態(tài)注冊

靜態(tài)注冊 在AndroidManifest.xml中注冊;動態(tài)注冊 在代碼中注冊.

前面我們說了應用程序發(fā)送廣播和接收廣播分別通過intent 和廣播接收器(BroadcastReceiver),那么如何新建一個廣播接收器呢??

只需要創(chuàng)建一個類,讓他繼承BoradcastReceiver,并重寫B(tài)oradcastReceiver的子方法onReceive()即可.當有廣播來時,onReceive()方法變回執(zhí)行,具體的邏輯可以在這個方法中處理.


public class MainActivity extends AppCompatActivity {


private NetworkChangeReceiver networkChangeReceiver;


private IntentFilter intentFilter;


@Override


protected void onCreate(Bundle savedInstanceState) {


super.onCreate(savedInstanceState);


setContentView(R.layout.activity_main);


IntentFilter intentFilter=new IntentFilter();


?intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");


?NetworkChangeReceiver networkChangeReceiver=new NetworkChangeReceiver(); registerReceiver(networkChangeReceiver,intentFilter); ?}


@Override


?protected void onDestroy(){


?super.onDestroy();


unregisterReceiver(networkChangeReceiver); ?}


class NetworkChangeReceiver extends BroadcastReceiver{


@Override


public void onReceive(Context context, Intent intent) {


// //直接打印Toast


Toast.makeText(context,"network is changed",Toast.LENGTH_SHORT).show();}}


}



首先看我們創(chuàng)建的NetworkChangeReceiver這個廣播接收器類,它繼承了BroadcastReceiver這個類,并重寫了這個類的onReceive方法,并在這個方法中執(zhí)行Toast()操作.

再看onCreat()方法,在這個方法中我們創(chuàng)建了一個IntentFilter實例,并給它添加了一個值為android.net.conn.CONNECTIVITY_CHANGE的action,因為網(wǎng)絡狀態(tài)發(fā)生變化時,系統(tǒng)發(fā)出的正是一條值為android.net.conn.CONNECTIVITY_CHANGE的廣播,也就是說我們的廣播接收器想要監(jiān)聽什么廣播,就在這里添加相應的action。(關(guān)于IntentFilter的用法詳情請見http://blog.csdn.net/today520/article/details/7000048 ,總的來說就是一個過濾器可以認為是intent過濾器)然后再onCreat()方法中創(chuàng)建NetworkChangeReceiver的實例,最后調(diào)用registerReceiver方法進行注冊,將NetworkChangeReceiver的實例IntentFilter實例都傳進去,這樣NetworkChangeReceiver就會接收所有值為android.net.conn.CONNECTIVITY_CHANGE的廣播,也就實現(xiàn)了監(jiān)聽網(wǎng)絡變化的功能.

最后要記得,動態(tài)注冊的廣播接收器一定都要取消注冊才行,這里我們是在。onDestroy()方法中通過調(diào)用unregisterReceiver()方法來實現(xiàn)的。(其中unregisterReceiver()方法中傳入的是我們創(chuàng)建的這個NetworkChangeReceiver這個廣播接收器)

如果我們覺得提醒網(wǎng)絡變化不夠人性化,那么我們可以對代碼進行如下調(diào)整.

public class MainActivity extends AppCompatActivity{

........

class NetworkChangeReceiver extends BroadcastReceiver{

@Override

public void onReceive(Context context,Intent intent){? ?

ConnectivityManager connectionManager==(ConnectivityManager)getSystemService(Context.CONNECTIVITY SERVICE);? ? NetworkInfo networkInfo = connectionManager.getActiveNetworkInfo();? ?

if (networkInfo!== null&&networkInfo.isAvailable()){? ? ? ? ?

Toast.makeText(context, "network is available",Toast .LENGTH_ SHORT).show();

}else{

? ? ? ? ? Toast.makeText(context, "network is unavailable",Toast .LENGTH_ SHORT).show();}

}

}}

需要注意的是:首先通過getSystemService(Context.CONNECTIVITY SERVICE);方法獲得?ConnectivityManager的實例,這個一個系統(tǒng)服務類(專門用于管理網(wǎng)絡連接的,可以通過查其源代碼發(fā)現(xiàn)返回的是Object對象,所以需要對其進行強制轉(zhuǎn)型.),然后通過調(diào)用它的getActiveNetworkInfo();方法可以判斷當前是否有網(wǎng)絡,然后加一個if語句來判斷,判斷條件是其若不為空,且存在則....否則 ?....

Android為了保護用戶的隱私和設備安全,嚴格規(guī)定:如果程序需要進行一些對用戶來說比較敏感的操作,就必須要在配置文件中聲明權(quán)限才可以,否則程序會很容易崩潰.比如這里的訪問系統(tǒng)的網(wǎng)絡狀態(tài)就是需要聲明權(quán)限的.聲明權(quán)限是在AndroidManifest.xml文件.


聲明權(quán)限如圖

靜態(tài)注冊實現(xiàn)開機啟動

動態(tài)注冊廣播接收器具有很大的靈活性,可是必須要在程序啟動后才能接收的廣播,因為注冊邏輯是寫在onCreat()方法中的,靜態(tài)注冊可以實現(xiàn)讓程序在未啟動的情況下就能接收廣播.步驟如下右擊。包--New---Other-Broadcast Receiver,會彈出一個窗口


創(chuàng)建廣播接收器的窗口

可以看到,這里我們將廣播接收器命名為BootCompleteReceiver, Exported屬性表示是否允許這個廣播接收器接收本程序以外的廣播,Enabled屬性表示是否啟用這個廣播接收器。勾選這兩個屬性,點擊Finish完成創(chuàng)建。

然后修改BootCompleteReceiver中的代碼,如下所示:

public class BootCompleteReceiver extends BroadcastReceiver{

@override

public void onReceive(Context context,Intent intent){

Toast.makeText(context, "Boot Complete",Toast.LENGTH_ LONG).show();

}}

另外,靜態(tài)的廣播接收器一定要在AndroidManifest.xml文件中注冊才可以使用,不過由于我們是使用Android Studio的快捷方式創(chuàng)建的廣播接收器,因此注冊這一步已經(jīng)被自動完成了。代開AndroidManifest.xml文件會看到一個<receiver>標簽屬性有name enabled expoorted等

它的用法其實和<activity>標簽非常相似,也是通過android:name來指定具體注冊哪一個廣播接收器,而enabled和exported屬性則是根據(jù)我們剛才勾選的狀態(tài)自動生成的。還要再給AndroidManifest.xml文件添加

2

才能收到開機廣播.

由于Android系統(tǒng)啟動完成后會發(fā)出一條值為android.intent.action.B00T_COMPLETED的廣播,因此我們在<intent-filter>標簽里添加了相應的action。另外,監(jiān)聽系統(tǒng)開機廣播也是需要聲明權(quán)限的,可以看到,我們使用<user-permission>標簽又加人了一條android.permission.RECEIVE_ BOOT COMPLETED權(quán)限.

發(fā)送自定義廣播

上面的內(nèi)容主要是通過廣播接收器來接收系統(tǒng)廣播,接下來我們學習一下如何在應用程序中發(fā)送自定義廣播.

發(fā)送標準廣播

發(fā)送廣播之前首先定義一個廣播接收器來接收次廣播才行,不然發(fā)出去也是白發(fā).新建一個MyBroadcastReceiver.

public class MyBroadcastReceiver extends BroadcastReceiver {

//------------發(fā)送自定義廣播------------發(fā)送標準廣播--------發(fā)送有序廣播----

@Override

public void onReceive(Context context, Intent intent) {

Toast.makeText(context,"received in MyBroadcastReceiver",Toast.LENGTH_SHORT).show();

//-------------發(fā)送有序廣播中的廣播截斷-----------

//在設置了接收廣播的優(yōu)先權(quán)之后,那么MyBroadcastReceiver便可以自己選擇是否允許廣播繼續(xù)傳遞了 //

通過abortBroadcast()方法 這個方法的意思是終止發(fā)送的意思

abortBroadcast();

} }

這里當MyBroadcastReceiver收到自定義廣播時,就會彈出received in MyBroadcastReceiver的提示

在AndroidMinifest.xml文件中修改

? ?

3

讓MyBroadcastReceiver接收一條值為com.exampte.broadcasttest.MY_BROADCAST的廣播,因此待會兒在發(fā)送廣播的時候,我們就需要發(fā)出這樣的一條廣播。


4

首先構(gòu)造了一個Intent對象,并把要發(fā)送的廣播的值傳入,然后調(diào)用了Context的sendBroadcast()方法將廣播發(fā)送出去,這樣所有監(jiān)聽com.example.broadcasttest.MY_BROADCAST這條廣播接收器都會收到消息.

在這里要和IntentFilter對比一下,在之前我們說了用intent發(fā)送消息,用廣播接收器接收接收廣播,IntentFilter是采用addAction()的方法將網(wǎng)絡狀態(tài)變化添加進去,Intent直接將要發(fā)送的廣播的值傳入其構(gòu)造方法中,然后調(diào)用context的sendBroadcast()方法來發(fā)送廣播,這樣所有監(jiān)聽com.example.broadcasttest.MY_BROADCAST這條廣播接收器(例如MyBroadcastReceiver)都會收到消息.

提一個問題:IntentFilter和AndroidMinifest.xml文件中intent-filter標簽有什么關(guān)系???

發(fā)送有序廣播和標準廣播基本上一個套路,只不過在MainActivity中將sendBroadcast(intent)改成了sendOrderBroadcast(intent,null)

想要發(fā)送有序廣播最好還是多建幾個廣播接收器,這樣效果比較好.比如再建一個AnotherBroadcastReceiver,同樣也是重寫onReceive()方法,然后在AndroidMinifest.xml文件中修改<intent-filter>標簽,將標簽中的action的名字指定為com.example.broadcasttest.MY_BROADCAST因為只有將AnotherBroadcastReceiver也接收這條廣播,現(xiàn)在運行BroadcastTest2項目將這個程序安裝到模擬器上,然后重新回到BroadcastTest項目的主界面,并點擊一下Send Broadcast按鈕,就會分別彈出兩次提示信息.

給廣播接收器設定先后順序在AndroidMinifest.xml中的intent-filter標簽中加上 ?android:priorty="100"即可!(android:priorty屬性給廣播接收器設置了優(yōu)先級,優(yōu)先級比較高的廣播接收器可以先收到廣播)-----獲得了接收廣播的優(yōu)先權(quán)之后 因為我們這次寫的代碼中MyBroadcastReceiver的優(yōu)先級要高于AnotherBroadcastReceiver的優(yōu)先級,所以在MyBroadCastReceiver.java中加上aborBroadcasr()方法(該方法的意思是終止發(fā)送廣播即后面一個廣播接收器AnotherBroadcastReceiver將收不到廣播消息即我們所說的廣播截斷)

使用本地廣播

系統(tǒng)全局廣播容易引起安全性問題,因為發(fā)出的廣播可以被任何應用程序接收到,并且我們也可以接受來自于其他任何應用程序的廣播.

本地廣播機制:使用這個廣播機制發(fā)出去的廣播只能夠在應用程序的內(nèi)部進行傳遞,并且廣播接收器也只能接受來自本應用程序發(fā)出的廣播

本地廣播的用法和我們之前說的動態(tài)注冊監(jiān)聽網(wǎng)絡變化差不多,只不過本地廣播用到了一個LocalBroadcastManager來對廣播進行管理,并提供了發(fā)送廣播和接收廣播的方法.

我認為共同的套路都是新建一個廣播接收器(LocalBroadcastReceiver),然后重寫B(tài)roadcastReceiver的onReceive方法

新建一個IntentFilter對象然后用IntentFilter.addaction(要發(fā)送的廣播的值)

IntentFilter和intent LocalBroadcastReceiver都要新建實例

無非就是多了:

1.采用LocalBroadcastManager.getInstance()方法來獲得LocalBroadcastManager的實例

2.在構(gòu)建Intent對象,并把要發(fā)送的廣播的值植入inent對象之后,采用LocalBroadcastManager.sendBroadcast(intent)方法來發(fā)送廣播

3.注冊廣播接收器的時候采用的是LocalBroadcastManager的registerReceiver()方法(方法中的參數(shù)是IntentFilter和localBroadcastReceiver)

4.取消注冊廣播接收器時是LocalBroadcastManager的unregisterReceiver()方法

本地廣播無法通過靜態(tài)注冊的方式來接收!!!要注意!!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內(nèi)容