前言:或許有人會疑問,為什么要把這三兄弟,寫在一起,因為:廣播,intent,handler都是Android自己的消息傳遞機制
廣播機制
概述
Android廣播分為兩個方面:廣播發送者和廣播接收者,通常情況下,BroadcastReceiver指的就是廣播接收者(廣播接收器)。廣播作為Android組件間的通信方式,可以使用的場景如下:
1.同一app內部的同一組件內的消息通信(單個或多個線程之間);
2.同一app內部的不同組件之間的消息通信(單個進程);
3.同一app具有多個進程的不同組件之間的消息通信;
4.不同app之間的組件之間消息通信;
5.Android系統在特定情況下與App之間的消息通信。比如,網絡斷開與連接
從實現原理看上,Android中的廣播使用了觀察者模式,基于消息的發布/訂閱事件模型。因此,從實現的角度來看,Android中的廣播將廣播的發送者和接受者極大程度上解耦,使得系統能夠方便集成,更易擴展。具體實現流程要點粗略概括如下:
1.廣播接收者BroadcastReceiver通過Binder機制向AMS(Activity Manager Service)進行注冊;(binder機制和AMS,這里不介紹,后面再詳細講)
2.廣播發送者通過binder機制向AMS發送廣播;
3.AMS查找符合相應條件(IntentFilter/Permission等)的BroadcastReceiver,將廣播發送到BroadcastReceiver(一般情況下是Activity)相應的消息循環隊列中;(這里同樣不講,消息隊列,后面再說)
4.消息循環執行拿到此廣播,回調BroadcastReceiver中的onReceive()方法。
對于不同的廣播類型,以及不同的BroadcastReceiver注冊方式,具體實現上會有不同。但總體流程大致如上。
廣播發送者和廣播接收者分別屬于觀察者模式中的消息發布和訂閱兩端,AMS屬于中間的處理中心。廣播發送者和廣播接收者的執行是異步的,發出去的廣播不會關心有無接收者接收,也不確定接收者到底是何時才能接收到。EventBus和Rxjava就和這個比較相似。
在上文說列舉的廣播機制具體可以使用的場景中,現分析實際應用中的適用性:
第一種情形:同一app內部的同一組件內的消息通信(單個或多個線程之間),實際應用中肯定是不會用到廣播機制的(雖然可以用),無論是使用擴展變量作用域、基于接口的回調還是Handler-post/Handler-Message等方式,都可以直接處理此類問題,若適用廣播機制,顯然有些“殺雞牛刀”的感覺,會顯太“重”;
第二種情形:同一app內部的不同組件之間的消息通信(單個進程),對于此類需求,在有些教復雜的情況下單純的依靠基于接口的回調等方式不好處理,此時可以直接使用EventBus等,相對而言,EventBus由于是針對統一進程,用于處理此類需求非常適合,且輕松解耦。
第三、四、五情形:個人暫時找不出多好的例子。
BroadcastReceiver(廣播接收)
主要講怎么用:
自定義BroadcastReceiver:
自定義廣播接收器需要繼承基類BroadcastReceivre,并實現抽象方法onReceive(context, intent)方法。廣播接收器接收到相應廣播后,會自動回到onReceive(..)方法。默認情況下,廣播接收器也是運行在UI線程,因此,onReceive方法中不能執行太耗時的操作。否則將因此ANR。一般情況下,根據實際業務需求,onReceive方法中都會涉及到與其他組件之間的交互,如發送Notification、啟動service等。
下面代碼片段是一個簡單的廣播接收器的自定義:
BroadcastReceiver注冊:
BroadcastReceiver總體上可以分為兩種注冊類型:靜態注冊和動態注冊。
靜態注冊:
其中,intent-filter由于指定此廣播接收器將用于接收特定的廣播類型。本示例中給出的是用于接收網絡狀態改變或開啟啟動時系統自身所發出的廣播。當此App首次啟動時,系統會自動實例化MyBroadcastReceiver,并注冊到系統中。
動態注冊
動態注冊時,無須在AndroidManifest中注冊組件。直接在代碼中通過調用Context的registerReceiver函數,可以在程序中動態注冊BroadcastReceiver。registerReceiver的定義形式如下:
注:Android中所有與觀察者模式有關的設計中,一旦涉及到register,必定在相應的時機需要unregister。因此,上例在onDestroy()回到中需要unregisterReceiver(mBroadcastReceiver)。
廣播發送及廣播類型:
廣播發送
經常說”發送廣播“和”接收“,表面上看廣播作為Android廣播機制中的實體,實際上這一實體本身是并不是以所謂的”廣播“對象存在的,而是以”意圖“(Intent)去表示。定義廣播的定義過程,實際就是相應廣播”意圖“的定義過程,然后通過廣播發送者將此”意圖“發送出去。被相應的BroadcastReceiver接收后將會回調onReceive()函數。
下段代碼片段顯示的是一個普通廣播的定義過程,并發送出去。其中setAction(..)對應于BroadcastReceiver中的intentFilter中的action。
Intent intent = new Intent();
?intent.setAction(BROADCAST_ACTION);
?intent.putExtra("name", "hello");
?sendBroadcast(intent);
廣播類型
Normal Broadcast:普通廣播
System Broadcast: 系統廣播
Ordered broadcast:有序廣播
Sticky Broadcast:粘性廣播(在 android 5.0/api 21中deprecated,不再推薦使用,相應的還有粘性有序廣播,同樣已經deprecated)
Local Broadcast:App應用內廣播
各種類型的發送方式及其特點
Normal Broadcast:普通廣播
此處將普通廣播界定為:開發者自己定義的intent,以context.sendBroadcast(intent, ...)形式。具體可以使用的方法有:
sendBroadcast(intent)
sendBroadcast(intent, receiverPermission)
sendBroadcastAsUser(intent, userHandler)
sendBroadcastAsUser(intent, userHandler ,receiverPermission)。
普通廣播會被注冊了的相應的intent-filter匹配接收,且順序是無序的。如果發送廣播時有相應的權限要求,BroadCastReceiver如果想要接收此廣播,也需要有相應的權限。(通常情況下,就用的第一種,通過傳一個intent來實現消息傳遞)
System Broadcast: 系統廣播
Android系統中內置了多個系統廣播,只要涉及到手機的基本操作,基本上都會發出相應的系統廣播。如:開啟啟動,網絡狀態改變,拍照,屏幕關閉與開啟,點亮不足等等。每個系統廣播都具有特定的intent-filter,其中主要包括具體的action,系統廣播發出后,將被相應的BroadcastReceiver接收。系統廣播在系統內部當特定事件發生時,有系統自動發出。
Ordered broadcast:有序廣播
有序廣播的有序廣播中的“有序”是針對廣播接收者而言的,指的是發送出去的廣播被BroadcastReceiver按照先后循序接收。有序廣播的定義過程與普通廣播無異,只是其的主要發送方式變為:sendOrderedBroadcast(intent, receiverPermission, ...)。
1.多個當前已經注冊且有效的BroadcastReceiver接收有序廣播時,是按照先后順序接收的,先后順序判定標準遵循為:將當前系統中所有有效的動態注冊和靜態注冊的BroadcastReceiver按照priority屬性值從大到小排序,對于具有相同的priority的動態廣播和靜態廣播,動態廣播會排在前面。
2.先接收的BroadcastReceiver可以對此有序廣播進行截斷,使后面的BroadcastReceiver不再接收到此廣播,也可以對廣播進行修改,使后面的BroadcastReceiver接收到廣播后解析得到錯誤的參數值。當然,一般情況下,不建議對有序廣播進行此類操作,尤其是針對系統中的有序廣播。
Sticky Broadcast:粘性廣播(在 android 5.0/api 21中deprecated,不再推薦使用,相應的還有粘性有序廣播,同樣已經deprecated)。
不再多做總結。
Local Broadcast:App應用內廣播(此處的App應用以App應用進程為界)
由前文闡述可知,Android中的廣播可以跨進程甚至跨App直接通信,且注冊是exported對于有intent-filter的情況下默認值是true,由此將可能出現安全隱患如下:
1.其他App可能會針對性的發出與當前App intent-filter相匹配的廣播,由此導致當前App不斷接收到廣播并處理;
2.其他App可以注冊與當前App一致的intent-filter用于接收廣播,獲取廣播具體信息。
無論哪種情形,這些安全隱患都確實是存在的。由此,最常見的增加安全性的方案是:
1.對于同一App內部發送和接收廣播,將exported屬性人為設置成false,使得非本App內部發出的此廣播不被接收;
2.在廣播發送和接收時,都增加上相應的permission,用于權限驗證;
3.發送廣播時,指定特定廣播接收器所在的包名,具體是通過intent.setPackage(packageName)指定在,這樣此廣播將只會發送到此包中的App內與之相匹配的有效廣播接收器中。
當然,以上這些問題,其實,遇到的情況并不多,不過安全起見,寫上吧。當然,懶一點,也可以不寫。
App應用內廣播可以理解成一種局部廣播的形式,廣播的發送者和接收者都同屬于一個App。實際的業務需求中,App應用內廣播確實可能需要用到。同時,之所以使用應用內廣播時,而不是使用全局廣播的形式,更多的考慮到的是Android廣播機制中的安全性問題。
相比于全局廣播,App應用內廣播優勢體現在:
1.安全性更高;
2.更加高效。
為此,Android v4兼容包中給出了封裝好的LocalBroadcastManager類,用于統一處理App應用內的廣播問題,使用方式上與通常的全局廣播幾乎相同,只是注冊/取消注冊廣播接收器和發送廣播時將主調context變成了LocalBroadcastManager的單一實例。
//registerReceiver(mBroadcastReceiver, intentFilter);
//注冊應用內廣播接收器
localBroadcastManager = LocalBroadcastManager.getInstance(this);
localBroadcastManager.registerReceiver(mBroadcastReceiver, intentFilter);
//unregisterReceiver(mBroadcastReceiver);
//取消注冊應用內廣播接收器
localBroadcastManager.unregisterReceiver(mBroadcastReceiver);
Intent intent =new Intent();
intent.setAction(BROADCAST_ACTION);
intent.putExtra("name", "helllo");
//sendBroadcast(intent);
//發送應用內廣播
localBroadcastManager.sendBroadcast(intent);
不同注冊方式的廣播接收器回調onReceive(context, intent)中的context具體類型
對于靜態注冊的ContextReceiver,回調onReceive(context, intent)中的context具體指的是ReceiverRestrictedContext;
對于全局廣播的動態注冊的ContextReceiver,回調onReceive(context, intent)中的context具體指的是Activity Context;
對于通過LocalBroadcastManager動態注冊的ContextReceiver,回調onReceive(context, intent)中的context具體指的是Application Context。
注:對于LocalBroadcastManager方式發送的應用內廣播,只能通過LocalBroadcastManager動態注冊的ContextReceiver才有可能接收到(靜態注冊或其他方式動態注冊的ContextReceiver是接收不到的)。
ok,廣播,就介紹這么多,如果還有什么疑問,自行百度,筆者,就不多解釋了。
本來想把intent和hadler都寫一起的,后來發現,篇幅太長了,所以,就分隔到下一篇了。