目錄
前言
1.廣播基礎(chǔ)知識
2.廣播實現(xiàn)流程
一贵试、廣播定義及類型
1.廣播定義
2.廣播類型
1)普通廣播
2)有序廣播
3)系統(tǒng)廣播
4)異步廣播
二德谅、廣播接收者体捏。
1.廣播接收者定義
第一步:定義廣播接收者呆馁,繼承BroadcastReceiver染苛,并重寫onReceive()方法鹊漠。
【關(guān)于接收的廣播類型的說明】
第二步:注冊感興趣的廣播Intent主到。
1)動態(tài)注冊
2)靜態(tài)注冊
【關(guān)于優(yōu)先級別的說明】
- 實現(xiàn)方法
- 設(shè)置方法
- 特別說明
【關(guān)于動、靜態(tài)注冊方式特別說明】
2.生命周期躯概。
三登钥、廣播實例。
【附錄】
常見系統(tǒng)廣播
前言
-
基礎(chǔ)知識娶靡。
1.Android廣播分為兩個方面:廣播發(fā)送者和廣播接收者牧牢,通常情況下,BroadcastReceiver指的就是廣播接收者(廣播接收器)姿锭。廣播可以跨進程甚至跨App直接通信塔鳍。
2.你的APP可以接收廣播(如當電話呼入時,或者數(shù)據(jù)網(wǎng)絡(luò)可用時)進行接收并做出響應(yīng)呻此。
3.廣播接收器沒有用戶界面轮纫。然而,它們可以啟動一個Activity或Service服務(wù)來響應(yīng)它們收到的信息焚鲜,或者用NotificationManager 來通知用戶掌唾。通知可以用很多種方式來吸引用戶的注意力──閃動背燈、震動忿磅、播放聲音等糯彬。一般來說是在狀態(tài)欄上放一個持久的圖標,用戶可以打開它并獲取消息贝乎。
-
實現(xiàn)流程。
Android中的廣播將廣播的發(fā)送者和接受者極大程度上解耦叽粹,使得系統(tǒng)能夠方便集成览效,更易擴展。具體實現(xiàn)流程要點粗略概括如下:
1.廣播接收者BroadcastReceiver通過Binder機制向AMS(Activity Manager Service)進行注冊虫几;
2.廣播發(fā)送者通過Binder機制向AMS發(fā)送廣播锤灿;
3.AMS查找符合相應(yīng)條件(IntentFilter/Permission等)的BroadcastReceiver,將廣播發(fā)送到BroadcastReceiver(一般情況下是Activity)相應(yīng)的消息循環(huán)隊列中辆脸;
4.消息循環(huán)執(zhí)行拿到此廣播但校,回調(diào)BroadcastReceiver中的onReceive()方法。
一状囱、廣播定義及類型。
廣播定義:Android系統(tǒng)在運行的過程中倘是,會產(chǎn)生很多系統(tǒng)廣播亭枷,比如開機、電量改變搀崭、收發(fā)短信叨粘、撥打電話、屏幕解鎖。當然我們也可以發(fā)送自定義普通或有序廣播升敲。
廣播類型:
1.普通廣播:通過Context.sendBroadcast(Intent intent)發(fā)送答倡,可以在同一時刻(邏輯上)被所有廣播接收者無需等待的接收到。
優(yōu)點:消息傳遞的效率比較高驴党。
缺點:1.接收者不能修改該廣播瘪撇。2.無法終止廣播Intent的傳播,即無法阻止其他接收者的接收動作鼻弧。
Intent intent = new Intent("One");
intent.putExtra("msg", "這是一條普通廣播");
sendBroadcast(intent);
2.有序廣播:通過Context.sendOrderedBroadcast(intent, receiverPermission)發(fā)送设江,是按照接收者聲明的優(yōu)先級別,被接收者依次接收廣播攘轩。
優(yōu)點:1叉存、廣播可以通過接收者調(diào)用abortBroadcast()方法截斷廣播(被截斷的廣播不能再繼續(xù)傳遞該廣播)。2度帮、接收者能修改處理結(jié)果(比如通過傳遞Bundle)傳遞給下一個接收者(一般情況下歼捏,不建議對有序廣播進行此類操作,尤其是針對系統(tǒng)中的有序廣播)笨篷。
缺點:消息傳遞的效率比普通廣播低瞳秽。
Intent intent2 = new Intent("Two");
intent2.putExtra("msg", "這是一條有序廣播");
sendOrderedBroadcast(intent2, null);
3.系統(tǒng)廣播:只要涉及到手機的基本操作,基本上都會發(fā)出相應(yīng)的系統(tǒng)廣播率翅。如:開機啟動练俐,網(wǎng)絡(luò)狀態(tài)改變,拍照冕臭,屏幕關(guān)閉與開啟腺晾,點亮不足等等。每個系統(tǒng)廣播都具有特定的intent-filter辜贵,其中主要包括具體的action悯蝉,系統(tǒng)廣播發(fā)出后,將被相應(yīng)的BroadcastReceiver接收托慨。系統(tǒng)廣播在系統(tǒng)內(nèi)部當特定事件發(fā)生時鼻由,由系統(tǒng)自動發(fā)出。詳見文章末尾附錄
4.異步廣播:通過mContext.sendStickyBroadcast(intent)發(fā)送厚棵,此廣播會一直滯留(等待)蕉世,以便有人注冊這則廣播消息后能盡快的收到這條廣播。其他功能與sendBroadcast相同婆硬。但是使用sendStickyBroadcast 發(fā)送廣播需要獲得BROADCAST_STICKY permission讨彼,如果沒有這個permission則會拋出異常。
優(yōu)點:廣播先發(fā)送柿祈,接收者后注冊依然能收到廣播哈误。
Intent intent3 = new Intent("Three");
intent3.putExtra("msg", "這是一條異步廣播");
sendStickyBroadcast(intent3);
二哩至、廣播接收者。
- 廣播接收者定義:BroadcastReceiver也就是“廣播接收者”的意思蜜自,顧名思義菩貌,它就是用來接收來自系統(tǒng)和應(yīng)用中的廣播,且可以做出相關(guān)操作重荠。
-
實現(xiàn)方法:
第一步:定義廣播接收者箭阶,繼承BroadcastReceiver,并重寫onReceive()方法戈鲁。
public class MyReceiver01 extends BroadcastReceiver {
@Override public void onReceive(Contextcontext, Intentintent) {
//abortBroadcast();//接到的廣播為有序的廣播則可截斷
String s = intent.getStringExtra("msg");
System.out.println("MyReceiver01收到消息:"+s);
}
}
【關(guān)于接收的廣播類型的說明】
-
接收的廣播為普通廣播:
1仇参、不可以在onReceive里面截斷廣播,否則會打印異常婆殿。
2诈乒、不可以處理廣播。 -
接收的廣播為有序廣播:
1婆芦、可以在onReceive里面通過abortBroadcast()截斷廣播怕磨,使廣播不再傳播。
2消约、可以在onReceive里面接收廣播Intent攜帶的數(shù)據(jù)(String字符串肠鲫、Bundle對象、或者實現(xiàn)Serializable接口或粮、Parcelable接口的Object對象)导饲。
3、可向低優(yōu)先級接收者傳遞新的數(shù)據(jù)氯材,如下代碼
//我是高優(yōu)先級接收者
@Override public void onReceive(Contextcontext, Intentintent) {
Bundle bundle = new Bundle();
bundle.putString("next_receiver", "下一個廣播接收者");
setResultExtras(bundle);
}
//我是低優(yōu)先級接收者
@Override
public void onReceive(Context context, Intent intent) {
Bundle bundle = getResultExtras(true);
String content = bundle.getString("next_receiver");
}
第二步:注冊感興趣的廣播Intent渣锦,注冊方法有兩種:
1、動態(tài)注冊(在onCreate代碼中進行注冊)浓体。
IntentFilter filter = new IntentFilter("感興趣的廣播名泡挺、例如上面發(fā)送的有序廣播:Two");
MyReceiver01 receiver = new MyReceiver01();
intentfilter.setPriority(200);//設(shè)置優(yōu)先級
registerReceiver(receiver, filter);
2辈讶、靜態(tài)注冊(在AndroidManifest.xml配置文件中注冊)命浴。
<!--prioritys設(shè)置優(yōu)先級-->
<receiver android:name=". MyReceiver01">
<intent-filter android:priority="100">
<action android:name="感興趣的廣播名、例如上面發(fā)送的有序廣播:Two"/>
</intent-filter>
</receiver>
【關(guān)于優(yōu)先級別的說明】
** 設(shè)置方法**(如上面注冊代碼所示):
1.靜態(tài)設(shè)置:聲明在靜態(tài)注冊的廣播接收者的intent-filter元素的android:priority屬性中贱除。
2.動態(tài)設(shè)置:調(diào)用IntentFilter對象的setPriority()進行聲明生闲。特別說明
1.靜態(tài)注冊時設(shè)置優(yōu)先級。數(shù)越大優(yōu)先級別越高,取值范圍:-1000到1000月幌。就會先接受到消息碍讯。
2.動態(tài)注冊優(yōu)先級高于任何靜態(tài)注冊。
3.如果動態(tài)注冊和靜態(tài)注冊都有注冊扯躺,則會執(zhí)行兩次onReceive捉兴。
4.同級別的接收順序是隨機的蝎困。
【關(guān)于動、靜態(tài)注冊方式特別說明】
靜態(tài)注冊廣播:又叫常駐型廣播倍啥,當你的應(yīng)用程序關(guān)閉了禾乘,如果有廣播信息來,你寫的廣播接收器同樣的能接受到虽缕,他的注冊方式就是在你的應(yīng)用程序中的AndroidManifast.xml進行訂閱的始藕。
動態(tài)注冊廣播:又叫非常駐型廣播,當應(yīng)用程序結(jié)束了氮趋,廣播自然就沒有了伍派,比如你在Activity中的onCreate或者onResume中訂閱廣播,同時你必須在onDestory或者onPause中取消廣播訂閱剩胁。不然會報異常诉植,這樣你的廣播接收器就一個非常駐型的了。
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//動態(tài)注冊BC2 接收者
IntentFilter intentfilter = new IntentFilter("One");
BC2 bc2 = new BC2();
registerReceiver(bc2, intentfilter);
}//必須在onDestory取消廣播訂閱
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(bc2);
}
-
注意:
1摧冀、動態(tài)注冊的時候使用的是隱式Intent方式倍踪,所以在發(fā)送廣播的時候需要使用隱式Intent去發(fā)送,不然是廣播接收者是接收不到廣播索昂。2建车、靜態(tài)注冊的時候,因為在AndroidMainfest.xml中訂閱的椒惨,所以在發(fā)送廣播的時候使用顯示Intent和隱式Intent都可以
所以以防萬一缤至,我們一般都采用隱式Intent去發(fā)送廣播。3康谆、對于靜態(tài)注冊的ContextReceiver领斥,回調(diào)onReceive(context, intent)中的context具體指的是ReceiverRestrictedContext;
4沃暗、對于動態(tài)注冊的ContextReceiver月洛,回調(diào)onReceive(context, intent)中的context具體指的是Activity Context;
-
生命周期孽锥。
-
它并不像 Activity 一樣復(fù)雜嚼黔,運行原理很簡單如下圖
廣播接收者生命周期只有十秒左右,如果在 onReceive() 內(nèi)做超過十秒內(nèi)的事情惜辑,就會報ANR(Application No Response) 程序無響應(yīng)的錯誤信息唬涧,如果需要完成一項比較耗時的工作 , 應(yīng)該通過發(fā)送 Intent 給 Service, 由Service 來完成 盛撑。
這里不能使用子線程來解決 , 因為 BroadcastReceiver 的生命周期很短 , 子線程可能還沒有結(jié)束BroadcastReceiver 就先結(jié)束了 .BroadcastReceiver 一旦結(jié)束 , 此時 BroadcastReceiver 的所在進程很容易在系統(tǒng)需要內(nèi)存時被優(yōu)先殺死 , 因為它屬于空進程 ( 沒有任何活動組件的進程 ). 如果它的宿主進程被殺死 , 那么正在工作的子線程也會被殺死 . 所以采用子線程來解決是不可靠的
三碎节、廣播實例。
為了幫助大家更好的理解抵卫,以下寫了一個接收系統(tǒng)系統(tǒng)電量廣播并處理的小例子狮荔。
//第一步胎撇,定義廣播接收者
public class BatteryChangedReceiver extends BroadcastReceiver {
private static final String TAG = "BatteryChangedReceiver";
@Override
public void onReceive(Context context, Intent intent) {
int currLevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0); //當前電量
int total = intent.getIntExtra(BatteryManager.EXTRA_SCALE, 1); //總電量
int percent = currLevel * 100 / total;
Log.i(TAG, "battery: " + percent + "%");
}
}
//第二步,注冊廣播接收者
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//動態(tài)注冊電量廣播接收者
IntentFilter intentfilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED;);
BatteryChangedReceiver batteryChangedReceiver = new BatteryChangedReceiver();
registerReceiver(bc2, intentfilter);
}//必須在onDestory取消廣播訂閱
protected void onDestroy() {
super.onDestroy();
unregisterReceiver(batteryChangedReceiver);
}
【附錄】
- 常見系統(tǒng)廣播
//關(guān)閉或打開飛行模式時的廣播
Intent.ACTION_AIRPLANE_M;
//充電狀態(tài)殖氏,或者電池的電量發(fā)生變化;//電池的充電狀態(tài)创坞、電荷級別改變,不能通過組建聲;
Intent.ACTION_BATTERY_CH;
//表示電池電量低
Intent.ACTION_BATTERY_LO;
//表示電池電量充足
Intent.ACTION_BATTERY_OK;
//關(guān)閉或打開飛行模式時的廣播
Intent.ACTION_AIRPLANE_MODE_CHANGED;
//充電狀態(tài)受葛,或者電池的電量發(fā)生變化//電池的充電狀態(tài)题涨、電荷級別改變,不能通過組建聲明接收這個廣播总滩,只有通過Context.registerReceiver()注冊
Intent.ACTION_BATTERY_CHANGED;
//表示電池電量低
Intent.ACTION_BATTERY_LOW;
//表示電池電量充足纲堵,即從電池電量低變化到飽滿時會發(fā)出廣播
Intent.ACTION_BATTERY_OKAY;
//在系統(tǒng)啟動完成后,這個動作被廣播一次(只有一次)闰渔。
Intent.ACTION_BOOT_COMPLETED;
//按下照相時的拍照按鍵(硬件按鍵)時發(fā)出的廣播
Intent.ACTION_CAMERA_BUTTON;
//當屏幕超時進行鎖屏時,當用戶按下電源按鈕,長按或短按(不管有沒跳出話框)席函,進行鎖屏時,android系統(tǒng)都會廣播此Action消息
Intent.ACTION_CLOSE_SYSTEM_DIALOGS;
//設(shè)備當前設(shè)置被改變時發(fā)出的廣播(包括的改變:界面語言,設(shè)備方向冈涧,等茂附,請參考Configuration.java)
Intent.ACTION_CONFIGURATION_CHANGED;
//設(shè)備日期發(fā)生改變時會發(fā)出此廣播
Intent.ACTION_DATE_CHANGED;
//設(shè)備內(nèi)存不足時發(fā)出的廣播,此廣播只能由系統(tǒng)使用,其它APP不可用
Intent.ACTION_DEVICE_STORAGE_LOW;
//設(shè)備內(nèi)存從不足到充足時發(fā)出的廣播,此廣播只能由系統(tǒng)使用督弓,其它APP不可用
Intent.ACTION_DEVICE_STORAGE_OK;
//發(fā)出此廣播的地方frameworks\base\services\java\com\android\server\DockObserver.java
Intent.ACTION_DOCK_EVENT;
//移動APP完成之后营曼,發(fā)出的廣播(移動是指:APP2SD)
Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE;
//正在移動APP時,發(fā)出的廣播(移動是指:APP2SD)
Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE;
//Gtalk已建立連接時發(fā)出的廣播
Intent.ACTION_GTALK_SERVICE_CONNECTED;
//Gtalk已斷開連接時發(fā)出的廣播
Intent.ACTION_GTALK_SERVICE_DISCONNECTED;
//在耳機口上插入耳機時發(fā)出的廣播
Intent.ACTION_HEADSET_PLUG;
//改變輸入法時發(fā)出的廣播
Intent.ACTION_INPUT_METHOD_CHANGED;
//設(shè)備當前區(qū)域設(shè)置已更改時發(fā)出的廣播
Intent.ACTION_LOCALE_CHANGED;
//表示用戶和包管理所承認的低內(nèi)存狀態(tài)通知應(yīng)該開始愚隧。
Intent.ACTION_MANAGE_PACKAGE_STORAGE;
//未正確移除SD卡(正確移除SD卡的方法:設(shè)置--SD卡和設(shè)備內(nèi)存--卸載SD卡)蒂阱,但已把SD卡取出來時發(fā)出的廣播 ,擴展介質(zhì)(擴展卡)已經(jīng)從 SD 卡插槽拔出,但是掛載點 (mount point) 還沒解除 (unmount)
Intent.ACTION_MEDIA_BAD_REMOVAL;
//按下"Media Button" 按鍵時發(fā)出的廣播,假如有"Media Button" 按鍵的話(硬件按鍵)
Intent.ACTION_MEDIA_BUTTON;
//插入外部儲存裝置狂塘,比如SD卡時录煤,系統(tǒng)會檢驗SD卡,此時發(fā)出的廣播?
Intent.ACTION_MEDIA_CHECKING;
//已拔掉外部大容量儲存設(shè)備發(fā)出的廣播(比如SD卡荞胡,或移動硬盤),不管有沒有正確卸載都會發(fā)出此廣播, 用戶想要移除擴展介質(zhì)(拔掉擴展卡)妈踊。
Intent.ACTION_MEDIA_EJECT;
//插入SD卡并且已正確安裝(識別)時發(fā)出的廣播, 擴展介質(zhì)被插入,而且已經(jīng)被掛載泪漂。
Intent.ACTION_MEDIA_MOUNTED;
//拓展介質(zhì)存在廊营,但使用不兼容FS(或為空)的路徑安裝點檢查介質(zhì)包含在Intent.mData領(lǐng)域。
Intent.ACTION_MEDIA_NOFS;
//外部儲存設(shè)備已被移除窖梁,不管有沒正確卸載,都會發(fā)出此廣播赘风, 擴展介質(zhì)被移除夹囚。
Intent.ACTION_MEDIA_REMOVED;
//廣播:已經(jīng)掃描完介質(zhì)的一個目錄
Intent.ACTION_MEDIA_SCANNER_FINISHED;
//請求媒體掃描儀掃描文件并將其添加到媒體數(shù)據(jù)庫纵刘。
Intent.ACTION_MEDIA_SCANNER_SCAN_FILE;
//廣播:開始掃描介質(zhì)的一個目錄
Intent.ACTION_MEDIA_SCANNER_STARTED;
// 廣播:擴展介質(zhì)的掛載被解除 (unmount),因為它已經(jīng)作為 USB 大容量存儲被共享荸哟。
Intent.ACTION_MEDIA_SHARED;
Intent.ACTION_MEDIA_UNMOUNTABLE;//
// 廣播:擴展介質(zhì)存在假哎,但是還沒有被掛載 (mount)
Intent.ACTION_MEDIA_UNMOUNTED
Intent.ACTION_NEW_OUTGOING_CALL;
//成功的安裝APK之后//廣播:設(shè)備上新安裝了一個應(yīng)用程序包瞬捕。//一個新應(yīng)用包已經(jīng)安裝在設(shè)備上,數(shù)據(jù)包括包名(最新安裝的包程序不能接收到這個廣播)
Intent.ACTION_PACKAGE_ADDED;
//一個已存在的應(yīng)用程序包已經(jīng)改變舵抹,包括包名
Intent.ACTION_PACKAGE_CHANGED;
//清除一個應(yīng)用程序的數(shù)據(jù)時發(fā)出的廣播(在設(shè)置--應(yīng)用管理--選中某個應(yīng)用肪虎,之后點清除數(shù)據(jù)時?)//用戶已經(jīng)清除一個包的數(shù)據(jù),包括包名(清除包程序不能接收到這個廣播)
Intent.ACTION_PACKAGE_DATA_CLEARED;
//觸發(fā)一個下載并且完成安裝時發(fā)出的廣播惧蛹,比如在電子市場里下載應(yīng)用扇救?
Intent.ACTION_PACKAGE_INSTALL;
//成功的刪除某個APK之后發(fā)出的廣播, 一個已存在的應(yīng)用程序包已經(jīng)從設(shè)備上移除,包括包名(正在被安裝的包程序不能接收到這個廣播)
Intent.ACTION_PACKAGE_REMOVED;
//替換一個現(xiàn)有的安裝包時發(fā)出的廣播(不管現(xiàn)在安裝的APP比之前的新還是舊香嗓,都會發(fā)出此廣播迅腔?)
Intent.ACTION_PACKAGE_REPLACED;
//用戶重新開始一個包,包的所有進程將被殺死靠娱,所有與其聯(lián)系的運行時間狀態(tài)應(yīng)該被移除沧烈,包括包名(重新開始包程序不能接收到這個廣播)
Intent.ACTION_PACKAGE_RESTARTED;
//插上外部電源時發(fā)出的廣播
Intent.ACTION_POWER_CONNECTED;
//已斷開外部電源連接時發(fā)出的廣播
Intent.ACTION_POWER_DISCONNECTED;
Intent.ACTION_PROVIDER_CHANGED;//
//重啟設(shè)備時的廣播
Intent.ACTION_REBOOT;
//屏幕被關(guān)閉之后的廣播
Intent.ACTION_SCREEN_OFF;
//屏幕被打開之后的廣播
Intent.ACTION_SCREEN_ON;
//關(guān)閉系統(tǒng)時發(fā)出的廣播
Intent.ACTION_SHUTDOWN;
//時區(qū)發(fā)生改變時發(fā)出的廣播
Intent.ACTION_TIMEZONE_CHANGED;
//時間被設(shè)置時發(fā)出的廣播
Intent.ACTION_TIME_CHANGED;
//廣播:當前時間已經(jīng)變化(正常的時間流逝), 當前時間改變像云,每分鐘都發(fā)送锌雀,不能通過組件聲明來接收
,只有通過Context.registerReceiver()方法來注冊
Intent.ACTION_TIME_TICK;
//一個用戶ID已經(jīng)從系統(tǒng)中移除發(fā)出的廣播
Intent.ACTION_UID_REMOVED;
//設(shè)備已進入USB大容量儲存狀態(tài)時發(fā)出的廣播迅诬?
Intent.ACTION_UMS_CONNECTED;
//設(shè)備已從USB大容量儲存狀態(tài)轉(zhuǎn)為正常狀態(tài)時發(fā)出的廣播腋逆?
Intent.ACTION_UMS_DISCONNECTED;
Intent.ACTION_USER_PRESENT;//
//設(shè)備墻紙已改變時發(fā)出的廣播
Intent.ACTION_WALLPAPER_CHANGED;
整理作者:汪博
少壯不努力,老大徒悲傷侈贷。
本文為Android學習規(guī)劃打造闲礼,如有不好的地方請多多指教。