這是四大組件的第一篇(其他還沒整理好:) ),之前有個習慣,就是把一些筆記記在書上弃甥,但是隨著書越來越多,翻閱的時候比較麻煩汁讼,尤其是一段時間不用之后潘飘,想要翻閱某個知識點太費勁,這里就打算統(tǒng)一整理在一起掉缺,方便查看卜录。
其中有很多內(nèi)容參照了網(wǎng)上的博文,但是時間比較久眶明,忘記出處了艰毒。另外就是參照Android developer的相關(guān)文檔,加上自己的理解搜囱,如果有錯誤丑瞧,歡迎指出啊。
應用場景
同一APP內(nèi)部的同一組件內(nèi)的消息通信(單個或多個線程之間)
同一APP內(nèi)部的不同組件間的消息通信(單個進程)
同一APP內(nèi)部的具有不同進程的多個組件間的消息通信(多個進程)
不同APP組件間的消息通信(多個進程)
Android系統(tǒng)與APP之間的消息通信(系統(tǒng)廣播)
分類
1. 普通廣播
<pre>
public abstract void sendBroadcast (Intent intent, String receiverPermission)
public abstract void sendBroadcast (Intent intent)
</pre>
所有receiver都是無序的蜀肘,各receiver往往同時運行绊汹。相對來說更為高效,但各receiver無法終止廣播或使用其他廣播執(zhí)行的結(jié)果扮宠。
2. 有序廣播
<pre>
public abstract void sendOrderedBroadcast (Intent intent, String receiverPermission)
public abstract void sendOrderedBroadcast (Intent intent,
String receiverPermission,
BroadcastReceiver finalResultReceiver,
Handler scheduler,
int initialCode,
String initialData,
Bundle initialExtras)
</pre>
finalResultReceiver作為最末尾的receiver西乖,可以得到前面一系列receiver的處理結(jié)果。通常應該提供自定義的receiver坛增。
scheduler一般設(shè)為null获雕,說明使用context的主線程。
initialCode一般設(shè)為RESULT_OK收捣,作為resultCode的初始值届案。
initialData一般設(shè)為null。作為resultData的初始值罢艾。
initialExtras一般設(shè)為null楣颠,作為resultExtras的初始值。
通過Context.sendOrderedBroadcast發(fā)送咐蚯,receiver會按照android:priority所指定的優(yōu)先級由大到小依次執(zhí)行(android:priority范圍為-1000~1000童漩,數(shù)值越大優(yōu)先級越高,默認優(yōu)先級為0)仓蛆,由于是有序傳播睁冬,可以實現(xiàn)如下效果:
終止傳播:通過調(diào)用abortBroadcast,之后的receiver將接收不到該廣播
向后繼者傳遞數(shù)據(jù):在onReceive中可以調(diào)用setResult、setResultCode豆拨、setResultData/setResultExtras設(shè)置信息直奋,后繼者可以通過getResultCode、getResultData施禾、getResultExtra來獲取信息脚线。sendOrderedBroadcast中的參數(shù)initialCode、initialData弥搞、initialExtras提供了初始值邮绿。
注意:當靜態(tài)注冊與動態(tài)注冊使用了相同的優(yōu)先級(priority)時,動態(tài)注冊的receiver處于更優(yōu)先的位置攀例。
安全方面的考慮
1. 隱患
其他APP可能會針對性的發(fā)出與當前APP中intent-filter相匹配的廣播船逮,導致當前APP不斷受到廣播并處理。
其他APP可能注冊與當前APP一致的intent-filter用于接收廣播粤铭,從而截獲了廣播的具體信息
2. 措施
如果receiver是用于同一APP內(nèi)部的挖胃,則直接將其exported設(shè)為false
在發(fā)送廣播時,使用含有permission的版本
在動態(tài)注冊receiver時梆惯,使用含有permission的版本
在靜態(tài)注冊receiver時酱鸭,在xml中增加permission字段
在發(fā)送廣播時,可以指定receiver的包名垛吗。通過intent.setPackage(packageName)
3. 更便捷的方式——使用LocalBroadcastManager
獲取單例
<pre>
static LocalBroadcastManager getInstance(Context context);
</pre>注冊
<pre>
void registerReceiver(BroadcastReceiver receiver, IntentFilter filter);
</pre>注銷
<pre>
void unregisterReceiver(BroadcastReceiver receiver);
</pre>發(fā)送廣播
<pre>
boolean sendBroadcast(Intent intent);
</pre>發(fā)送廣播(同步發(fā)送凹髓,會阻塞直至所有相關(guān)receiver執(zhí)行完畢onReceive并返回)
<pre>
void sendBroadcastSync(Intent intent);
</pre>
注冊方式
1. 靜態(tài)注冊
通過xml文件的形式進行注冊,此種方式注冊的receiver會在APP運行期間一直存在怯屉,當APP被kill后就接收不到了蔚舀。此種方式更為常用。
通過靜態(tài)注冊方式注冊的receiver蚀之,其onReceive(Context context蝗敢, Intent intent)中的context為ReceiverRestrictedContext(Context含有的bindService和registerReceiver函數(shù)被禁用)
注意:當BroadcastReceiver作為內(nèi)部類被實現(xiàn),同時又使用了靜態(tài)注冊的方式足删,那么該內(nèi)部類必須聲明為“public static”
注意:從4.0開始,需要至少啟動一次APP后锁右,靜態(tài)注冊的receiver才算注冊完畢
2. 動態(tài)注冊
通過代碼的方式進行注冊失受,BroadcastReceiver可實現(xiàn)為內(nèi)部類或一般的外部類。
一般情況下咏瑟,可以在onResume注冊receiver拂到,在onPause中注銷receiver(少數(shù)情況下,也可以在onCreate中注冊码泞,在onDestory中注銷)
通過動態(tài)注冊方式注冊的receiver兄旬,其onReceive(Context context, Intent intent)中的context為Activity的Context。
3. LocalBroadcastManager的動態(tài)注冊
如果廣播只是在APP內(nèi)部進行收發(fā)领铐,那么更高效和安全的方式是使用LocalBroadcastManager悯森。這種方式只能夠進行動態(tài)注冊。
采用此種方式注冊的receiver绪撵,其onReceive(Context context瓢姻, Intent intent)中的context為Application的Context。
生命周期
receiver的生命周期
receiver對象只有在onReceive的調(diào)用期間是有效的音诈,是“活躍”的幻碱,一旦從onReceive中返回,那么系統(tǒng)將認為該對象已經(jīng)結(jié)束细溅,變?yōu)?strong>“不活躍”狀態(tài)褥傍。
任何需要異步的操作都不應該放在onReceive中,因為當異步操作結(jié)束時喇聊,receiver可能已經(jīng)處于“不活躍”狀態(tài)恍风,不能保證對象是否還存在。
注意:不能夠在onReceive中顯示對話框承疲,可行的替代方案是使用NotificationManager相關(guān)功能邻耕。
注意:不能夠在onReceive中綁定服務(bindService),可行的替代方案是通過startService發(fā)送命令燕鸽。
process的生命周期
正在執(zhí)行receiver中onReceive的代碼的process被認為是“前臺進程”兄世,它會一直保持運行(除非遇到非常極端的內(nèi)存方面的壓力才會被kill,這一般不會出現(xiàn))
一旦從onReceive中返回啊研,receiver變?yōu)椤安换钴S”狀態(tài)御滩,它所在的process的重要性將取決于運行在該process中的其他組件。如果這個process沒有其他組件在運行(比如一個APP党远,用戶近期沒有與之交互過)削解,那么系統(tǒng)將認為這是一個“空process”,會在合適的時機kill掉它沟娱。
產(chǎn)生的問題:如果在onReceive中產(chǎn)生一個thread氛驮,然后返回,整個進程济似、包括新產(chǎn)生的thread矫废,都不認定為“不活躍”的,存在被kill的危機砰蠢。解決的方式是與service相結(jié)合蓖扑,在onReceive中啟動一個Service,讓Service去做具體的工作台舱,由于Service的存在律杠,當前的process的重要性取決于Service的狀態(tài),只要Service執(zhí)行的工作未完成,它就一直是“活躍”的柜去,就不會被kill灰嫉。
其他注意事項
如果希望在receiver中的onReceive中開啟一個Activity,則必須增加FLAG_ACTIVITY_NEW_TASK標記诡蜓。
onReceive必須在10秒鐘內(nèi)執(zhí)行完畢熬甫,否則會產(chǎn)生ANR(Application Not Response)。如確實需要進行耗時的操作蔓罚,可以通過啟動一個Service的方式進行椿肩。
與廣播相關(guān)的Intent的FLAG:
<pre>
FLAG_EXCLUDE_STOPPED_PACKAGES (不再通知process被終止的receiver,默認行為)
FLAG_INCLUDE_STOPPED_PACKAGES (仍然通知process被終止的receiver)
</pre>
從3.1開始豺谈,如果靜態(tài)注冊的APP退出后郑象,不一定能夠收到廣播。
因為3.1開始系統(tǒng)增加了對APP是否處于運行狀態(tài)的跟蹤茬末。在發(fā)送廣播時厂榛,系統(tǒng)默認增加了FLAG_EXCLUDE_STOPPED_PACKAGES的flag,導致即使是靜態(tài)注冊的receiver,當其所在process退出后丽惭,同樣無法接收到廣播击奶。
對于自定義的廣播,可以修改這種行為责掏,使靜態(tài)注冊的receiver在process被結(jié)束后依然可以收到廣播柜砾,方法就是在intent中將FLAG_EXCLUDE_STOPPED_PACKAGES改寫為FLAG_INCLUDE_STOPPED_PACKAGES。
對于系統(tǒng)廣播换衬,則無能為力了痰驱。
常見系統(tǒng)廣播
ACTION_TIME_TICK
當前時間變化,每分鐘廣播一次瞳浦。只能使用Context.registerReceiver()動態(tài)注冊担映,靜態(tài)注冊無效。
值: "android.intent.action.TIME_TICK"
ACTION_TIME_CHANGED
系統(tǒng)時間被設(shè)置叫潦。
值: "android.intent.action.TIME_SET"
ACTION_TIMEZONE_CHANGED
時區(qū)被修改蝇完。帶有extra:time-zone
值: "android.intent.action.TIMEZONE_CHANGED"
ACTION_BOOT_COMPLETED
系統(tǒng)啟動完成〈H铮可用進行一些初始化工作四敞,比如安裝alarm等。
權(quán)限:RECEIVE_BOOT_COMPLETED
值: "android.intent.action.BOOT_COMPLETED"
ACTION_PACKAGE_ADDED
新的APP被安裝拔妥。(新安裝的APP不會受到此廣播)
Data:新APP的包名
可能包含的Extras:
- EXTRA_UID 新APP的UID.
- EXTRA_REPLACING 是否是重裝或升級。如果這個廣播緊跟在ACTION_PACKAGE_REMOVED之后达箍,并且作用的是同一個包没龙,那么這個值為true
值: "android.intent.action.PACKAGE_ADDED"
ACTION_PACKAGE_CHANGED
已安裝的APP被改動,比如禁用或使能了某個組件。
Data: 包名
Extras:
- EXTRA_UID 包的UID
- EXTRA_CHANGED_COMPONENT_NAME_LIST 包含了被修改的組件的類名(或包名本身)
- EXTRA_DONT_KILL_APP 布爾量硬纤,是否覆蓋重啟APP的默認action(待確認)
值: "android.intent.action.PACKAGE_CHANGED"
ACTION_PACKAGE_REMOVED
已安裝的APP被卸載解滓。
Data:被卸載的APP的包名。
Extras:
- EXTRA_UID 被卸載APP的uid
- EXTRA_DATA_REMOVED 如果整個APP(包含代碼和數(shù)據(jù))被卸載筝家,其值被設(shè)為true
- EXTRA_REPLACING 是否是重裝或升級洼裤。如果這個廣播后面緊跟著ACTION_PACKAGE_ADDED之后,并且作用的是同一個包溪王,那么這個值為true
值: "android.intent.action.PACKAGE_REMOVED"
ACTION_PACKAGE_RESTARTED
用戶重啟了這個APP腮鞍,它的所有process被kill,所有與它相關(guān)的運行時狀態(tài)被移除(包括process莹菱、alarm移国、notification等)。被重啟的APP收不到這個廣播道伟。
Data:被重啟APP的包名
Extras:
- EXTRA_UID 被重啟APP的uid
值: "android.intent.action.PACKAGE_RESTARTED"
ACTION_PACKAGE_DATA_CLEARED
用戶清空了APP的數(shù)據(jù)迹缀。這需要發(fā)生在ACTION_PACKAGE_RESTARTED之前。在擦除該APP所有持久化數(shù)據(jù)之后蜜徽,本廣播被發(fā)出祝懂。被清空的APP收不到此廣播。
Data:被清空數(shù)據(jù)的APP的包名
Extras:
- EXTRA_UID APP的uid
值: "android.intent.action.PACKAGE_DATA_CLEARED"
ACTION_UID_REMOVED
UID被從系統(tǒng)移除拘鞋。UID被以EXTRA_UID為鍵存儲在extras中饰抒。
值: "android.intent.action.UID_REMOVED"
ACTION_BATTERY_CHANGED
粘性廣播被聲明為過時的习勤,此處待驗證。
ACTION_POWER_CONNECTED
連接外部電源。
值: "android.intent.action.ACTION_POWER_CONNECTED"
ACTION_POWER_DISCONNECTED
外部電源被移除虾宇。
值: "android.intent.action.ACTION_POWER_DISCONNECTED"