文章出處

廣播概述

  • 廣播用來在組件之間傳遞消息,可以是同進程或跨進程。
  • 廣播機制是基于發布訂閱的事件驅動模型,使用上比Binder通信(跨進程接口回調)更低耦合、簡單。
  • ActivityManagerService(簡稱AMS)作為廣播消息發布訂閱的注冊中心,廣播接收器(Broadcast Receiver,簡稱Receiver)以靜態或動態方式注冊到AMS。
  • 廣播底層實現就是Binder,包括使用Binder進行跨進程回調接口注冊,發送廣播時使用Binder異步通信發送廣播給接收者所在進程。
  • 繼承Context的Service或Activity組件可以發送有序或無需廣播到AMS。
  • AMS把消息發送給接收此廣播類型的Receiver。
  • 有序廣播根據Receiver優先級被接收,動態注冊的先收到消息,而無需廣播同時發送給所有Receiver。
  • 廣播的生命周期:動態注冊的廣播組件其生命周期和其使用者關聯。靜態注冊的廣播,每次收到廣播時一個Receiver被創建,在主線程中執行其onReceive()方法,方法返回后,Receiver組件即等待銷毀。
  • 因為onReceive在主線程執行,所以耗時操作會引起ANR。
  • 耗時操作應該啟動一個Service去執行,不能是bindService()這樣的,因為bindService()本身是和Service進行通信的方式,而不是增加Receiver存活時間的方式。onReceive()本身的執行就應該很短。startService()保證一個耗時操作放在Service中得已運行更長的時間得到執行。

NOTE:
使用Broadcast完成組件間的事件通知,在跨進程的情況下,比使用Binder進行跨進程接口回調要簡單且更加低耦合。

案例

下面以在MyActivity中注冊MyReceiver為例,MyReceiver接收Action為“com.hxw.bot.broadcast.ACTION”。

BroadcastReceiver receiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {

    }
};
IntentFilter filter = new IntentFilter("com.hxw.bot.broadcast.ACTION");
registerReceiver(receiver, filter);

一個filter可以攔截多個Action。

廣播注冊過程

1. ContextWrapper.registerReceiver

2. ContextImpl.registerReceiver

LoadedApk mPackageInfo;
ActivityThread mMainThread;
...
@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
        String broadcastPermission, Handler scheduler) {
    return registerReceiverInternal(receiver, filter, broadcastPermission,
            scheduler, getOuterContext());
}
...
private Intent registerReceiverInternal(BroadcastReceiver receiver,
        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 {
            ...
        }
    }
    try {
        return ActivityManagerNative.getDefault().registerReceiver(
                mMainThread.getApplicationThread(),
                rd, filter, broadcastPermission);
    ...
}

mMainThread.getHandler()返回一個當前進程的主線程上的Handler,
scheduler用來將AMS回調rd方法從Binder線程池的線程中轉到主線程中進行。

LoadedApk.getReceiverDispatcher()將MyReceiver包裝成一個IIntentReceiver rd,
rd類型是InnerReceiver(繼承自IIntentReceiver.Stub)——一個Binder對象,發送給AMS其代理,完成“廣播接收器”回調接口注冊。

參數context為ContextImpl.getOuterContext(),它返回MyActivity對象。也就是注冊MyReceiver的
Context對象,MyReceiver和MyActivity關聯。

LoadedApk類有一個字段mReceivers:

private final HashMap<Context, HashMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>> mReceivers
        = new HashMap<Context, HashMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>>();

它以Receiver關聯的Context對象(也就是執行注冊的context對象)作為key,存儲了對應context注冊的所有的BroadcastReceiver對象。

class ReceiverDispatcher {
  final IIntentReceiver.Stub mIIntentReceiver; // 作為跨進程接口實例的Binder對象
  final BroadcastReceiver mReceiver; // MyReceiver
  final Context mContext; // MyActivity對象
  final Handler mActivityThread; // 主線程的handler
  ...
}

3. ActivityManagerProxy.registerReceiver

public Intent registerReceiver(IApplicationThread caller,
            IIntentReceiver receiver,
            IntentFilter filter, String perm) throws RemoteException

將參數寫入Parcel data,然后向AMS發起進程間通信REGISTER_RECEIVER_TRANSACTION。

4. AMS.registerReceiver

AMS.registerReceiver()響應REGISTER_RECEIVER_TRANSACTION。

ActivityManagerService {
  /**
    * Keeps track of all IIntentReceivers that have been registered for
    * broadcasts.  Hash keys are the receiver IBinder, hash value is
    * a ReceiverList.
    */
   final HashMap mRegisteredReceivers = new HashMap();

   public Intent registerReceiver(IApplicationThread caller,
            IIntentReceiver receiver, IntentFilter filter,
            String requiredPermission) throws RemoteException {
       ...
       ReceiverList rl
                = (ReceiverList)mRegisteredReceivers.get(receiver.asBinder());
       ...
       BroadcastFilter bf = new BroadcastFilter(filter, rl, permission);
       rl.add(bf);
       mReceiverResolver.addFilter(bf);
   }
}

參數receiver:
AMS收到的“廣播接收器”是MyReceiver對應的InnerReceiver的BinderProxy。

AMS使用BroadcastFilter記錄已經注冊的Receiver:

class BroadcastFilter extends IntentFilter {
  // Back-pointer to the list this filter is in.
  final ReceiverList receiverList;
  final String requiredPermission;

  BroadcastFilter(IntentFilter _filter, ReceiverList _receiverList,
          String _requiredPermission) {
      super(_filter);
      receiverList = _receiverList;
      requiredPermission = _requiredPermission;
  }
}

BroadcastFilter保存了關聯的filter、receiverList。

class ReceiverList extends ArrayList<BroadcastFilter>
        implements IBinder.DeathRecipient {
    final ActivityManagerService owner;
    public final IIntentReceiver receiver;
    ...
}

ReceiverList記錄了某個Receiver——IIntentReceiver對象所接收的所有廣播。
因為一個MyReceiver這樣的對象可以用來同時接收多種廣播類型。

AMS最后使用mReceiverResolver來根據發送的廣播對應的IntentFilter找到合適的receiver并
調用。

廣播發送過程

在Service或Activity中,這里假設是MyService中,通知MyActivity更新進度:

int progress = 1;
...

Intent intent = new Intent("com.hxw.bot.broadcast.ACTION");
intent.putExtra("progress", progress);

sendBroadcast(intent);

階段1:發送廣播消息給AMS

廣播發送者,即Activity或Service組件,將一個特點類型的廣播發送給AMS。
這里是MyActivity,發送Action為"com.hxw.bot.broadcast.ACTION"的廣播。

Step0:Service.sendBroadcast

廣播使用intent對象描述,其Action即廣播的類型,intent也可以攜帶必要的數據。

Step1:ContextWrapper.sendBroadcast

Step2:ContextImpl.sendBroadcast

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

Step3:ActivityManagerProxy.broadcastIntent

打包參數,向AMS發起進程間通信:
mRemote.transact(BROADCAST_INTENT_TRANSACTION, data, reply, 0);

階段2:AMS找到接收者,通知其線程的消息隊列處理廣播

AMS收到一個廣播后,找到與這個廣播對應的接收者,將它們添加到廣播調度隊列。然后向創建AMS的線程的消息隊列發送一個類型為BROADCAST_INTENT_MSG的消息。對于廣播發送者來說,一個廣播發送完成了。

AMS.unbroadcastIntent:同步和消息隊列?

廣播發送者向AMS發起的BROADCAST_INTENT_TRANSACTION操作是同步RPC,響應方法應該盡快返回。
對于Binder-IPC通信,AMS.unbroadcastIntent()的執行是在Binder線程中的,Binder線程一般也應該盡快執行完畢。
一個進程對應的Binder線程不止一個,所以AMS.unbroadcastIntent()是同步的,它將將廣播的處理轉為AMS被創建時的線程中消息隊列對消息的處理,自身不執行廣播的發送。

廣播的發送是異步的,發送者不會等待AMS實際將廣播發送給接收者操作完成。

階段3:AMS消息隊列處理BROADCAST_INTENT_MSG

當AMS所運行線程的消息隊列中BROADCAST_INTENT_MSG消息被處理時,AMS從廣播調度隊列中找到需要接收此廣播的廣播接收者,并將對應的廣播發送給它們所運行在的應用程序進程。

AMS.performReceiveLocked

static void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,
        Intent intent, int resultCode, String data, Bundle extras,
        boolean ordered, boolean sticky) throws RemoteException {
    // Send the intent to the receiver asynchronously using one-way binder calls.
    if (app != null && app.thread != null) {
        // If we have an app thread, do the call through that so it is
        // correctly ordered with other one-way calls.
        app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
                data, extras, ordered, sticky);
    } else {
        receiver.performReceive(intent, resultCode, data, extras, ordered, sticky);
    }
}

AMS發送給目標進程廣播時,采用異步進程間通信方式。
發送給一個Binder對象的所有異步事務都保存在一個異步事務隊列中,其中的事務每次只處理一個,就是隊列頭部的異步事務。所以,AMS發送給同一個應用程序進程的所有廣播都可以被按照發送順序來串行地接收和處理。

ApplicationThreadProxy.scheduleRegisteredReceiver():

mRemote.transact(SCHEDULE_REGISTERED_RECEIVER_TRANSACTION, data, null,
               IBinder.FLAG_ONEWAY);

階段4:接收者進程在主線程消息隊列中響應廣播

廣播接收者所運行在的應用程序進程收到AMS發送的廣播后,并不是直接將收到的廣播分發給MyReceiver處理,而將廣播封裝為一個消息,發送到主線程的消息隊列中。消息被處理時,應用程序進程在主線程中將消息所描述的廣播發送給相應的廣播接收者MyReceiver。

ApplicationThread.scheduleRegisteredReceiver

// This function exists to make sure all receiver dispatching is
// correctly ordered, since these are one-way calls and the binder driver
// applies transaction ordering per object for such calls.
public void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent,
        int resultCode, String dataStr, Bundle extras, boolean ordered,
        boolean sticky) throws RemoteException {
    receiver.performReceive(intent, resultCode, dataStr, extras, ordered, sticky);
}

參數receiver就是注冊過程提供的InnerReceiver(見LoadedApk.java)對象。

接收者所在線程(見ActivityThread.java)將intent所表示的廣播封裝為一個消息(android.os.Message),然后發送到主線程消息隊列中。

Args.run

final class LoadedApk {
  ...
  static final class ReceiverDispatcher {
    final IIntentReceiver.Stub mIIntentReceiver; // 作為跨進程接口實例的Binder對象
    final BroadcastReceiver mReceiver; // MyReceiver
    final Context mContext; // MyActivity對象
    final Handler mActivityThread; // 主線程的handler
    ...

    final static class InnerReceiver extends IIntentReceiver.Stub {
            final WeakReference<LoadedApk.ReceiverDispatcher> mDispatcher;
            final LoadedApk.ReceiverDispatcher mStrongRef;
            ...
    }

    final class Args implements Runnable {
      private Intent mCurIntent;
      ...
      public void run() {
        IActivityManager mgr = ActivityManagerNative.getDefault();
        ...
        BroadcastReceiver receiver = mReceiver;
        ...
        receiver.onReceive(mContext, intent);
        ...
        mgr.finishReceiver(mIIntentReceiver,
                                   mCurCode, mCurData, mCurMap, false);
      }
    }
  }
}

這里,主線程執行Args.run()方法,得到關聯的MyReceiver調用其onReceive()。
如果當前廣播是有序廣播,那么onReceive()執行完畢后調用mgr.finishReceiver()
通知AMS將廣播傳遞給下一個接收者。

補充

  • Binder線程池
  • Binder異步通信
  • sticky粘性廣播
    接收者可以接收到對應類型的它注冊前的最后一個廣播。

(本文使用Atom編寫)


文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()