Andriod廣播註冊接收過程簡析

  爲了利於各個進程應用之間的通訊,android提供了一個很方便的處理方式:廣播機制。廣播機制顧名思義,就是廣播發送者無需判斷具體某個接收者的存在,把廣播發送出去,任務即完成。這樣保證了有效通訊的同時又最大限度的下降了android系統各個模塊的耦合性。

Android廣播有兩個很重要的要素: android

   1 廣播 - 用於發送廣播 app

         有序廣播  -  被廣播接收器接收後,可被終止,沒法往下繼續傳達。         典型表明:短信廣播 ui

         普通廣播  -  發送至每個已經註冊(訂閱)的廣播接收器,沒法被終止。 典型表明:開機啓動廣播 this

   2 廣播接收器 - 用於訂閱廣播後接收廣播 spa

         靜態註冊廣播 - 在AndroidManifest.xml中設置,程序不用啓動亦可接收。 典型表明:不少開機啓動的APP,都是接收開機啓動廣播帶起服務的。 rest

         動態註冊廣播 - 代碼中註冊廣播,程序未啓動時,沒法接收廣播。             典型表明:Go短信,將Go短信強行中止,Go短信沒法接收短信。 code

廣播註冊過程和接收廣播順序過程 component

       

                                             圖1 註冊廣播流程簡圖 xml

    靜態廣播接收器 由PackageManagerService負責,當手機啓動時(或者新安裝了應用),PackageManagerService負責掃描手機中全部已安裝的APP應用(題外話,肯定再也不使用的APP須要卸載了),將AndroidManifest.xml中 有關注冊廣播的信息 解析出來,存儲至一個全局靜態變量當中 mReceiversblog

  須要注意的是:

1 PackageManagerService掃描目錄的順序以下:

  system/framework

  system/app

  vendor/app

  data/app

  drm/app-private

  2 當處於同一目錄下時:按照file.list()的返回順序。(題外話:由於在data/app下的應用都是用戶安裝的,而且都是以com.xxx.xxx-1.apk 的形式出現,因此若是打算作手機管家之類的應用,須要好好的研究下包名,爭取在file.list()的獨木橋下搶的頭籌---優先接收開機啓動完成的廣播。)

   3 在此處並未對 接收順序作完整的排序。(注意修飾詞完整的,畢竟先掃描的固然會有必定優先級)

    動態廣播接收器 由ActivityManagerService負責,當APP的服務或者進程起來以後,執行了註冊廣播接收的代碼邏輯,即進行加載,最後會存儲在一個全局靜態變量

mReceiverResolver中。

    須要注意的是:

    1 這個並不是是一成不變的,當程序被殺死以後,  已註冊的動態廣播接收器也會被移出mReceiverResolver,直到下次程序啓動,再進行動態廣播的註冊,固然這裏面的順序也已經變動了一次。

    2  這裏也並沒完整的進行廣播的排序,只記錄的註冊的前後順序,並未有結合優先級的處理。

當有廣播發出時,接收順序以下:

                                                  圖2 廣播接收流程簡圖
   在ActivityManagerService處理廣播,當廣播爲有序廣播時,將動態廣播接收器和動態廣播接收器合併起來,造成最終的有序廣播接收順序。
   上述的 規則1排序爲:
                                1 優先級高的先接收
                                2 同優先級的動靜態廣播接收器,動態優先於靜態
                                3 同優先級的動態廣播接收器  或者同優先級的靜態廣播接收器,按照圖1 的流程註冊順序。
                                   即靜態:先掃描的大於後掃描的,動態:先註冊的大於後註冊的。
 
   當廣播爲普通廣播時, 規則2排序爲:
                                1 無視優先級,動態廣播接收器優先於靜態廣播接收器
                                2 同 規則1排序的第3點

接下來請看代碼以片斷:

複製代碼

 1 private final int broadcastIntentLocked(ProcessRecord callerApp,  2  String callerPackage, Intent intent, String resolvedType,  3 IIntentReceiver resultTo, int resultCode, String resultData,  4  Bundle map, String requiredPermission,  5 boolean ordered, boolean sticky, int callingPid, int callingUid) {  6  7  …………  8  …………  9  10 // 靜態廣播接收器list  11 List receivers = null;  12  13 // 動態廣播接收器List  14 List<BroadcastFilter> registeredReceivers = null;  15  16 // 獲取靜態廣播接收器mReceivers  17 try {  18 if (intent.getComponent() != null) {  19 // Broadcast is going to one specific receiver class...   20 ActivityInfo ai = AppGlobals.getPackageManager().  21  getReceiverInfo(intent.getComponent(), STOCK_PM_FLAGS);  22 if (ai != null) {  23 receivers = new ArrayList();  24 ResolveInfo ri = new ResolveInfo();  25 ri.activityInfo = ai;  26  receivers.add(ri);  27  }  28 } else {  29 // Need to resolve the intent to interested receivers...   30 if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY)  31 == 0) {  32 receivers =  33  AppGlobals.getPackageManager().queryIntentReceivers(  34  intent, resolvedType, STOCK_PM_FLAGS);  35  }  36 // 獲取動態廣播接收器mReceiverResolver  37 registeredReceivers = mReceiverResolver.queryIntent(intent, resolvedType, false);  38  }  39 } catch (RemoteException ex) {  40 // pm is in same process, this will never happen.   41  }  42  43 final boolean replacePending =  44 (intent.getFlags()&Intent.FLAG_RECEIVER_REPLACE_PENDING) != 0;  45  46 int NR = registeredReceivers != null ? registeredReceivers.size() : 0;  47  ……  48 // 若是接收到的廣播 是普通廣播。  49 if (!ordered && NR > 0) {  50 // If we are not serializing this broadcast, then send the  51 // registered receivers separately so they don't wait for the  52 // components to be launched.   53 BroadcastRecord r = new BroadcastRecord(intent, callerApp,  54  callerPackage, callingPid, callingUid, requiredPermission,  55  registeredReceivers, resultTo, resultCode, resultData, map,  56 ordered, sticky, false);  57  58 // 很明顯接收到普通廣播以後,在這隻處理了動態廣播 registeredReceivers,對於普通廣播而言,動態廣播接收器要優先於靜態廣播接收器 無關設置的優先級  59 boolean replaced = false;  60 if (replacePending) {  61 for (int i=mParallelBroadcasts.size()-1; i>=0; i--) {  62 if (intent.filterEquals(mParallelBroadcasts.get(i).intent)) {  63 if (DEBUG_BROADCAST) Slog.v(TAG,  64 "***** DROPPING PARALLEL: " + intent);  65  mParallelBroadcasts.set(i, r);  66 replaced = true;  67 break;  68  }  69  }  70  }  71 if (!replaced) {  72  mParallelBroadcasts.add(r);  73  scheduleBroadcastsLocked();  74  }  75 //將registeredReceivers置爲null,後面只處理靜態廣播接收器,因此不會有衝突。  76 registeredReceivers = null;  77 NR = 0;  78  }  79  80 //若是是有序廣播,將靜態廣播接收器和動態廣播接收器組合成一個最終的順序  81 int ir = 0;  82 if (receivers != null) {  83  ...  84 //合併的過程,注意順序   85 int NT = receivers != null ? receivers.size() : 0;  86 int it = 0;  87 ResolveInfo curt = null;  88 BroadcastFilter curr = null;  89 while (it < NT && ir < NR) {  90 if (curt == null) {  91 curt = (ResolveInfo)receivers.get(it);  92  }  93 if (curr == null) {  94 curr = registeredReceivers.get(ir);  95  }  96 //若是動態廣播接收器優先級高於或者等於靜態廣播接收器,那麼就插到前面  97 //很明顯動態的要在靜態的前面   98 if (curr.getPriority() >= curt.priority) {  99 // Insert this broadcast record into the final list.  100  receivers.add(it, curr); 101 ir++; 102 curr = null; 103 it++; 104 NT++; 105 } else { 106 // Skip to the next ResolveInfo in the final list.  107 it++; 108 curt = null; 109  } 110  } 111 }

複製代碼


最後舉個例子:

(如下的靜A 表示靜態廣播接收器,同理動B。)

1 靜A (優先級1)

2 動B(優先級1)

3 靜C (優先級2,後掃描)

4 靜D (優先級2,先掃描)

5 動E   (優先級2,先註冊)

6 動F  (優先級2,後註冊)

當來了一個 有序廣播,接收順序以下:動E >  動F  > 靜D > 靜C > 動B > 靜A

當來了一個 普通廣播,接收順序以下:動E >  動F  > 動B > 靜D > 靜C > 靜A

參考文章:http://su1216.iteye.com/blog/1776121

相關文章
相關標籤/搜索