Android 四大組件之「 BroadcastReceiver 」

前言

Android四大組件重要性已經不言而喻了,今天談談的是Android中的廣播機制。在咱們上學的時候,每一個班級的教室裏都會裝有一個喇叭,這些喇叭都是接入到學校的廣播室的,一旦有什麼重要的通知,就會播放一條廣播來告知全校的師生。相似的工做機制其實在計算機領域也有很普遍的應用,若是你瞭解網絡通訊原理應該會知道,在一個 IP 網絡範圍中最大的 IP 地址是被保留做爲廣播地址來使用的。好比某個網絡的 IP 範圍是 192.168.0.XXX,子網掩碼是 255.255.255.0,那麼這個網絡的廣播地址就是 192.168.0.255。 廣播數據包會被髮送到同一網絡上的全部端口,這樣在該網絡中的每臺主機都將會收到這條廣播。爲了方便於進行系統級別的消息通知,Android 也引入了一套相似的廣播消息機制。html

目錄

  • 廣播機制介紹
  • BroadcastReceiver用法
  • 發送自定義廣播
  • 本地廣播介紹
  • 廣播的註冊過程
  • 廣播的發送和接受過程
  • 總結

廣播機制介紹

爲何說 Android中的廣播機制更加靈活呢?這是由於 Android中的每一個應用程序均可以對本身感興趣的廣播進行註冊,這樣該程序就只會接收到本身所關心的廣播內容,這些 播多是來自於系統的,也多是來自於其餘應用程序的。Android提供了一套完整的 API, 容許應用程序自由地發送和接收廣播。接收廣播的方法則須要引入一個新的概念,廣播接收器(Broadcast Receiver),它就是用來接收來自系統和應用中的廣播。java

在Android廣播中,主要分爲兩種類型:標準廣播和有序廣播。android

標準廣播(Normal broadcasts)是一種徹底異步執行的廣播,在廣播發出以後,全部的 廣播接收器幾乎都會在同一時刻接收到這條廣播消息,所以它們之間沒有任何前後順序可 言。這種廣播的效率會比較高,但同時也意味着它是沒法被截斷的。標準廣播的工做流程如圖所示。安全

標準廣播

有序廣播(Ordered broadcasts)則是一種同步執行的廣播,在廣播發出以後,同一時刻只會有一個廣播接收器可以收到這條廣播消息,當這個廣播接收器中的邏輯執行完畢後,廣播纔會繼續傳遞。因此此時的廣播接收器是有前後順序的,優先級高的廣播接收器就能夠先收到廣播消息,而且前面的廣播接收器還能夠截斷正在傳遞的廣播,這樣後面的廣播接收器就沒法收到廣播消息了。性能優化

有序廣播

BroadcastReceiver用法

BroadcastReceiver主要包括兩方面的內容,一個是廣播的註冊過程,另外一個是廣播的發送和接收過程。那麼該如何建立一個廣播接收器呢?其實只須要新建一個類,讓它繼承自BroadcastReceiver, 並重寫父類的 onReceive()方法就好了。這樣當有廣播到來時,onReceive()方法就會獲得執行, 具體的邏輯就能夠在這個方法中處理。 廣播的使用方法有兩個:靜態方法和動態方法。網絡

動態方法app

public class MainActivity extends Activity {    
  private IntentFilter intentFilter;  
  private NetworkChangeReceiver networkChangeReceiver;  
  
  @Override  
  protected void onCreate(Bundle savedInstanceState) {   
    super.onCreate(savedInstanceState);   
    setContentView(R.layout.activity_main);   
    intentFilter = new IntentFilter();
    intentFilter.addAction("android.net.conn.CONNECTIVITY_CHANGE");  
    networkChangeReceiver = new NetworkChangeReceiver();       
    registerReceiver(networkChangeReceiver, intentFilter);
  }   
  
  @Override 
  protected void onDestroy() {   
    super.onDestroy();  
    unregisterReceiver(networkChangeReceiver);  
  }  
  
  private class NetworkChangeReceiver extends BroadcastReceiver {  
    @Override   
    public void onReceive(Context context, Intent intent) { 
        Toast.makeText(context, "網絡變化", Toast.LENGTH_SHORT).show(); 
    }  
 }  
}

能夠看到,咱們在 MainActivity 中定義了一個內部類 NetworkChangeReceiver,這個類 是繼承自 BroadcastReceiver的,並重寫了父類的 onReceive()方法。這樣每當網絡狀態發生變 化時,onReceive()方法就會獲得執行,這裏只是簡單地使用 Toast提示了一段文本信息。框架

而後觀察 onCreate()方法,首先咱們建立了一個 IntentFilter 的實例,並給它添加了一個 值爲 android.net.conn.CONNECTIVITY_CHANGE 的 action,爲何要添加這個值呢?由於 當網絡狀態發生變化時,系統發出的正是一條值爲 android.net.conn.CONNECTIVITY_ CHANGE 的廣播,也就是說咱們的廣播接收器想要監聽什麼廣播,就在這裏添加相應的 action就好了。接下來建立了一個 NetworkChangeReceiver的實例,而後調用 registerReceiver() 方法進行註冊,將 NetworkChangeReceiver 的實例和 IntentFilter 的實例都傳了進去,這樣 NetworkChangeReceiver就會收到全部值爲android.net.conn.CONNECTIVITY_CHANGE的廣 播,也就實現了監聽網絡變化的功能。異步

最後要記得,動態註冊的廣播接收器必定都要取消註冊才行,這裏咱們是在 onDestroy() 方法中經過調用 unregisterReceiver()方法來實現的。ide

靜態方法

動態註冊的廣播接收器能夠自由地控制註冊與註銷,在靈活性方面有很大的優點,可是 它也存在着一個缺點,即必需要在程序啓動以後才能接收到廣播,由於註冊的邏輯是寫在 onCreate()方法中的。那麼有沒有什麼辦法可讓程序在未啓動的狀況下就能接收到廣播 呢?這就須要使用靜態註冊的方式了。

這裏咱們準備讓程序接收一條開機廣播,當收到這條廣播時就能夠在 onReceive()方法裏 執行相應的邏輯,從而實現開機啓動的功能。新建一個 BootCompleteReceiver 繼承自 BroadcastReceiver,代碼以下所示

public class BootCompleteReceiver extends BroadcastReceiver {
    @Override  
    public void onReceive(Context context, Intent intent) { 
        Toast.makeText(context, "Boot Complete", Toast.LENGTH_LONG).show();  
    }  
}

能夠看到,這裏再也不使用內部類的方式來定義廣播接收器,由於稍後咱們須要在 AndroidManifest.xml中將這個廣播接收器的類名註冊進去。在 onReceive()方法中,仍是簡單 地使用 Toast彈出一段提示信息。

而後修改 AndroidManifest.xml文件,代碼以下所示:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"                
  package="com.example.broadcasttest"     
  android:versionCode="1" 
  android:versionName="1.0" >    
    ……     
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />    
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />          
      <application 
        android:allowBackup="true"   
        android:icon="@drawable/ic_launcher"      
        android:label="@string/app_name"     
        android:theme="@style/AppTheme" >  
        <receiver android:name=".BootCompleteReceiver" >       
            <intent-filter>                 
                <action android:name="android.intent.action.BOOT_COMPLETED" /> 
            </intent-filter>    
        </receiver>     
      </application>
</manifest>

標籤內出現了一個新的標籤 ,全部靜態註冊的廣播接收器 都是在這裏進行註冊的。它的用法其實和 標籤很是類似,首先經過 android:name 來指定具體註冊哪個廣播接收器,而後在 標籤里加入想要接收的廣播就好了, 因爲Android系統啓動完成後會發出一條值爲android.intent.action.BOOT_COMPLETED的廣 播,所以咱們在這裏添加了相應的 action。

另外,監聽系統開機廣播也是須要聲明權限的,能夠看到,咱們使用 標籤又加入了一條 android.permission.RECEIVE_BOOT_COMPLETED權限

發送自定義廣播

如今你已經學會了經過廣播接收器來接收系統廣播,接下來咱們就要學習一下如何在應用程序中發送自定義的廣播。前面已經介紹過了,廣播主要分爲兩種類型,標準廣播和有序 廣播。

在API文檔中關於BroadcastReceiver的概述:

  • 廣播接收器是一個專一於接收廣播通知信息,並作出對應處理的組件。不少廣播是源自於系統代碼的──好比,通知時區改變、電池電量低、拍攝了一張照片或者用戶改變了語言選項。應用程序也能夠進行廣播──好比說,通知其它應用程序一些數據下載完成並處於可用狀態。
  • 應用程序能夠擁有任意數量的廣播接收器以對全部它感興趣的通知信息予以響應。全部的接收器均繼承自BroadcastReceiver基類。
  • 廣播接收器沒有用戶界面。然而,它們能夠啓動一個activity來響應它們收到的信息,或者用NotificationManager來通知用戶。通知能夠用不少種方式來吸引用戶的注意力──閃動背燈、震動、播放聲音等等。通常來講是在狀態欄上放一個持久的圖標,用戶能夠打開它並獲取消息。

那麼廣播事件的流程如何呢,以下:

  • 註冊廣播事件:註冊方式有兩種,一種是靜態註冊,就是在AndroidManifest.xml文件中定義,註冊的廣播接收器必需要繼承BroadcastReceiver;另外一種是動態註冊,是在程序中使用Context.registerReceiver註冊,註冊的廣播接收器至關於一個匿名類。兩種方式都須要IntentFIlter。

  • 發送廣播事件:經過Context.sendBroadcast來發送,由Intent來傳遞註冊時用到的Action。

  • 接收廣播事件:當發送的廣播被接收器監聽到後,會調用它的onReceive()方法,並將包含消息的Intent對象傳給它。onReceive中代碼的執行時間不要超過10s,不然Android會彈出超時dialog。

具體作法:

在發送廣播以前,咱們仍是須要先定義一個廣播接收器來準備接收此廣播才行,否則發 出去也是白髮。所以新建一個 MyBroadcastReceiver繼承自 BroadcastReceiver

public class MyBroadcastReceiver extends BroadcastReceiver {    
    @Override  
    public void onReceive(Context context, Intent intent) {   
      Toast.makeText(context, "接收到廣播消息", Toast.LENGTH_SHORT).show();    }  
}

這裏當 MyBroadcastReceiver收到自定義的廣播時,就會彈出提示語。而後在 AndroidManifest.xml中對這個廣播接收器進行註冊:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"                
  package="com.example.broadcasttest"     
  android:versionCode="1" 
  android:versionName="1.0" >    
    ……     
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />    
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />          
      <application 
        android:allowBackup="true"   
        android:icon="@drawable/ic_launcher"      
        android:label="@string/app_name"     
        android:theme="@style/AppTheme" >  
        <receiver android:name=".MyBroadcastReceiver" >       
            <intent-filter>                 
                <action android:name="com.example.broadcasttest.MY_BROADCAST" /> 
            </intent-filter>    
        </receiver>     
      </application>
</manifest>

能夠看到,這裏讓 MyBroadcastReceiver 接收一條值爲 com.example.broadcasttest. MY_BROADCAST的廣播,所以待會兒在發送廣播的時候,咱們就須要發出這樣的一條廣播。

public class MainActivity extends Activity {  
    @Override  
    protected void onCreate(Bundle savedInstanceState) {   
      super.onCreate(savedInstanceState);   
      setContentView(R.layout.activity_main);  
      Button button = (Button) findViewById(R.id.button);  
      button.setOnClickListener(new OnClickListener() {   
        @Override    
        public void onClick(View v) {  
          Intent intent = new Intent("com.example.broadcasttest.MY_BROADCAST");         
          sendBroadcast(intent);   
        }
      });   
    } 
}

能夠看到,咱們在按鈕的點擊事件裏面加入了發送自定義廣播的邏輯。首先構建出了一 個 Intent對象,並把要發送的廣播的值傳入,而後調用了 Context的 sendBroadcast()方法將廣 播發送出去,這樣全部監聽 com.example.broadcasttest.MY_BROADCAST 這條廣播的廣播接 收器就會收到消息。此時發出去的廣播就是一條標準廣播。

本地廣播介紹

前面咱們發送和接收的廣播所有都是屬於系統全局廣播,即發出的廣播能夠被其餘任何的任何應用程序接收到,而且咱們也能夠接收來自於其餘任何應用程序的廣播。這樣就很容易會引發安全性的問題,好比說咱們發送的一些攜帶關鍵性數據的廣播有可能被其餘的應用 程序截獲,或者其餘的程序不停地向咱們的廣播接收器裏發送各類垃圾廣播。

爲了可以簡單地解決廣播的安全性問題,Android 引入了一套本地廣播機制,使用這個機制發出的廣播只可以在應用程序的內部進行傳遞,而且廣播接收器也只能接收來自本應用程序發出的廣播,這樣全部的安全性問題就都不存在了。 本地廣播的用法並不複雜,主要就是使用了一個 LocalBroadcastManager 來對廣播進行管理,並提供了發送廣播和註冊廣播接收器的方法。下面咱們就經過具體的實例來嘗試一下它的用法,修改 MainActivity中的代碼,以下所示:

public class MainActivity extends Activity {   
    private IntentFilter intentFilter;  
    private LocalReceiver localReceiver;  
    private LocalBroadcastManager localBroadcastManager;
  
    @Override  
    protected void onCreate(Bundle savedInstanceState) {   
      super.onCreate(savedInstanceState); 
      setContentView(R.layout.activity_main); 
      localBroadcastManager = LocalBroadcastManager.getInstance(this); 
      // 獲取實例  
      Button button = (Button) findViewById(R.id.button);
      button.setOnClickListener(new OnClickListener() {  
        @Override  
        public void onClick(View v) { 
            Intent intent = new Intent("com.example.broadcasttest. LOCAL_BROADCAST");       
            localBroadcastManager.sendBroadcast(intent);// 發送本地廣播 
        }
      }); 
      intentFilter = new IntentFilter();   
      intentFilter.addAction("com.example.broadcasttest.LOCAL_BROADCAST"); 
      localReceiver = new LocalReceiver();   
      // 註冊本地廣播監聽器
      localBroadcastManager.registerReceiver(localReceiver, intentFilter); 
    }  
  
    @Override 
    protected void onDestroy() { 
      super.onDestroy();  
      localBroadcastManager.unregisterReceiver(localReceiver); 
    }  
  
    private class LocalReceiver extends BroadcastReceiver {  
        @Override 
        public void onReceive(Context context, Intent intent) { 
            Toast.makeText(context, "received local broadcast", Toast.LENGTH_SHORT).show(); 
        }  
    }  
}

有沒有感受這些代碼很熟悉?沒錯,其實這基本上就和咱們前面所學的動態註冊廣播接 收器以及發送廣播的代碼是同樣。只不過如今首先是經過 LocalBroadcastManager的 getInstance() 方法獲得了它的一個實例,而後在註冊廣播接收器的時候調用的是 LocalBroadcastManager 的 registerReceiver()方法,在發送廣播的時候調用的是 LocalBroadcastManager的 sendBroadcast() 方法,僅此而已。

另外還有一點須要說明,本地廣播是沒法經過靜態註冊的方式來接收的。其實這也徹底 能夠理解,由於靜態註冊主要就是爲了讓程序在未啓動的狀況下也能收到廣播,而發送本地 廣播時,咱們的程序確定是已經啓動了,所以也徹底不須要使用靜態註冊的功能。

總結下使用本地廣播的幾點優點吧。

  1. 能夠明確地知道正在發送的廣播不會離開咱們的程序,所以不須要擔憂機密數據泄 漏的問題。
  2. 其餘的程序沒法將廣播發送到咱們程序的內部,所以不須要擔憂會有安全漏洞的隱 患。
  3. 發送本地廣播比起發送系統全局廣播將會更加高效。

廣播的註冊過程

咱們如今知道了廣播的註冊有靜態註冊和動態註冊,其中靜態註冊的廣播在應用安裝時由系統自動完成註冊的。具體來講是由PMS(PackageManagerService)來完成整個註冊過程的,除了廣播覺得,其餘三大組件也都是應用安裝時由PMS解析並註冊的,這裏分析下廣播的動態註冊過程,動態註冊過程是從ContextWrapper的registerReceiver方法開始的,和Activity以及Service同樣。ContextWrapper並無作實際的工做,基本將註冊過程直接交給ContextImpl來完成。

@Override
  public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
      return registerReceiver(receiver, filter, null, null);
  }

ContextImpl的registerReceiver方法調用了本身的registerReceiverInternal方法,具體實現以下:

private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,
    IntentFilter filter, String broadcastPermission,Handler scheduler, Context context) {
      IIntentReceiver rd = null;
      if (receiver != null) {
        if (mPackageInfo != null && context != null) {
          if (scheduler == null) {
            scheduler = mMainThread.getHandler();
          }
          rd = mPackageInfo.getReceiverDispatcher(
            receiver, context, scheduler,
            mMainThread.getInstrumentation(), true);
        } else {
          if (scheduler == null) {
            scheduler = mMainThread.getHandler();
          }
          rd = new LoadedApk.ReceiverDispatcher(
            receiver, context, scheduler, null, true).getIIntentReceiver();
        }
      }
      try {
        return ActivityManagerNative.getDefault().registerReceiver(
          mMainThread.getApplicationThread(), mBasePackageName,
          rd, filter, broadcastPermission, userId);
      } catch (RemoteException e) {
        return null;
      }
}

系統首先從mPackageInfo獲取IIntentReceiver對象,而後再採用跨進程的方式向AMS發送廣播註冊的請求。之因此用IIntentReceiver而不是直接採用BroadcastReceiver,這是由於上述註冊過程是一個進程間通訊的過程,而BroadcastReceiver做爲一個Android組件是不能直接跨進程傳遞的,因此須要經過IIntentReceiver來中轉一下,毫無疑問,IIntentReceiver必須是一個Binder接口,它的具體實現是LoadedApk.ReceiverDispatcher,ReceiverDispatcher的內部同時保存了BroadcastReceiver和InnerReceiver,這樣當接收到廣播時ReceiverDispatcher能夠很方便地調用BroadcastReceiver的onReceiver方法。

這裏的ActivityManagerNative.getDefault()實際上就是一個AMS。具體代碼以下:

public IIntentReceiver getReceiverDispatcher(BroadcastReceiver r,Context context, Handler handler,Instrumentation instrumentation, boolean registered) {
   synchronized (mReceivers) {
     LoadedApk.ReceiverDispatcher rd = null;
     ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> map = null;
     if (registered) {
       map = mReceivers.get(context);
       if (map != null) {
         rd = map.get(r);
       }
     }
     if (rd == null) {
       rd = new ReceiverDispatcher(r, context, handler,instrumentation, registered);
       if (registered) {
         if (map == null) {
           map = new ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>();
           mReceivers.put(context, map);
         }
         map.put(r, rd);
       }
     } else {
       rd.validate(context, handler);
     }
     rd.mForgotten = false;
     return rd.getIIntentReceiver();
   }

因爲註冊的廣播真正的實現過程是在AMS中,最終會把遠程的InnerReceiver對象以及IntentFilter對象存儲起來,這樣整個廣播的註冊過程就完成了。

廣播的發送和接受過程

當經過send方法來發送廣播時,AMS會查找出匹配的廣播接收者並將廣播發送給他們處理。廣播的發送有幾種類型:普通廣播,有序廣播和粘性廣播。這裏分析下普通廣播的實現。

廣播的發送和接收。其本質是一個過程的兩個階段。廣播的發送仍然開始於ContextWrapper的sendBroadcast方法,之因此不是Context,那是由於Context的sendBroadcast是一個抽象方法。和廣播的註冊過程同樣ContextWrapper的sendBroadcast方法仍然什麼都不作,只是把事情交給ContextImpl去處理,ContextImpl的sendBroadcast方法源碼以下:

public void sendBroadcast(Intent intent) {
  warnIfCallingFromSystemProcess();
  String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
  try {
    intent.prepareToLeaveProcess();
    ActivityManagerNative.getDefault().broadcastIntent(
      mMainThread.getApplicationThread(), intent, resolvedType, null,
      Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, false, false,
      getUserId());
  } catch (RemoteException e) {
  }
}

它直接向AMS發起了一個異步請求用於發送廣播。那麼AMS的broadcastIntent方法的源碼以下:

public final int broadcastIntent(IApplicationThread caller,
            Intent intent, String resolvedType, IIntentReceiver resultTo,
            int resultCode, String resultData, Bundle map,
            String requiredPermission, boolean serialized, boolean sticky, int userId) {
        synchronized(this) {
            intent = verifyBroadcastLocked(intent);
            
            final ProcessRecord callerApp = getRecordForAppLocked(caller);
            final int callingPid = Binder.getCallingPid();
            final int callingUid = Binder.getCallingUid();
            final long origId = Binder.clearCallingIdentity();
            int res = broadcastIntentLocked(callerApp,
                    callerApp != null ? callerApp.info.packageName : null,
                    intent, resolvedType, resultTo,
                    resultCode, resultData, map, requiredPermission, serialized, sticky,
                    callingPid, callingUid, userId);
            Binder.restoreCallingIdentity(origId);
            return res;
        }
    }

從代碼上看,broadcastIntent調用了broadcastIntentLocked方法,但在AMS的broadcastIntentLocked方法裏有這麼一句:

// By default broadcasts do not go to stopped apps.
    intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);

這表示在Android5.0下,默認狀況下廣播不會發送給已經中止的應用。FLAG_EXCLUDE_STOPPED_PACKAGES的含義是表示 不包含已經中止的應用,這個時候廣播不會發送給已經中止的應用。

在broadcastIntentLocked的內部,會根據intent-filter查找出匹配的廣播接收者並通過一系列的條件過濾,最終會將知足條件的廣播接收者添加到BroadcastQueue中,接着BroadcastQueue將會廣播發送給相應的廣播接收者。

if ((receivers != null && receivers.size() > 0)
                || resultTo != null) {
            BroadcastQueue queue = broadcastQueueForIntent(intent);
            BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
                    callerPackage, callingPid, callingUid, requiredPermission,
                    receivers, resultTo, resultCode, resultData, map, ordered,
                    sticky, false);
            if (DEBUG_BROADCAST) Slog.v(
                    TAG, "Enqueueing ordered broadcast " + r
                    + ": prev had " + queue.mOrderedBroadcasts.size());
            if (DEBUG_BROADCAST) {
                int seq = r.intent.getIntExtra("seq", -1);
                Slog.i(TAG, "Enqueueing broadcast " + r.intent.getAction() + " seq=" + seq);
            }
            boolean replaced = replacePending && queue.replaceOrderedBroadcastLocked(r); 
            if (!replaced) {
                queue.enqueueOrderedBroadcastLocked(r);
                queue.scheduleBroadcastsLocked();
            }
        }

下面看下BroadcastQueue中廣播的發送過程的實現。以下所示:

public void scheduleBroadcastsLocked() {
            if (DEBUG_BROADCAST) Slog.v(TAG, "Schedule broadcasts ["
                    + mQueueName + "]: current="
                    + mBroadcastsScheduled);
            if (mBroadcastsScheduled) {
                return;
            }
            mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
            mBroadcastsScheduled = true;
        }

BroadcastQueue的scheduleBroadcastsLocked方法並無當即發送廣播,而是發送了一個BROADCAST_INTENT_MSG類型的消息,BroadcastQueue收到消息後會調用processNextBroadcast方法,BroadcastQueue的processNextBroadcast方法對普通廣播的處理方式以下:

// First, deliver any non-serialized broadcasts right away.
                while (mParallelBroadcasts.size() > 0) {
                    r = mParallelBroadcasts.remove(0);
                    r.dispatchTime = SystemClock.uptimeMillis();
                    r.dispatchClockTime = System.currentTimeMillis();
                    final int N = r.receivers.size();
                    if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG, "Processing parallel broadcast ["
                            + mQueueName + "] " + r);
                    for (int i=0; i<N; i++) {
                        Object target = r.receivers.get(i);
                        if (DEBUG_BROADCAST)  Slog.v(TAG,
                                "Delivering non-ordered on [" + mQueueName + "] to registered "
                                + target + ": " + r);
                        deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false);
                    }
                    addBroadcastToHistoryLocked(r);
                    if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG, "Done with parallel broadcast ["
                            + mQueueName + "] " + r);
                }

能夠看到,無序廣播存儲在mParallelBroadcasts中,系統會遍歷mParallelBroadcasts並將其中的廣播發送給它們全部接收者,具體的發送過程是經過deliverToRegisteredReceiverLocked方法來實現的。

最終呢,會調用ApplicationThread的scheduleRegisteredReceiver的實現比較簡單,它經過InnerReceiver來實現廣播的接收。而後InnerReceiver的performReceive方法會調用LoadedApk.ReceiverDispatcher的PerformReceive方法。最終會回調到receiver.onReceive()這個方法。

很顯然,這個時候BroadcastReceiver的onReceive方法被執行了,也就是說應用收到廣播了,同時,onReceive方法是在廣播接收者的主線程中被調用,因此不能作耗時操做,由於是在ApplicationThread的主線程上執行的。

總結

總結一下,Android中應用程序發送廣播的過程:

  • 經過sendBroadcast把一個廣播經過Binder發送給ActivityManagerService,ActivityManagerService根據這個廣播的Action類型找到相應的廣播接收器,而後把這個廣播放進本身的消息隊列中,就完成第一階段對這個廣播的異步分發。
  • ActivityManagerService在消息循環中處理這個廣播,並經過Binder機制把這個廣播分發給註冊的ReceiverDispatcher,ReceiverDispatcher把這個廣播放進MainActivity所在線程的消息隊列中,就完成第二階段對這個廣播的異步分發。
  • ReceiverDispatcher的內部類Args在MainActivity所在的線程消息循環中處理這個廣播,最終是將這個廣播分發給所註冊的BroadcastReceiver實例的onReceive函數進行處理:

做爲Android中四大組件之一的廣播,能夠應用不少場景的,好比用戶異地登錄強制下線,應用開機啓動服務,網絡狀態變化通知等等,掌握好其中的定義,使用方法,背後的註冊流程,發送和接收消息流程機制,對於咱們在開發時是頗有幫助的。

參考信息:

1,http://blog.csdn.net/zuolongsnail/article/details/6450156

2,《第一行代碼》

閱讀擴展

源於對掌握的Android開發基礎點進行整理,羅列下已經總結的文章,從中能夠看到技術積累的過程。
1,Android系統簡介
2,ProGuard代碼混淆
3,講講Handler+Looper+MessageQueue關係
4,Android圖片加載庫理解
5,談談Android運行時權限理解
6,EventBus初理解
7,Android 常見工具類
8,對於Fragment的一些理解
9,Android 四大組件之 " Activity "
10,Android 四大組件之" Service "
11,Android 四大組件之「 BroadcastReceiver "
12,Android 四大組件之" ContentProvider "
13,講講 Android 事件攔截機制
14,Android 動畫的理解
15,Android 生命週期和啓動模式
16,Android IPC 機制
17,View 的事件體系
18,View 的工做原理
19,理解 Window 和 WindowManager
20,Activity 啓動過程分析
21,Service 啓動過程分析
22,Android 性能優化
23,Android 消息機制
24,Android Bitmap相關
25,Android 線程和線程池
26,Android 中的 Drawable 和動畫
27,RecylerView 中的裝飾者模式
28,Android 觸摸事件機制
29,Android 事件機制應用
30,Cordova 框架的一些理解
31,有關 Android 插件化思考
32,開發人員必備技能——單元測試

相關文章
相關標籤/搜索