在這裡 篇文章裡面我將總結廣播接收器(Broadcast Receiver)方面的知識。首先我們來瞭解下andorid中廣播的類型,android中的廣播可以分為2種,標準廣播和有序廣播。
標準廣播:是一種完全異步執行的廣播,在廣播發出後,所有廣播接收器幾乎都會同一時刻接收到這條廣播消息,因此它們之間沒有任何先後順序可言。這種廣播的效率比較高,但同時也意味著它無法被截斷的。
有序廣播:則是一種同步機制的廣播,在廣播發出之後,同一時刻隻會有一個廣播接收器能夠接收到這條廣播信息,當這個廣播接收器中邏輯執行完畢後,廣播才會繼續傳遞。所以此時的廣播接收器是有先後順序的,優先級高的廣播接收器就可以先收到廣播信息,並且前面的廣播接收器還可以截斷正在傳遞的廣播,這樣後面的廣播接收器就無法收到廣播消息瞭。
接收系統廣播
廣播接收器可以自由的對自己感興趣的廣播進行註冊,這樣當有相應的廣播發出時,廣播接收器就能夠收到該廣播,並在內部處理相應的邏輯。註冊廣播的方式一般有2種,在代碼中和在AndroidManifest.xml中註冊,其中前者也被稱為動態註冊,後者也被稱為靜態註冊。
那麼該如何創建一個廣播接收器?其實隻要新建一個類,讓它繼承自BroadcastReceiver,並重寫父類的onReceiver方法就行瞭。這樣當有廣播到來時,onReceiver方法就會得到執行,具體的邏輯處理就可以在這個方法中處理。
下面我們就先通過動態註冊的方式來編寫一個能夠監聽網絡變化的程序。新建一個項目,項目為BroadcastTest。修改MainActivity中的代碼,如下所示:
package com.wj.broadcasttest; 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.Menu; import android.widget.Toast; public class MainActivity extends Activity { private IntentFilter intentFilter; private NetworkChangeReceiver networkChangeReceiver; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); intentFilter=new IntentFilter(); intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE"); networkChangeReceiver=new NetworkChangeReceiver(); registerReceiver(networkChangeReceiver, intentFilter); } @Override protected void onDestroy() { // TODO Auto-generated method stub super.onDestroy(); unregisterReceiver(networkChangeReceiver); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } //繼承BroadcastReceiver,重寫onReceiver方法,進行廣播接收處理。 class NetworkChangeReceiver extends BroadcastReceiver{ @Override public void onReceive(Context context, Intent intent) { // TODO Auto-generated method stub Toast.makeText(context, "network changes", Toast.LENGTH_SHORT).show(); } } }
可以看到,MainActivity中定義瞭一個內部類NetworkChangeReceiver,這個類是繼承自BroadcastReceiver的,並重寫瞭父類的onReceiver方法,這樣每當網絡狀態發送變化時,onReceiver方法就會得到執行,這裡隻是簡單的使用Toast提示瞭一段文本信息。
在onCreate方法中,首先我們創建瞭一個IntentFilter的實例,並給它添加瞭一個值為android.net.conn.CONNECTIVITY_CHANGE的action,為什麼要添加這個值呢?因為當網絡狀態發送變化時,系統發出的正是一條值為android.net.conn.CONNECTIVITY_CHANGE的廣播,也就是說我們的廣播接收器想要監聽的廣播,就在這裡添加相應的action就行瞭。接著創建瞭一個NetworkChangeReceiver實例,然後調用registerReceiver(networkChangeReceiver,
intentFilter);方法進行註冊,將NetworkChangeReceiver實例和IntentFilter實例都傳瞭進去,這樣NetworkChangeReceiver就會收到所有值為android.net.conn.CONNECTIVITY_CHANGE的廣播,也就實現瞭監聽網絡變化的功能。
最後要記得,動態註冊的廣播接收器一定都要取消註冊才行,這裡我們是在onDestroy方法中通過調用unregisterReceiver(networkChangeReceiver)方法來實現的。
首先你會在註冊完成的時候收到一條廣播,然後按下home鍵回到主界面(註意不能按back鍵,否則onDestroy方法會執行),接著按下menu鍵–》System settings—》Data usage進入到數據使用詳情界面,然後嘗試著打開Mobile
Data來啟動和禁用網絡,你就會看到有Toast提醒你網絡發生瞭變化。
上面隻是提醒網絡變化瞭還不夠人性化,最好是能夠準確的告訴用戶當前是有網絡還是沒有網絡,所以我們對onReceiver的代碼進行修改,代碼如下:
//繼承BroadcastReceiver,重寫onReceiver方法,進行廣播接收處理。 class NetworkChangeReceiver extends BroadcastReceiver{ @Override public void onReceive(Context context, Intent intent) { // TODO Auto-generated method stub ConnectivityManager connectivityManager=(ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo networkInfo=connectivityManager.getActiveNetworkInfo(); if(networkInfo!=null&&networkInfo.isAvailable()){ Toast.makeText(context, "network is Available", Toast.LENGTH_LONG).show(); }else{ Toast.makeText(context, "network is unAvailable", Toast.LENGTH_LONG).show(); } } }
在onReceiver中首先通過getSystemService方法得到ConnectivityManager的實例,這是一個系統服務類,專門用於管理網絡連接。讓回調用它的getActiveNetworkInfo方法可以得到NetworkInfo的實例,接著調用NetworkInfo的isAvailable方法,就可以判斷出當前是否有網絡瞭,最後我們通過Toast的方式對用戶進行提示。
android系統為瞭保證應用程序的安全性能做瞭規定,如果程序需要訪問一些系統的關鍵信息,必須在配置文件中聲明權限才可以,否則程序將會崩潰,比如這裡查詢網絡狀態就是需要生命權限的。給程序添加權限,代碼如下:
重寫運行程序,然後按下home鍵—》按下menu鍵—》System settings –》Data usage(沒的Data usage,你就找到手機的網絡設置也一樣),運行程序結果如下:
vcD4KPHA+PGltZyBzcmM9″/uploadfile/Collfiles/20141110/20141110083632105.jpg” alt=”\”>
靜態註冊實現開機啟動
動態註冊的廣播可以自由地控制註冊與註銷,在靈活性方面有很大的優勢,但是存在的缺點是,必須要在程序啟動之後才能接收到廣播,因為註冊的邏輯是寫在onCreate方法中的。如果要程序在未啟動的情況下就能接收到廣播,這就需要使用靜態註冊的方式瞭。
下面準備讓程序接收一條開機廣播,當收到這條廣播時就可以在onReciver方法裡執行相應的邏輯,從而實現開機啟動的功能。新建BootCompleteReceiver,繼承BroadcastReceiver,代碼如下:
package com.wj.broadcasttest; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.widget.Toast; public class BootCompleteReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { // TODO Auto-generated method stub Toast.makeText(context, "Boot Complete", Toast.LENGTH_LONG).show(); } }
在AndroidMainifest.xml中將上面的廣播進行註冊,代碼如下:
application標簽內出現瞭一個新的標簽receiver,所有靜態註冊的廣播接收器都是在這裡進行註冊的。由於android系統啟動完成後會發出一條值為android.intent.action.BOOT_COMPLETED的廣播,依次在這裡添加瞭相應的action。
另外監聽系統開機廣播需要聲明權限,所以使用瞭
把監聽網絡變化的Toast顯示註釋掉,然後關閉模擬器,並重寫啟動,運行程序,將看到Boot Complete彈出來瞭。
註意:不要在onReceiver方法中添加過多的邏輯或者進行任何的耗時操作,因為在廣播接收器中是不允許開啟線程的,當onReceiver方法運行瞭較長時間,而沒有結束時,程序就會報錯。因此廣播接收器更多的是扮演一種打開程序其他組件的角色,比如創建一條狀態通知,或者啟動一個服務等。
發送自定義廣播
發送標準廣播
新建一個MyBroadcastReceiver類繼承BroadcastReceiver,代碼如下所示:
package com.wj.broadcasttest; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.widget.Toast; public class MyBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { // TODO Auto-generated method stub Toast.makeText(context, "received in MyBroadcastReceiver", Toast.LENGTH_SHORT).show(); } }
當MyBroadcastReceiver收到自定義的廣播時,就會彈出received in MyBroadcastReceiver的提示,然後在AndroidManifest.xml中對廣播接收器進行註冊:
MyBroadcastReceiver接收值為com.wj.broadcasttest.MY_BROADCAST的廣播。
修改稿activity_main.xml中的代碼:
修改MainActivity裡面的代碼,代碼如下:
package com.wj.broadcasttest; import android.net.ConnectivityManager; import android.net.NetworkInfo; 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.Menu; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.Toast; public class MainActivity extends Activity { private IntentFilter intentFilter; private NetworkChangeReceiver networkChangeReceiver; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); 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("com.wj.broadcasttest.MY_BROADCAST"); sendBroadcast(intent); } }); intentFilter=new IntentFilter(); intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE"); networkChangeReceiver=new NetworkChangeReceiver(); registerReceiver(networkChangeReceiver, intentFilter); } @Override protected void onDestroy() { // TODO Auto-generated method stub super.onDestroy(); unregisterReceiver(networkChangeReceiver); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } //繼承BroadcastReceiver,重寫onReceiver方法,進行廣播接收處理。 class NetworkChangeReceiver extends BroadcastReceiver{ @Override public void onReceive(Context context, Intent intent) { // TODO Auto-generated method stub ConnectivityManager connectivityManager=(ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo networkInfo=connectivityManager.getActiveNetworkInfo(); if(networkInfo!=null&&networkInfo.isAvailable()){ /*Toast.makeText(context, "network is Available", Toast.LENGTH_LONG).show();*/ }else{ Toast.makeText(context, "network is unAvailable", Toast.LENGTH_LONG).show(); } } } }
在按鈕的點擊事件裡面加入瞭發送自定義廣播的邏輯。首先構建出瞭一個Intent對象,並把要發送的廣播值傳入,然後調用瞭Context的sendBroadcast方法將廣播發送出去。這樣所有監聽com.wj.broadcasttest.MY_BROADCAST這條廣播的廣播接收器就會收到消息。此時發送出去的廣播就是一條標準廣播。運行程序,並點擊send
broadcast按鈕,運行結果如下:
當然廣播發送的時候也可以攜帶一些數據在Intent中進行傳送。
發送有序廣播
廣播是一種跨進程的通信方式,這一點從前面接收系統廣播的時候就可以看出來。因此在我們應用程序內發出的廣播,其他的應用程序也是可以接收到的。為瞭驗證這一點,我們在建立一個BroadcastTest2項目。將項目創建好之後,還需要在這個項目下定義一個廣播接收器,用於接收上面的自定義廣播。
在上面的項目BroadcastTest2中新建AnotherBroadcastReceiver類繼承,BroadcastReceiver。代碼如下:
package com.wj.broadcasttest2; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.widget.Toast; public class AnotherBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { // TODO Auto-generated method stub Toast.makeText(context, "received in AnotherBroadcastReceiver", Toast.LENGTH_SHORT).show(); } }上面的代碼,隻為瞭在收到廣播的時候,彈出一段文本信息。然後在AndroidManifest.xml文件中對上面的廣播進行註冊,代碼如下:
AnotherBroadcastReceiver同樣接收的是com.wj.broadcasttest.MY_BROADCAST這條廣播。
現在運行BroadcastTest2項目將這個程序安裝到模擬器上,然後回到BroadcastTest項目的主界面,並點擊send broadcast按鈕,會彈出下面的信息:
上面的這個小案例,就證明瞭我們應用程序發出的廣播是可以被其他應用程序接收到的。不過到目前為止我們發送的廣播都還是標準廣播,現在我們來嘗試下發送有序廣播。
修改BroadcastTest項目的MainActivity裡面的代碼,代碼如下:
package com.wj.broadcasttest; import android.net.ConnectivityManager; import android.net.NetworkInfo; 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.Menu; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.Toast; public class MainActivity extends Activity { private IntentFilter intentFilter; private NetworkChangeReceiver networkChangeReceiver; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); 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("com.wj.broadcasttest.MY_BROADCAST"); //sendBroadcast(intent); /*將sendBroadcast(intent)方法改成sendOrderedBroadcast(intent, null) * 就可以發送有序廣播瞭。sendOrderedBroadcast(intent, null)方法接收兩個參數 * ,第一個參數是仍然是Intent,第二參數是一個與權限相關的字符串,這裡傳入的是null。 * */ sendOrderedBroadcast(intent, null);//發送有序廣播。 } }); intentFilter=new IntentFilter(); intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE"); networkChangeReceiver=new NetworkChangeReceiver(); registerReceiver(networkChangeReceiver, intentFilter); } @Override protected void onDestroy() { // TODO Auto-generated method stub super.onDestroy(); unregisterReceiver(networkChangeReceiver); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } //繼承BroadcastReceiver,重寫onReceiver方法,進行廣播接收處理。 class NetworkChangeReceiver extends BroadcastReceiver{ @Override public void onReceive(Context context, Intent intent) { // TODO Auto-generated method stub ConnectivityManager connectivityManager=(ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo networkInfo=connectivityManager.getActiveNetworkInfo(); if(networkInfo!=null&&networkInfo.isAvailable()){ /*Toast.makeText(context, "network is Available", Toast.LENGTH_LONG).show();*/ }else{ Toast.makeText(context, "network is unAvailable", Toast.LENGTH_LONG).show(); } } } }sendOrderedBroadcast(intent, null);//發送有序廣播。發送有序廣播,運行程序,點擊send broadcast按鈕,你會發現這2個應用程序仍然都會接收到這條廣播。看上去好像和標準廣播沒什麼區別,不過別忘瞭,這個時候的廣播接收器是有先後順序的,而且前面的廣播接收器還可以將廣播截斷,以阻止其繼續傳播。
那麼該如何設定廣播接收器的先後順序瞭?這個是在註冊的時候進行設定的,代碼如下:
android:priority="100"屬性給廣播接收器設置瞭優先級,優先級比較高廣播接收器就可以先收到廣播。這裡把MyBroadcastReceiver的優先級設置為100,以保證它一定會在AnotherBroadcastReceiver之前收到廣播。
既然已經獲得瞭接收廣播的優先權,那麼MyBroadcastReceiver就可以選擇釋放允許廣播繼續傳遞瞭,修改MyBroadcastReceiver的代碼如下所示:
package com.wj.broadcasttest; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.widget.Toast; public class MyBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { // TODO Auto-generated method stub Toast.makeText(context, "received in MyBroadcastReceiver", Toast.LENGTH_SHORT).show(); abortBroadcast();//該方法攔截瞭有序廣播,後面的廣播就無法在接收到這條廣播瞭 } }在運行程序,這時候就隻有MyBroadcastReceiver能收到廣播瞭,彈出信息提示。
使用本地廣播
前面發送和接收的廣播全部是屬於系統全局廣播,即發出的廣播可以被其他任何的應用程序接收到,並且我們也可以接收到來自於其他任何應用程序的廣播。這樣很容易引起安全問題。為瞭解決廣播的安全問題,android引入瞭一套本地廣播機制,使用這個機制發出的廣播隻能夠在應用程序的內部進行傳遞。並且廣播接收器也隻能接收來自應用程序發出的廣播,這樣所有的安全問題就都不存在瞭。在本地廣播的使用主要是使用瞭LocalBroadcastManager來對廣播進行管理。並提供瞭發送廣播和註冊廣播接收器的方法,下面是具體的實例。修改BroadcastTest項目的MainActivity的代碼。
package com.wj.broadcasttest; import android.net.ConnectivityManager; import android.net.NetworkInfo; 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.support.v4.content.LocalBroadcastManager; import android.view.Menu; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.Toast; public class MainActivity extends Activity { private IntentFilter intentFilter; private NetworkChangeReceiver networkChangeReceiver; private LocalReceiver localReceiver; private LocalBroadcastManager localBroadcastManager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //獲取LocalBroadcastManager的實例 localBroadcastManager=LocalBroadcastManager.getInstance(this); 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("com.wj.broadcasttest.MY_BROADCAST");*/ //sendBroadcast(intent); /*將sendBroadcast(intent)方法改成sendOrderedBroadcast(intent, null) * 就可以發送有序廣播瞭。sendOrderedBroadcast(intent, null)方法接收兩個參數 * ,第一個參數是仍然是Intent,第二參數是一個與權限相關的字符串,這裡傳入的是null。 * */ /*sendOrderedBroadcast(intent, null);//發送有序廣播。*/ //發送本地廣播 Intent intent=new Intent("com.wj.broadcasttest.LOCAL_BROADCAST"); localBroadcastManager.sendBroadcast(intent);//發送本地廣播 } }); /*intentFilter=new IntentFilter(); intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE"); networkChangeReceiver=new NetworkChangeReceiver(); registerReceiver(networkChangeReceiver, intentFilter);*/ //註冊本地廣播 intentFilter=new IntentFilter(); intentFilter.addAction("com.wj.broadcasttest.LOCAL_BROADCAST"); localReceiver=new LocalReceiver(); localBroadcastManager.registerReceiver(localReceiver, intentFilter); } @Override protected void onDestroy() { // TODO Auto-generated method stub super.onDestroy(); /*unregisterReceiver(networkChangeReceiver);*/ localBroadcastManager.unregisterReceiver(localReceiver); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.main, menu); return true; } class LocalReceiver extends BroadcastReceiver{ @Override public void onReceive(Context context, Intent intent) { // TODO Auto-generated method stub Toast.makeText(context, "receiver local broadcast", Toast.LENGTH_SHORT).show(); } } //繼承BroadcastReceiver,重寫onReceiver方法,進行廣播接收處理。 class NetworkChangeReceiver extends BroadcastReceiver{ @Override public void onReceive(Context context, Intent intent) { // TODO Auto-generated method stub ConnectivityManager connectivityManager=(ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo networkInfo=connectivityManager.getActiveNetworkInfo(); if(networkInfo!=null&&networkInfo.isAvailable()){ /*Toast.makeText(context, "network is Available", Toast.LENGTH_LONG).show();*/ }else{ Toast.makeText(context, "network is unAvailable", Toast.LENGTH_LONG).show(); } } } }上面的代碼和前面學的動態註冊廣播以及發送廣播的代碼是一樣的。隻不過現在首先是通LocalBroadcastManager
的getInstance方法得到它的一個實例,然後在註冊廣播接收器的時候調用的是LocalBroadcastManager的registerReceiver方法,在發送廣播的時候調用的是LocalBroadcastManager的sendBroadcast方法。
運行程序,效果如下:
漏洞的問題。
3.發送本地廣播比發送系統全局廣播將會更加高效。
最後註意的是:在廣播接收器中啟動活動,因此一定要給Intent加FLAG_ACTIVITY_NEW_TASK這個標志。最後當在廣播中要彈出一個一個對話框的時候,需要給對話框設置alertDialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT),不然對話框將無法在廣播接收器中彈出。
轉載請註明來至:https://blog.csdn.net/j903829182/article/details/40713385