Android總結篇系列:Android廣播機制

1.Android廣播機制概述html

Android廣播分爲兩個方面:廣播發送者和廣播接收者,一般狀況下,BroadcastReceiver指的就是廣播接收者(廣播接收器)。廣播做爲Android組件間的通訊方式,可使用的場景以下:
1.同一app內部的同一組件內的消息通訊(單個或多個線程之間);android

2.同一app內部的不一樣組件之間的消息通訊(單個進程);api

3.同一app具備多個進程的不一樣組件之間的消息通訊;安全

4.不一樣app之間的組件之間消息通訊;網絡

5.Android系統在特定狀況下與App之間的消息通訊。併發

從實現原理看上,Android中的廣播使用了觀察者模式,基於消息的發佈/訂閱事件模型。所以,從實現的角度來看,Android中的廣播將廣播的發送者和接受者極大程度上解耦,使得系統可以方便集成,更易擴展。具體實現流程要點粗略歸納以下:app

1.廣播接收者BroadcastReceiver經過Binder機制向AMS(Activity Manager Service)進行註冊;異步

2.廣播發送者經過binder機制向AMS發送廣播;ide

3.AMS查找符合相應條件(IntentFilter/Permission等)的BroadcastReceiver,將廣播發送到BroadcastReceiver(通常狀況下是Activity)相應的消息循環隊列中;函數

4.消息循環執行拿到此廣播,回調BroadcastReceiver中的onReceive()方法。

 對於不一樣的廣播類型,以及不一樣的BroadcastReceiver註冊方式,具體實現上會有不一樣。但整體流程大體如上。

由此看來,廣播發送者和廣播接收者分別屬於觀察者模式中的消息發佈和訂閱兩端,AMS屬於中間的處理中心。廣播發送者和廣播接收者的執行是異步的,發出去的廣播不會關心有無接收者接收,也不肯定接收者究竟是什麼時候才能接收到。顯然,總體流程與EventBus很是相似。

在上文說列舉的廣播機制具體可使用的場景中,現分析實際應用中的適用性

第一種情形:同一app內部的同一組件內的消息通訊(單個或多個線程之間),實際應用中確定是不會用到廣播機制的(雖然能夠用),不管是使用擴展變量做用域、基於接口的回調仍是Handler-post/Handler-Message等方式,均可以直接處理此類問題,若適用廣播機制,顯然有些「殺雞牛刀」的感受,會顯太「重」;

第二種情形:同一app內部的不一樣組件之間的消息通訊(單個進程),對於此類需求,在有些教複雜的狀況下單純的依靠基於接口的回調等方式很差處理,此時能夠直接使用EventBus等,相對而言,EventBus因爲是針對統一進程,用於處理此類需求很是適合,且輕鬆解耦。能夠參見文件《Android各組件/控件間通訊利器之EventBus》。

第3、4、五情形:因爲涉及不一樣進程間的消息通訊,此時根據實際業務使用廣播機制會顯得很是適宜。下面主要針對Android廣播中的具體知識點進行總結。

 

2.BroadcastReceiver

自定義BroadcastReceiver

自定義廣播接收器須要繼承基類BroadcastReceivre,並實現抽象方法onReceive(context, intent)方法。廣播接收器接收到相應廣播後,會自動回到onReceive(..)方法。默認狀況下,廣播接收器也是運行在UI線程,所以,onReceive方法中不能執行太耗時的操做。不然將所以ANR。通常狀況下,根據實際業務需求,onReceive方法中都會涉及到與其餘組件之間的交互,如發送Notification、啓動service等。
下面代碼片斷是一個簡單的廣播接收器的自定義:

 1 public class MyBroadcastReceiver extends BroadcastReceiver {  2     public static final String TAG = "MyBroadcastReceiver";  3     public static int m = 1;  4 
 5  @Override  6     public void onReceive(Context context, Intent intent) {  7         Log.w(TAG, "intent:" + intent);  8         String name = intent.getStringExtra("name");  9         Log.w(TAG, "name:" + name + " m=" + m); 10         m++; 11         
12         Bundle bundle = intent.getExtras(); 13         
14  } 15 }

 

BroadcastReceiver註冊類型

BroadcastReceiver整體上能夠分爲兩種註冊類型:靜態註冊和動態註冊。

1).靜態註冊:
直接在AndroidManifest.xml文件中進行註冊。規則以下:

<receiver android:enabled=["true" | "false"] android:exported=["true" | "false"] android:icon="drawable resource" android:label="string resource" android:name="string" android:permission="string" android:process="string" > . . . </receiver>

其中,須要注意的屬性
android:exported  ——此broadcastReceiver可否接收其餘App的發出的廣播,這個屬性默認值有點意思,其默認值是由receiver中有無intent-filter決定的,若是有intent-filter,默認值爲true,不然爲false。(一樣的,activity/service中的此屬性默認值同樣遵循此規則)同時,須要注意的是,這個值的設定是以application或者application user id爲界的,而非進程爲界(一個應用中可能含有多個進程);
android:name  —— 此broadcastReceiver類名;
android:permission  ——若是設置,具備相應權限的廣播發送方發送的廣播才能被此broadcastReceiver所接收;
android:process  ——broadcastReceiver運行所處的進程。默認爲app的進程。能夠指定獨立的進程(Android四大基本組件均可以經過此屬性指定本身的獨立進程)

常見的註冊形式有:

<receiver android:name=".MyBroadcastReceiver" >
    <intent-filter>
        <action android:name="android.net.conn.CONNECTIVITY_CHANGE" />
    </intent-filter>
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED" />
    </intent-filter>
</receiver>

其中,intent-filter因爲指定此廣播接收器將用於接收特定的廣播類型。本示例中給出的是用於接收網絡狀態改變或開啓啓動時系統自身所發出的廣播。當此App首次啓動時,系統會自動實例化MyBroadcastReceiver,並註冊到系統中。

以前常說:靜態註冊的廣播接收器即便app已經退出,主要有相應的廣播發出,依然能夠接收到,但此種描述自Android 3.1開始有可能再也不成立,具體分析詳見本文後面部分

 

2).動態註冊:
動態註冊時,無須在AndroidManifest中註冊<receiver/>組件。直接在代碼中經過調用Context的registerReceiver函數,能夠在程序中動態註冊BroadcastReceiver。registerReceiver的定義形式以下:

1 registerReceiver(BroadcastReceiver receiver, IntentFilter filter)
2 registerReceiver(BroadcastReceiver receiver, IntentFilter filter, String broadcastPermission, Handler scheduler)

典型的寫法示例以下:

 1 public class MainActivity extends Activity {  2     public static final String BROADCAST_ACTION = "com.example.corn";  3     private BroadcastReceiver mBroadcastReceiver;  4 
 5  @Override  6     protected void onCreate(Bundle savedInstanceState) {  7         super.onCreate(savedInstanceState);  8  setContentView(R.layout.activity_main);  9 
10         mBroadcastReceiver = new MyBroadcastReceiver(); 11         IntentFilter intentFilter = new IntentFilter(); 12  intentFilter.addAction(BROADCAST_ACTION); 13  registerReceiver(mBroadcastReceiver, intentFilter); 14  } 15     
16  @Override 17     protected void onDestroy() { 18         super.onDestroy(); 19  unregisterReceiver(mBroadcastReceiver); 20  } 21 
22 }

注:Android中全部與觀察者模式有關的設計中,一旦涉及到register,一定在相應的時機須要unregister。所以,上例在onDestroy()回到中須要unregisterReceiver(mBroadcastReceiver)。

當此Activity實例化時,會動態將MyBroadcastReceiver註冊到系統中。當此Activity銷燬時,動態註冊的MyBroadcastReceiver將再也不接收到相應的廣播。

 

3.廣播發送及廣播類型

常常說」發送廣播「和」接收「,表面上看廣播做爲Android廣播機制中的實體,實際上這一實體自己是並非以所謂的」廣播「對象存在的,而是以」意圖「(Intent)去表示。定義廣播的定義過程,實際就是相應廣播」意圖「的定義過程,而後經過廣播發送者將此」意圖「發送出去。被相應的BroadcastReceiver接收後將會回調onReceive()函數。

下段代碼片斷顯示的是一個普通廣播的定義過程,併發送出去。其中setAction(..)對應於BroadcastReceiver中的intentFilter中的action。

1 Intent intent = new Intent();
2 intent.setAction(BROADCAST_ACTION);
3 intent.putExtra("name", "qqyumidi");
4 sendBroadcast(intent);

根據廣播的發送方式,能夠將其分爲如下幾種類型:
1.Normal Broadcast:普通廣播

2.System Broadcast: 系統廣播

3.Ordered broadcast:有序廣播

4.Sticky Broadcast:粘性廣播(在 android 5.0/api 21中deprecated,再也不推薦使用,相應的還有粘性有序廣播,一樣已經deprecated)

5.Local Broadcast:App應用內廣播

下面分別總結下各類類型的發送方式及其特色。

1).Normal Broadcast:普通廣播

此處將普通廣播界定爲:開發者本身定義的intent,以context.sendBroadcast_"AsUser"(intent, ...)形式。具體可使用的方法有:
sendBroadcast(intent)/sendBroadcast(intent, receiverPermission)/sendBroadcastAsUser(intent, userHandler)/sendBroadcastAsUser(intent, userHandler,receiverPermission)。
普通廣播會被註冊了的相應的感興趣(intent-filter匹配)接收,且順序是無序的。若是發送廣播時有相應的權限要求,BroadCastReceiver若是想要接收此廣播,也須要有相應的權限。

2).System Broadcast: 系統廣播

Android系統中內置了多個系統廣播,只要涉及到手機的基本操做,基本上都會發出相應的系統廣播。如:開啓啓動,網絡狀態改變,拍照,屏幕關閉與開啓,點亮不足等等。每一個系統廣播都具備特定的intent-filter,其中主要包括具體的action,系統廣播發出後,將被相應的BroadcastReceiver接收。系統廣播在系統內部當特定事件發生時,有系統自動發出。

3)Ordered broadcast:有序廣播

有序廣播的有序廣播中的「有序」是針對廣播接收者而言的,指的是發送出去的廣播被BroadcastReceiver按照前後循序接收。有序廣播的定義過程與普通廣播無異,只是其的主要發送方式變爲:sendOrderedBroadcast(intent, receiverPermission, ...)。

對於有序廣播,其主要特色總結以下:

1>多個具當前已經註冊且有效的BroadcastReceiver接收有序廣播時,是按照前後順序接收的,前後順序斷定標準遵循爲:將當前系統中全部有效的動態註冊和靜態註冊的BroadcastReceiver按照priority屬性值從大到小排序,對於具備相同的priority的動態廣播和靜態廣播,動態廣播會排在前面。

2>先接收的BroadcastReceiver能夠對此有序廣播進行截斷,使後面的BroadcastReceiver再也不接收到此廣播,也能夠對廣播進行修改,使後面的BroadcastReceiver接收到廣播後解析獲得錯誤的參數值。固然,通常狀況下,不建議對有序廣播進行此類操做,尤爲是針對系統中的有序廣播。

4)Sticky Broadcast:粘性廣播(在 android 5.0/api 21中deprecated,再也不推薦使用,相應的還有粘性有序廣播,一樣已經deprecated)。

既然已經deprecated,此處再也不多作總結。

5)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的單一實例。

代碼片斷以下:

 1 //registerReceiver(mBroadcastReceiver, intentFilter);  2 //註冊應用內廣播接收器
 3 localBroadcastManager = LocalBroadcastManager.getInstance(this);  4 localBroadcastManager.registerReceiver(mBroadcastReceiver, intentFilter);  5         
 6 //unregisterReceiver(mBroadcastReceiver);  7 //取消註冊應用內廣播接收器
 8 localBroadcastManager.unregisterReceiver(mBroadcastReceiver);  9 
10 Intent intent = new Intent(); 11 intent.setAction(BROADCAST_ACTION); 12 intent.putExtra("name", "qqyumidi"); 13 //sendBroadcast(intent); 14 //發送應用內廣播
15 localBroadcastManager.sendBroadcast(intent);

 

4.不一樣註冊方式的廣播接收器回調onReceive(context, intent)中的context具體類型

1).對於靜態註冊的ContextReceiver,回調onReceive(context, intent)中的context具體指的是ReceiverRestrictedContext;

2).對於全局廣播的動態註冊的ContextReceiver,回調onReceive(context, intent)中的context具體指的是Activity Context;

3).對於經過LocalBroadcastManager動態註冊的ContextReceiver,回調onReceive(context, intent)中的context具體指的是Application Context。

注:對於LocalBroadcastManager方式發送的應用內廣播,只能經過LocalBroadcastManager動態註冊的ContextReceiver纔有可能接收到(靜態註冊或其餘方式動態註冊的ContextReceiver是接收不到的)。

 

5.不一樣Android API版本中廣播機制相關API重要變遷

1).Android5.0/API level 21開始粘滯廣播和有序粘滯廣播過時,之後再也不建議使用;

2).」靜態註冊的廣播接收器即便app已經退出,主要有相應的廣播發出,依然能夠接收到,但此種描述自Android 3.1開始有可能再也不成立「

Android 3.1開始系統在Intent與廣播相關的flag增長了參數,分別是FLAG_INCLUDE_STOPPED_PACKAGES和FLAG_EXCLUDE_STOPPED_PACKAGES。

FLAG_INCLUDE_STOPPED_PACKAGES:包含已經中止的包(中止:即包所在的進程已經退出)

FLAG_EXCLUDE_STOPPED_PACKAGES:不包含已經中止的包

主要緣由以下:

自Android3.1開始,系統自己則增長了對全部app當前是否處於運行狀態的跟蹤。在發送廣播時,無論是什麼廣播類型,系統默認直接增長了值爲FLAG_EXCLUDE_STOPPED_PACKAGES的flag,致使即便是靜態註冊的廣播接收器,對於其所在進程已經退出的app,一樣沒法接收到廣播。

詳情參加Android官方文檔:http://developer.android.com/about/versions/android-3.1.html#launchcontrols

由此,對於系統廣播,因爲是系統內部直接發出,沒法更改此intent flag值,所以,3.1開始對於靜態註冊的接收系統廣播的BroadcastReceiver,若是App進程已經退出,將不能接收到廣播。

可是對於自定義的廣播,能夠經過複寫此flag爲FLAG_INCLUDE_STOPPED_PACKAGES,使得靜態註冊的BroadcastReceiver,即便所在App進程已經退出,也能能接收到廣播,並會啓動應用進程,但此時的BroadcastReceiver是從新新建的。

1 Intent intent = new Intent(); 2 intent.setAction(BROADCAST_ACTION); 3 intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); 4 intent.putExtra("name", "qqyumidi"); 5 sendBroadcast(intent);

注1:對於動態註冊類型的BroadcastReceiver,因爲此註冊和取消註冊實在其餘組件(如Activity)中進行,所以,不受此改變影響。

注2:在3.1之前,相信很多app可能經過靜態註冊方式監聽各類系統廣播,以此進行一些業務上的處理(如即時app已經退出,仍然能接收到,能夠啓動service等..),3.1後,靜態註冊接受廣播方式的改變,將直接致使此類方案再也不可行。因而,經過將Service與App自己設置成不一樣的進程已經成爲實現此類需求的可行替代方案。

相關文章
相關標籤/搜索