?在網絡通信中,一個IP網絡范圍中最大的IP 地址是被保留作為廣播地址來使用的。比如某個網絡的IP 范圍是192.168.0.XXX,子網掩碼是255.255.255.0,那么這個網絡的廣播地址就是192.168.0.255。廣播數據包會被發送到同一網絡上的所有端口,這樣在該網絡中的每臺主機都將會收到這條廣播。
??????? 為了方便于進行系統級別的消息通知,Android?也引入了一套類似的廣播消息機制。相比于我前面舉出的兩個例子,Android 中的廣播機制會顯得更加的靈活,本章就將對這一機制的方方面面進行詳細的講解。
?
一、?廣播機制簡介
??????? 為什么說Android 中的廣播機制更加靈活呢?這是因為Android 中的每個應用程序都可以對自己感興趣的廣播進行注冊,這樣該程序就只會接收到自己所關心的廣播內容,這些廣播可能是來自于系統的,也可能是來自于其他應用程序的。Android 提供了一套完整的API,允許應用程序自由地發送和接收廣播。發送廣播的方法其實之前稍微有提到過一下,如果你記性好的話可能還會有印象,就是借助我們學過的Intent。而接收廣播的方法則需要引入一個新的概念,廣播接收器(Broadcast Receiver)。
??????? 廣播接收器的具體用法將會在下一節中做介紹,這里我們先來了解一下廣播的類型。Android 中的廣播主要可以分為兩種類型,標準廣播和有序廣播。
?????? 標準廣播(Normal broadcasts)是一種完全異步執行的廣播,在廣播發出之后,所有的廣播接收器幾乎都會在同一時刻接收到這條廣播消息,因此它們之間沒有任何先后順序可言。這種廣播的效率會比較高,但同時也意味著它是無法被截斷的。標準廣播的工作流程如下圖:
?
??????有序廣播(Ordered broadcasts)則是一種同步執行的廣播,在廣播發出之后,同一時刻只會有一個廣播接收器能夠收到這條廣播消息,當這個廣播接收器中的邏輯執行完畢后,廣播才會繼續傳遞。所以此時的廣播接收器是有先后順序的,優先級高的廣播接收器就可以先收到廣播消息,并且前面的廣播接收器還可以截斷正在傳遞的廣播,這樣后面的廣播接收器就無法收到廣播消息了。有序廣播的工作流程如圖:
?
二、接收系統廣播
???????Android 內置了很多系統級別的廣播,我們可以在應用程序中通過監聽這些廣播來得到各種系統的狀態信息。比如手機開機完成后會發出一條廣播,電池的電量發生變化會發出一條廣播,時間或時區發生改變也會發出一條廣播等等。如果想要接收到這些廣播,就需要使用廣播接收器,下面我們就來看一下它的具體用法。
1、 動態注冊監聽網絡變化
???????廣播接收器可以自由地對自己感興趣的廣播進行注冊,這樣當有相應的廣播發出時,廣播接收器就能夠收到該廣播,并在內部處理相應的邏輯。注冊廣播的方式一般有兩種,在代碼中注冊和在AndroidManifest.xml 中注冊,其中前者也被稱為動態注冊,后者也被稱為靜態注冊。
?????? 那么該如何創建一個廣播接收器呢?其實只需要新建一個類,讓它繼承自BroadcastReceiver,并重寫父類的onReceive()方法就行了。這樣當有廣播到來時,onReceive()方法就會得到執行,具體的邏輯就可以在這個方法中處理。
?????? 那我們就先通過動態注冊的方式編寫一個能夠監聽網絡變化的程序,借此學習一下廣播接收器的基本用法吧。新建一個BroadcastTest 項目,然后修改MainActivity 中的代碼,其中重要的代碼如下所示,該部分在onCreate 方法中實現
[java]?view plaincopy
IntentFilter?myintentfilter?=?new?IntentFilter();?? myintentfilter.addAction("time");?? ?????????? MyBroadCast?mybroadcast?=?new?MyBroadCast();?? ?????????? registerReceiver(mybroadcast,myintentfilter);??
第一步:這里先創建一個IntentFilter 實例,并在其addAction 中添加我們要監聽的廣播,這里是"time”,靜態注冊則把這一步在AndroidManifest.xml?中完成;
第二步:創建了一個MyBroadCast 實例,然后調用registerReceiver?方法進行注冊,將MyBroadCast 實例和IntentFilter 實例都傳播出去;
這樣,mybroadcast 就會收到所有值為"time" 的廣播,實現監聽功能。
當然,這里我們新建一個類繼承自BroadcastReceiver ,并重寫 onReceive() 方法:
[java]?view plaincopy
class?MyBroadCast?extends?BroadcastReceiver{?? ????Override?? ????public?void?onReceive(Context?context,?Intent?intent)?{?? ?????????? ????????int?s?=?intent.getIntExtra("key",0);?? ?????????????? ????????TextView?text1?=?(TextView)?findViewById(R.id.text1);?? ????????text1.setText("計時:?"+s+"秒");?????? ????}?? }??
通過這個廣播,我們也可以利用Intent 的方法,實現傳送數據的功能,并對其進行處理。
?
2、靜態注冊實現開機啟動
?????? 動態注冊的廣播接收器可以自由地控制注冊與注銷,在靈活性方面有很大的優勢,但是它也存在著一個缺點,即必須要在程序啟動之后才能接收到廣播,因為注冊的邏輯是寫在onCreate()方法中的。那么有沒有什么辦法可以讓程序在未啟動的情況下就能接收到廣播呢?這就需要使用靜態注冊的方式了。
??????我們準備讓程序接收一條開機廣播,當收到這條廣播時就可以在onReceive()方法里執行相應的邏輯,從而實現開機啟動的功能。新建一個MyReceiver繼承自
BroadcastReceiver,代碼如下所示:
[java]?view plaincopy
public?class?MyReceive?extends?BroadcastReceiver?{?? ?? ????@Override?? ????ublic?void?onReceive(Context?context,?Intent?intent)?{?? ?????????? ????????Log.v("MainActivity",?"我是廣播!!!我來了!!!");??? ????????Toast.makeText(context,?"我是廣播!!我出來了!!!",?0).show();?? ????}?? }??
可以看到,這里不再使用內部類的方式來定義廣播接收器,因為稍后我們需要在AndroidManifest.xml 中將這個廣播接收器的類名注冊進去。在onReceive()方法中,還是簡單地使用Toast 彈出一段提示信息。
然后修改AndroidManifest.xml 文件,代碼如下所示:
[java]?view plaincopy
<receiver??? ?????android:name="cn.com.qiang.sendbroadcast.MyReceive">?? ?????<intent-filter?>?? ?????????<action?android:name="BroadCast"/>?? ?????</intent-filter>?? ?</receiver>??
?????? 終于,<application>標簽內出現了一個新的標簽<receiver>,所有靜態注冊的廣播接收器都是在這里進行注冊的。它的用法其實和<activity>標簽非常相似,首先通過android:name來指定具體注冊哪一個廣播接收器,然后在<intent-filter>標簽里加入想要接收的廣播就行了,由于Android 系統啟動完成后會發出一條值為BroadCast的廣播,因此我們在這里添加了相應的action。
??????另外,監聽系統開機廣播也是需要聲明權限的,可以看到,我們使用<uses-permission>標簽又加入了一條android.permission.RECEIVE_BOOT_COMPLETED 權限。
?
三、發送自定義廣播
???????廣播主要分為兩種類型,標準廣播和有序廣播,在本節中我們就將通過實踐的方式來看下這兩種廣播具體的區別:
1、發送標準廣播
??????我們來修改來修改activity_main.xml 中的代碼,通過按鈕來發送廣播
[java]?view plaincopy
<Button??? ???????android:id="@+id/button1"?? ???????android:layout_width="match_parent"?? ???????android:layout_height="wrap_content"?? ???????android:text="發送廣播"/>??
這里在布局文件中定義了一個按鈕,用于作為發送廣播的觸發點。然后修改MainActivity中的代碼,如下所示:
[java]?view plaincopy
Button?button1?=?(Button)findViewById(R.id.button1);?? button1.setOnClickListener(new?OnClickListener()?{?? ????@Override?? ????public?void?onClick(View?v)?{?? ????????/?TODO?Auto-generated?method?stub?? ????????Intent?intent?=?new?Intent();?? ????????intent.setAction("BroadCast");?? ????????sendBroadcast(intent);?? ????}?? });??
可以看到,我們在按鈕的點擊事件里面加入了發送自定義廣播的邏輯。首先構建出了一個Intent 對象,并把要發送的廣播的值傳入,然后調用了Context 的sendBroadcast()方法將廣播發送出去,這樣所有監聽BroadCast 條廣播的廣播接收器就會收到消息。此時發出去的廣播就是一條標準廣播。
注意:由于廣播是使用Intent 進行傳遞的,因此你還可以在Intent 中攜帶一些數據傳遞給廣播接收器。
?
2、發送有序廣播
?????? 廣播是一種可以跨進程的通信方式,這一點從前面接收系統廣播的時候就可以看出來了。因此在我們應用程序內發出的廣播,其他的應用程序應該也是可以收到的。
??????有序廣播和標準廣播的區別之一是,我們發送廣播時,調用的是sendOrderedBroadcast 方法:
[java]?view plaincopy
sendOrderedBroadcast(intent,?null);??
其中sendOrderedBroadcast()方法接收兩個參數,第一個參數仍然是Intent,第二個參數是一個與權限相關的字符串,這里傳入null 就行了。
???????? 廣播接收器是有先后順序的,而且前面的廣播接收器還可以將廣播截斷,以阻止其繼續傳播。那么該如何設定廣播接收器的先后順序呢?當然是在注冊的時候進行設定的了,修改AndroidManifest.xml 中的代碼,如下所示:
[java]?view plaincopy
<receiver?android:name=".MyBroadcastReceiver">?? ????<intent-filter?android:priority="100"?>?? ????????<action?android:name="com.example.broadcasttest.MY_BROADCAST"/>?? ????</intent-filter>?? </receiver>??
????? 我們通過android:priority 屬性給廣播接收器設置了優先級,優先級比較高的廣播接收器就可以先收到廣播。這里將MyBroadcastReceiver 的優先級設成了100,以保證它一定會在AnotherBroadcastReceiver 之前收到廣播。
????? 既然已經獲得了接收廣播的優先權,那么MyBroadcastReceiver 就可以選擇是否允許廣播繼續傳遞了。修改MyBroadcastReceiver 中的代碼,如下所示:
[java]?view plaincopy
public?class?MyBroadcastReceiver?extends?BroadcastReceiver?{?? ????@Override?? ????public?void?onReceive(Context?context,?Intent?intent)?{?? ????????Toast.makeText(context,?"received?in?MyBroadcastReceive",?? ????????Toast.LENGTH_SHORT).show();?? ????????abortBroadcast();?? ????}?? }??
???? 如果在onReceive()方法中調用了abortBroadcast()方法,就表示將這條廣播截斷,后面的廣播接收器將無法再接收到這條廣播。現在重新運行程序,并點擊一下Send Broadcast 按鈕,你會發現, 只有MyBroadcastReceiver 中的Toast 信息能夠彈出, 說明這條廣播經過MyBroadcastReceiver 之后確實是終止傳遞了。
?????下面我們用一個實例——計時器,將前面學到的活動、服務、廣播綜合在一起進行學習整合,程序實現的功能很簡單,就是在前臺實現計時的功能,并能暫停計時,且繼續計時時,數字會接上暫停前數據繼續跳轉,實例圖如下:
具體代碼如下:
1、MainActivty 端
???? 主要實現廣播的動態注冊,廣播的發送與接收,開啟服務與停止服務的功能,具體代碼如下:
[java]?view plaincopy
package?cn.com.qiang.timeservice;?? ?? import?android.os.Bundle;?? import?android.app.Activity;?? import?android.content.BroadcastReceiver;?? import?android.content.Context;?? import?android.content.Intent;?? import?android.content.IntentFilter;?? import?android.view.View;?? import?android.view.View.OnClickListener;?? import?android.widget.Button;?? import?android.widget.TextView;?? ?? ?? public?class?MainActivity?extends?Activity?{?? ?? ????@Override?? ????protected?void?onCreate(Bundle?savedInstanceState)?{?? ????????super.onCreate(savedInstanceState);?? ????????setContentView(R.layout.activity_main);?? ?? ????????IntentFilter?myintentfilter?=?new?IntentFilter();?? ????????myintentfilter.addAction("time");?? ????????MyBroadCast?mybroadcast?=?new?MyBroadCast();?? ????????registerReceiver(mybroadcast,myintentfilter);?? ?????????? ????????Button?button1?=?(Button)findViewById(R.id.button1);?? ????????button1.setOnClickListener(new?OnClickListener()?{?? ?????????????? ????????????@Override?? ????????????public?void?onClick(View?v)?{?? ?????????????????? ????????????????Intent?startintent?=?new?Intent(MainActivity.this,MyService.class);?? ????????????????startService(startintent);?? ????????????}?? ????????});?? ?????????? ????????Button?button2?=?(Button)findViewById(R.id.button2);?? ????????button2.setOnClickListener(new?OnClickListener()?{?? ?????????????? ????????????@Override?? ????????????public?void?onClick(View?v)?{?? ?????????????????? ????????????????Intent?stopintent?=?new?Intent(MainActivity.this,MyService.class);?? ????????????????stopService(stopintent);?? ????????????????TextView?text1?=?(TextView)?findViewById(R.id.text1);?? ????????????????text1.setText("計時:?"+"0"+"秒");?? ????????????}?? ????????});?? ?????????? ????????Button?button3?=?(Button)findViewById(R.id.button3);?? ????????button3.setOnClickListener(new?OnClickListener()?{???????? ????????????@Override?? ????????????public?void?onClick(View?v)?{?? ?????????????????? ????????????????Intent?intent?=?new?Intent();?? ????????????????intent.setAction("pause");?? ????????????????sendBroadcast(intent);?? ????????????}?? ????????});?? ?????????? ????}?? ?????? ????class?MyBroadCast?extends?BroadcastReceiver{?? ????????@Override?? ????????public?void?onReceive(Context?context,?Intent?intent)?{?? ?????????????? ????????????int?s?=?intent.getIntExtra("key",0);?? ?????????????? ????????????TextView?text1?=?(TextView)?findViewById(R.id.text1);?? ????????????text1.setText("計時:?"+s+"秒");?? ?????????????? ????????}?? ????}?? }??
?
2、MySevice 端
?????? 具體實現計時服務,與廣播的接受與發送,具體代碼如下:
[java]?view plaincopy
package?cn.com.qiang.timeservice;?? ?? import?android.app.Service;?? import?android.content.BroadcastReceiver;?? import?android.content.Context;?? import?android.content.Intent;?? import?android.content.IntentFilter;?? import?android.os.IBinder;?? ?? public?class?MyService?extends?Service?{?? ?? ????@Override?? ????public?IBinder?onBind(Intent?intent)?{?? ?????????? ????????return?null;?? ????}?? ????boolean?flag;?? ????private?Thread?mythread;?? ????private?int?count;?? ?????? ????@Override?? ????public?void?onCreate()?{?? ?????????? ????????super.onCreate();?? ????????IntentFilter?myfilter?=?new?IntentFilter();?? ????????myfilter.addAction("pause");?? ????????Pause?pause?=?new?Pause();?? ????????registerReceiver(pause,myfilter);?? ????}?? ?????? ????@Override?? ????public?void?onStart(Intent?intent,?int?startId)?{?? ?????????? ????????super.onStart(intent,?startId);?? ????????mythread?=?new?MyThread();?? ????????mythread.start();?? ????}?? ?????? ????@Override?? ????public?void?onDestroy()?{?? ?????????? ????????super.onDestroy();?? ????????((MyThread)?mythread).setflag();?? ????}?? ?????? ????class?Pause?extends?BroadcastReceiver{?? ????????@Override?? ????????public?void?onReceive(Context?context,?Intent?intent)?{?? ?????????????? ????????????flag?=?true;?? ????????}?? ????}?? ?????? ????class?MyThread?extends?Thread{?? ?????????? ?? ????????@Override?? ????????public?void?run()?{?? ?????????????? ????????????super.run();?? ????????????flag?=?false;?? ????????????while(!flag){?? ????????????????try?{?? ????????????????????Thread.sleep(1000);?? ????????????????}?catch?(Exception?e)?{?? ?????????????????????? ????????????????}?? ?????????????? ????????????????Intent?intent?=?new?Intent();?? ????????????????intent.setAction("time");?? ????????????????intent.putExtra("key",?++count);?? ????????????????sendBroadcast(intent);?? ?????????????? ????????????}?? ????????}?? ?????????? ????????public?void?setflag(){?? ????????????flag?=?true;?? ????????}?? ????}?? ?? }??
?
3、AndroidManiTest 端就不寫了,該注冊的注冊就可以了;
?
附:在活動中使用Toast
??????? Toast 是Android 系統提供的一種非常好的提醒方式,在程序中可以使用它將一些短小的信息通知給用戶,這些信息會在一段時間后自動消失,并且不會占用任何屏幕空間,我們現在就嘗試一下如何在活動中使用Toast。
??????? 首先需要定義一個彈出Toast 的觸發點,正好界面上有個按鈕,那我們就讓點擊這個按鈕的時候彈出一個Toast 吧。在onCreate()方法中添加代碼:
[java]?view plaincopy
protected?void?onCreate(Bundle?savedInstanceState)?{?? ????super.onCreate(savedInstanceState);?? ????requestWindowFeature(Window.FEATURE_NO_TITLE);?? ????setContentView(R.layout.first_layout);?? ????Button?button1?=?(Button)?findViewById(R.id.button_1);?? ????button1.setOnClickListener(new?OnClickListener()?{?? ????????@Override?? ????????public?void?onClick(View?v)?{?? ????????????Toast.makeText(FirstActivity.this,?"You?clicked?Button?1",?? ????????????????Toast.LENGTH_SHORT).show();?? ????????}?? ????});?? }??
?????? 在活動中,可以通過findViewById()方法獲取到在布局文件中定義的元素,這里我們傳入R.id.button_1,來得到按鈕的實例,這個值是剛才在first_layout.xml 中通過android:id 屬性指定的。findViewById()方法返回的是一個View 對象,我們需要向下轉型將它轉成Button對象。得到了按鈕的實例之后,我們通過調用setOnClickListener()方法為按鈕注冊一個監聽器,點擊按鈕時就會執行監聽器中的onClick()方法。因此,彈出Toast 的功能當然是要在onClick()方法中編寫了。
????? Toast 的用法非常簡單,通過靜態方法makeText()創建出一個Toast 對象,然后調用show()將Toast 顯示出來就可以了。這里需要注意的是,makeText()方法需要傳入三個參數。第一個參數是Context,也就是Toast 要求的上下文,由于活動本身就是一個Context 對象,因此這里直接傳入FirstActivity.this即可。第二個參數是Toast顯示的文本內容,第三個參數是Toast顯示的時長,有兩個內置常量可以選擇Toast.LENGTH_SHORT 和Toast.LENGTH_LONG。
總結
以上是生活随笔為你收集整理的Android 四大组件 —— 广播(广播机制解析)的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。