BroadcastReceiver 的工作過程分析

xbs530 8年前發布 | 10K 次閱讀 Android開發 移動開發 BroadcastReceiver

在 Android 中,廣播的注冊分為靜態注冊和動態注冊,靜態注冊是指在 AndroidManifest 文件中注冊,動態注冊是指在 Java 代碼中通過 registerReceiver 方法注冊的。靜態注冊的廣播在應用安裝時由系統自動完成注冊,具體來說是由 PMS (PackageManagerService)來完成注冊的。本文只分析廣播的動態注冊過程。

廣播的注冊過程

首先,分析過程當然從注冊的入口說起,我們平常在 Activity 內寫 registerReceiver 來注冊動態廣播調用的是父類 ContextWrapper 內的 registerReceiver 方法,代碼如下:

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

內部使用的是典型的橋接模式,轉而調用 mBase 的 registerReceiver 方法,這里的 mBase 是一個 Context 類型對象,其實現類是 ContextImpl (在 Activity 的啟動過程和創建過程中可以分析得出其實現類是 ContextImpl ),所以下面直接來看 ContextImpl 的 registerReceiver 方法,如下:

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

繼續跟下去:

@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
 String broadcastPermission, Handler scheduler) {
    return registerReceiverInternal(receiver, getUserId(),
            filter, broadcastPermission, scheduler, getOuterContext());
}

內部調用了 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;
    }
}

上面一段代碼不長,也很好理解,看代碼的最后面一段:

...
try {
    return ActivityManagerNative.getDefault().registerReceiver(
            mMainThread.getApplicationThread(), mBasePackageName,
            rd, filter, broadcastPermission, userId);
} catch (RemoteException e) {
    return null;
}
...

先看這個 ActivityManagerNative 的 getDefault() 方法:

static public IActivityManager getDefault() {
    return gDefault.get();
}

返回類型是 IActivity Manager ,是一個 Binder 接口,實際上就是 ActivityManagerService (后面簡稱 AMS ),因為 ActivityManagerNative 繼承自 Binder 且實現了 IActivityManager 接口,而 AMS 繼承自 ActivityManagerNative 。

所以到這里可以得到的結論是,注冊廣播時,最終會跨進程調用交給 AMS 去完成注冊。

因為需要 AMS 來注冊,所以需要把廣播傳給 AMS ,但是這里 ActivityManagerService 的 registerReceiver 方法的第三個參數傳的是 IIntentReceiver 類型的 rd ,為 IIntentReceiver 類型對象,不像類似啟動 Service 或者 Activity 直接傳遞 Intent 給 AMS 那樣來直接傳遞廣播。之所以采用 IIntentReceiver 而不是直接采用 BroadcastReceiver ,這是因為上述注冊過程是一個進程間通信的過程,而 BroadcastReceiver 作為 Android 的一個組件是不能直接跨進程傳遞的,所以需要 IIntentReceiver 來中轉一下,毫無疑問, IIntentReceiver 必須是一個 Binder 接口。

所以看第三個參數 IIntentReceiver 的實例化過程,其中可以看到:

...
rd = mPackageInfo.getReceiverDispatcher(
    receiver, context, scheduler,
    mMainThread.getInstrumentation(), true);
...

mPackageInfo 為 LoadApk 類型對象,看 getReceiverDispatcher 方法實現:

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();
    }
}

最后一行 return rd.getIIntentReceiver(); ,這里的 rd 為 LoadedApk 的內部類 ReceiverDispatcher , ReceiverDispatcher 的內部封裝了 BroadcastReceiver 和 InnerReceiver ( InnerReceiver 為 ReceiverDispatcher 的內部類)對象。這個 InnerReceiver 繼承自 IIntentReceiver.Stub ,很明顯是 AIDL 的形式,沒錯,就是 IIntentReceiver 的具體實現,其也含有 ReceiverDispatcher 的弱引用。 ReceiverDispatcher的 getIIntentReceiver() 方法返回的就是這個 InnerReceiver 對象,將其傳遞給了 AMS 。 ReceiverDispatcher 構造器實現:

ReceiverDispatcher(BroadcastReceiver receiver, Context context,
        Handler activityThread, Instrumentation instrumentation,
        boolean registered) {
    if (activityThread == null) {
        throw new NullPointerException("Handler must not be null");
    }

    mIIntentReceiver = new InnerReceiver(this, !registered);
    mReceiver = receiver;
    mContext = context;
    mActivityThread = activityThread;
    mInstrumentation = instrumentation;
    mRegistered = registered;
    mLocation = new IntentReceiverLeaked(null);
    mLocation.fillInStackTrace();
}

再來梳理一遍,跨進程交由 AMS 去注冊廣播的時候,將 InnerReceiver 對象傳遞給了 AMS , InnerReceiver 是 ReceiverDispatcher 的內部類, ReceiverDispatcher 內保存了要注冊的 BroadcastReceiver 和繼承了 IIntentReceiver.Stub 的 InnerReceiver , InnerReceiver 也有其外部類的 ReceiverDispatcher 的引用,后面可以方便的調用 ReceiverDispatcher 內部的 BroadcastReceiver 的 onReceive() 方法。

下面再看 AMS 中的 registerReceiver 的方法,這個方法很長,只看關鍵的部分:

public Intent registerReceiver(IApplicationThread caller, String callerPackage,
 IIntentReceiver receiver, IntentFilter filter, String permission, int userId) {
    ...
    ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
    ....
    // 將遠程receiver(IIntentReceiver)對象保存起來
    if (rl == null) {
        ...
        mRegisteredReceivers.put(receiver.asBinder(), rl);
        ...
    }

    ....
    BroadcastFilter bf = new BroadcastFilter(filter/*遠程IntentFilter*/, rl, callerPackage,
            permission, callingUid, userId);
    rl.add(bf);
    if (!bf.debugCheck()) {
        Slog.w(TAG, "==> For Dynamic broadcast");
    }
    // 保存遠程IntentFilter
    mReceiverResolver.addFilter(bf);
    ....

rl 是一個 ReceiverList 對象,ReceiverList 繼承自 ArrayList,只用于保存 BroadcastFilter 的。BroadcastFilter 看源碼確實是封裝了一些信息,有那個rl,還有包名,權限,UID , UserID什么之類的。因為一個 Receiver 可能被多個 IntentFilter 注冊,所以 Android 采用了一個 HashMap 數據結構,key 就是那個傳遞到AMS的那個IIntentReceiver Binder(可以理解成一個Receiver,因為Receiver不能直接傳,前面有說道),value 就是這個rl ReceiverList 對象了(可以理解其里面全是這個 Receiver 注冊的IntentFilter,BroadcastFilter 就是繼承自 IntentFilter 的) ,所以AMS注冊時,先會取出這個 Receiver 之前已注冊過的所有的意圖集合( ReceiverList )

最終在 AMS 內,將遠程 receiver ( IIntentReceiver ) 對象和遠程 IntentFilter 保存起來,完成動態廣播的注冊。

廣播的發送與接收過程

下面開始分析發送廣播和接收廣播的過程,也是先從 ContextImpl 的 sendBroadcast 方法出發:

@Override
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, null, false, false,
                getUserId());
    } catch (RemoteException e) {
        throw new RuntimeException("Failure from system", e);
    }
}

內部沒干什么事情,由上面分析可知,直接跨進程交由AMS處理,調用了 AMS 的 broadcastIntent 方法,在 AMS 的 broadcastIntent 方法內調用了 broadcastIntentLocked 方法,這個方法很長,先看該方法的最前面有下面這句話:

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

從注釋可以看出,添加這個標記的目的是默認廣播不會傳遞給那些已經停止的應用,一個應用處于停止狀態有兩種情形:

  • 應用安裝后未運行
  • 應用被手動或者其他應用強行停止了

從Android 3.1開始,為Intent添加了兩個標記位:

  • FLAG_EXCLUDE_STOPPED_PACKAGES : 表示不包含已經停止的應用,這個時候廣播不會發送給已經停止的應用
  • FLAG_INCLUDE_STOPPED_PACKAGES : 表示包含已經停止的應用,這個時候廣播會發送給已經停止的應用

從 Android 3.1 開始,系統為所有的廣播默認添加了 FLAG_EXCLUDE_STOPPED_PACKAGES 標志,這樣做是為了防止廣播無意間或者在不必要的時候調起已經停止運行的應用。如果的確需要調起已經停止的應用,那么只需要為廣播的Intent添加 FLAG_INCLUDE_STOPPED_PACKAGES 標記即可。當兩個標記共存時,以 FLAG_INCLUDE_STOPPED_PACKAGES 為準。

下面繼續分析發送流程,在 broadcastIntentLocked 內部,會根據 intent-filter 查找出匹配的廣播接收者并經過一系列的過濾,最終會將滿足條件的廣播接收者添加到 BroadcastQueue 中,接著 BroadcastQueue 就會將廣播發送給相應的廣播接收者,過程如下:

...
if (!ordered && NR > 0) {
    // If we are not serializing this broadcast, then send the
    // registered receivers separately so they don't wait for the
    // components to be launched.
    final BroadcastQueue queue = broadcastQueueForIntent(intent);
    BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
            callerPackage, callingPid, callingUid, resolvedType, requiredPermissions,
            appOp, brOptions, registeredReceivers, resultTo, resultCode, resultData,
            resultExtras, ordered, sticky, false, userId);
    if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing parallel broadcast " + r);
    final boolean replaced = replacePending && queue.replaceParallelBroadcastLocked(r);
    if (!replaced) {
        // 保存無序廣播
        queue.enqueueParallelBroadcastLocked(r);
        queue.scheduleBroadcastsLocked();
    }
    registeredReceivers = null;
    NR = 0;
}
...
if ((receivers != null && receivers.size() > 0)
        || resultTo != null) {
    BroadcastQueue queue = broadcastQueueForIntent(intent);
    BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
            callerPackage, callingPid, callingUid, resolvedType,
            requiredPermissions, appOp, brOptions, receivers, resultTo, resultCode,
            resultData, resultExtras, ordered, sticky, false, userId);

    if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing ordered broadcast " + r
            + ": prev had " + queue.mOrderedBroadcasts.size());
    if (DEBUG_BROADCAST) Slog.i(TAG_BROADCAST,
            "Enqueueing broadcast " + r.intent.getAction());

    boolean replaced = replacePending && queue.replaceOrderedBroadcastLocked(r);
    if (!replaced) {
        // 保存有序廣播
        queue.enqueueOrderedBroadcastLocked(r);
        queue.scheduleBroadcastsLocked();
    }
}
...

對應 BroadcastQueue 的 enqueueParallelBroadcastLocked 和 enqueueOrderedBroadcastLocked 兩個方法實現:

public void enqueueParallelBroadcastLocked(BroadcastRecord r) {
    // mParallelBroadcasts 集合保存無序廣播
    mParallelBroadcasts.add(r);
    r.enqueueClockTime = System.currentTimeMillis();
}

public void enqueueOrderedBroadcastLocked(BroadcastRecord r) {
    // mOrderedBroadcasts 集合保存有序廣播
    mOrderedBroadcasts.add(r);
    r.enqueueClockTime = System.currentTimeMillis();
}

在最后的 queue.scheduleBroadcastsLocked(); 內看廣播的發送過程,下面是 BroadcastQueue 的 scheduleBroadcastsLocked 方法:

public void scheduleBroadcastsLocked() {
    if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Schedule broadcasts ["
            + mQueueName + "]: current="
            + mBroadcastsScheduled);

    if (mBroadcastsScheduled) {
        return;
    }
    mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
    mBroadcastsScheduled = true;
}

scheduleBroadcastsLocked 里面并沒有實際的處理廣播,而是發送了一個 BROADCAST_INTENT_MSG 消息:

...
case BROADCAST_INTENT_MSG: {
    if (DEBUG_BROADCAST) Slog.v(
            TAG_BROADCAST, "Received BROADCAST_INTENT_MSG");
    processNextBroadcast(true);
} break;
...

收到消息后,調用了 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_BROADCAST, "Processing parallel broadcast ["
            + mQueueName + "] " + r);
    for (int i=0; i<N; i++) {
        Object target = r.receivers.get(i);
        if (DEBUG_BROADCAST)  Slog.v(TAG_BROADCAST,
                "Delivering non-ordered on [" + mQueueName + "] to registered "
                + target + ": " + r);
        deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false);
    }
    addBroadcastToHistoryLocked(r);
    if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Done with parallel broadcast ["
            + mQueueName + "] " + r);
}

通過注釋可以看到, mParallelBroadcasts 中保存的都是無序廣播,系統會遍歷這個集合,然后將廣播發送給它們的所有接收者,具體過程是通過 deliverToRegisteredReceiverLocked 方法實現的,在 deliverToRegisteredReceiverLocked 方法內部,調用了 performReceiveLocked 方法,如下:

private static void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,
 Intent intent, int resultCode, String data, Bundle extras,
 boolean ordered, boolean sticky, int sendingUser) throws RemoteException {
    // Send the intent to the receiver asynchronously using one-way binder calls.
    if (app != null) {
        if (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, sendingUser, app.repProcState);
        } else {
            // Application has died. Receiver doesn't exist.
            throw new RemoteException("app.thread must not be null");
        }
    } else {
        receiver.performReceive(intent, resultCode, data, extras, ordered,
                sticky, sendingUser);
    }
}

上面的 app.thread 就是我們最開始在 ContextImpl 中的注冊廣播的時候 registerReceiverInternal 內傳給AMS的 mMainThread.getApplicationThread() :

...
return ActivityManagerNative.getDefault().registerReceiver(
        mMainThread.getApplicationThread(), mBasePackageName,
        rd, filter, broadcastPermission, userId);
...

所以 app.therad 就是 ApplicationThread 類型對象,下面看 ApplicationThread 的 scheduleRegisteredReceiver 方法實現:

public void scheduleRegisteredReceiver(IIntentReceiver receiver, Intent intent,
 int resultCode, String dataStr, Bundle extras, boolean ordered,
 boolean sticky, int sendingUser, int processState) throws RemoteException {
    updateProcessState(processState, false);
    receiver.performReceive(intent, resultCode, dataStr, extras, ordered,
            sticky, sendingUser);
}

直接調用了 IIntentReceiver 的 performReceive 方法,這個 IIntentReceiver 就是我們上面傳遞給 AMS 的那個,其實現類是 LoadApk 的內部類 InnerReceiver ,而 InnerReceiver 的 performReceive 方法內轉而調用了其內部之前保存著的 ReceiverDispatcher 的 performReceive 方法,所以下面直接看 ReceiverDispatcher 的 performReceive 方法:

public void performReceive(Intent intent, int resultCode, String data,
 Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
    if (ActivityThread.DEBUG_BROADCAST) {
        int seq = intent.getIntExtra("seq", -1);
        Slog.i(ActivityThread.TAG, "Enqueueing broadcast " + intent.getAction() + " seq=" + seq
                + " to " + mReceiver);
    }
    Args args = new Args(intent, resultCode, data, extras, ordered,
            sticky, sendingUser);
    if (!mActivityThread.post(args)) {
        if (mRegistered && ordered) {
            IActivityManager mgr = ActivityManagerNative.getDefault();
            if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
                    "Finishing sync broadcast to " + mReceiver);
            args.sendFinished(mgr);
        }
    }
}

看 mActivityThread.post(args) ,這個 mActivityThread 是一個 Handler ,其實就是 ActivityThread 中的那個 mH ,是一個與 UI 主線程相關聯的 Handler , Args 類實現了 Runnable 接口,所以其 run 方法是在主線程執行的,所以看 Args 的 run 方法:

public void run() {
    final BroadcastReceiver receiver = mReceiver;
    ...
    try {
        ClassLoader cl =  mReceiver.getClass().getClassLoader();
        intent.setExtrasClassLoader(cl);
        setExtrasClassLoader(cl);
        receiver.setPendingResult(this);
        receiver.onReceive(mContext, intent);
    } catch (Exception e) {
       ...
    }
    ...
}

很明顯,在這里終于回調了 mReceiver 的 onReceive 方法,也是就我們自己繼承 BroadcastReceiver 時所必須實現的那個 onReceive 方法,而且是在主線程中執行的。

 

來自:http://melodyxxx.com/2016/09/24/broadcast-receiver-analysis/

 

 本文由用戶 xbs530 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!