1. 程式人生 > >Android四大元件——BroadcastReceiver(原理篇)

Android四大元件——BroadcastReceiver(原理篇)

前言

Android四大元件——BroadcastReceiver(基礎篇)裡面介紹了BroadcastReceiver相關的基礎知識,本文將從Android 8.0原始碼來分析一下廣播的註冊和接收原理。

BroadcastReceiver的註冊

Android系統中BroadcastReceiver的註冊方式分為動態註冊和靜態註冊兩種。動態註冊必須在程式執行期動態註冊,其實際的註冊動作由ContextImpl物件完成;靜態註冊則是在AndroidManifest.xml中宣告的。在基礎篇中提到過,因為靜態註冊耗電、佔記憶體、不受程式生命週期影響,所以Google在Android 8.0上禁止了大部分廣播的靜態註冊,以此來減少耗電、增加待機時間、節省記憶體空間、提升效能。

動態註冊過程原始碼分析

Activity是通過Context類的registerReceiver()方法進行動態註冊廣播監聽的。Context是一個抽象類,它是應用端和AMS、WMS等系統服務進行通訊的介面,Activity、Service和Application都是繼承它的子類。Context的實現類是ContextImpl,也就是說註冊時最終呼叫到的是ContextImpl中的registerReceiver方法。下面將以registerReceiver為入口一步步分析廣播是如何動態註冊和接收的。

ContextImpl中的registerReceiver方法最終會呼叫本類的私有方法registerReceiverInternal。在這個方法裡面主要做了兩件事情,一件是通過LoadedApk類提供的方法獲得IIntentReceiver的例項,另一件是通過ActivityManagerService.registerReceiver方法把廣播註冊到AMS中。

/**
 * Common implementation of Context API, which provides the base
 * context object for Activity and other application components.
 */
class ContextImpl extends Context {
    ...
    private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,
            IntentFilter filter, String broadcastPermission,
            Handler scheduler, Context context, int flags) {
        //1、通過LoadedApk類提供的方法獲得IIntentReceiver的例項
        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();
            }
        }

        //2、通過ActivityManagerService.registerReceiver方法把廣播註冊到AMS中
        try {
            final Intent intent = ActivityManager.getService().registerReceiver(
                    mMainThread.getApplicationThread(), mBasePackageName, rd, filter,
                    broadcastPermission, userId, flags);
            if (intent != null) {
                intent.setExtrasClassLoader(getClassLoader());
                intent.prepareToEnterProcess();
            }
            return intent;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }
    ...
}

接下來跳轉到LoadedApk和ActivityManagerService中看看在IIntentReceiver例項化和廣播註冊到AMS的時候具體做了什麼事情。

  • 客戶端(LoadedApk)

當Activity中有新的BroadcastReceiver被註冊,LoadedApk就會為他生成一個ReceiverDispatcher例項,然後把Context、BroadcastReceiver和ReceiverDispatcher三者的關係儲存到關係對映表中。其中,在ReceiverDispatcher的構造方法中生成了IIntentReceiver類的例項,並可以通過ReceiverDispatcher.getIIntentReceiver方法獲得。
LoadedApk的相關原始碼如下:

    /**
     * Local state maintained about a currently loaded .apk.
     * @hide
     */
    public final class LoadedApk {
        ...
        //3、Context、BroadcastReceiver和ReceiverDispatcher三者對映關係表
        private final ArrayMap<Context, ArrayMap<BroadcastReceiver, ReceiverDispatcher>> mReceivers
            = new ArrayMap<>();
        ...
        //4、把Context、BroadcastReceiver和ReceiverDispatcher三者對映關係儲存到對映表中
        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();
        }
    }
    ...
    static final class ReceiverDispatcher {
        ReceiverDispatcher(BroadcastReceiver receiver, Context context,
                Handler activityThread, Instrumentation instrumentation,
                boolean registered) {
            if (activityThread == null) {
                throw new NullPointerException("Handler must not be null");
            }
           //5、例項化IIntentReceiver
            mIIntentReceiver = new InnerReceiver(this, !registered);
            mReceiver = receiver;
            mContext = context;
            mActivityThread = activityThread;
            mInstrumentation = instrumentation;
            mRegistered = registered;
            mLocation = new IntentReceiverLeaked(null);
            mLocation.fillInStackTrace();
        }
    }
    ...
    //6、獲取IIntentReceiver類的例項
    IIntentReceiver getIIntentReceiver() {
        return mIIntentReceiver;
    }
    ...
}
  • 服務端(ActivityManagerService)

AMS在收到客戶端廣播註冊請求時,會把提供服務的IIntentReceivers介面、ReceiverList和BroadcastFilter的對映關係儲存到對映關係表中。同時,把BroadcastFilter儲存到廣播解析器IntentResolver中。

public class ActivityManagerService extends IActivityManager.Stub
        implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
     ...
    //7、提供服務的IBinder介面、ReceiverList和BroadcastFilter的對映關係表
    /**
     * Keeps track of all IIntentReceivers that have been registered for broadcasts.
     * Hash keys are the receiver IBinder, hash value is a ReceiverList.
     */
    final HashMap<IBinder, ReceiverList> mRegisteredReceivers = new HashMap<>();
    //8、儲存BroadcastFilter的廣播解析器
    /**
     * Resolver for broadcast intents to registered receivers.
     * Holds BroadcastFilter (subclass of IntentFilter).
     */
    final IntentResolver<BroadcastFilter, BroadcastFilter> mReceiverResolver
            = new IntentResolver<BroadcastFilter, BroadcastFilter>() {...};
    ...
    public Intent registerReceiver(IApplicationThread caller, String callerPackage,
            IIntentReceiver receiver, IntentFilter filter, String permission, int userId,
            int flags) {
        ...
        synchronized (this) {
            if (callerApp != null && (callerApp.thread == null
                    || callerApp.thread.asBinder() != caller.asBinder())) {
                // Original caller already died
                return null;
            }
            //9、把IBinder、ReceiverList和BroadcastFilter的對映關係儲存到對映關係表中
            ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
            if (rl == null) {
                rl = new ReceiverList(this, callerApp, callingPid, callingUid,
                        userId, receiver);
                if (rl.app != null) {
                    rl.app.receivers.add(rl);
                } else {
                    try {
                        receiver.asBinder().linkToDeath(rl, 0);
                    } catch (RemoteException e) {
                        return sticky;
                    }
                    rl.linkedToDeath = true;
                }
                mRegisteredReceivers.put(receiver.asBinder(), rl);
            } else if (rl.uid != callingUid) {
                throw new IllegalArgumentException(
                        "Receiver requested to register for uid " + callingUid
                        + " was previously registered for uid " + rl.uid
                        + " callerPackage is " + callerPackage);
            } else if (rl.pid != callingPid) {
                throw new IllegalArgumentException(
                        "Receiver requested to register for pid " + callingPid
                        + " was previously registered for pid " + rl.pid
                        + " callerPackage is " + callerPackage);
            } else if (rl.userId != userId) {
                throw new IllegalArgumentException(
                        "Receiver requested to register for user " + userId
                        + " was previously registered for user " + rl.userId
                        + " callerPackage is " + callerPackage);
            }
            //10、把BroadcastFilter儲存到對應的ReceiverList中,BroadcastFilter裡面包含了IntentFilter和ReceiverList等相關資訊
            BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
                    permission, callingUid, userId, instantApp, visibleToInstantApps);
            rl.add(bf);
            if (!bf.debugCheck()) {
                Slog.w(TAG, "==> For Dynamic broadcast");
            }
            //11、把BroadcastFilter儲存到廣播解析器IntentResolver中
            mReceiverResolver.addFilter(bf);
            ...
        }
    }
    ...
}
  • 註冊小結

每一個應用都持有一個LoadedApk例項,LoadedApk例項中包含多個Context例項(一個程序對應多個Activity和Service以及一個Application),每個Context例項可能建立了多個BroadcastReceiver例項,每個BroadcastReceiver例項在動態註冊的時候都會生成一個對應的ReceiverDispatcher例項,每個ReceiverDispatcher例項內部又會由InnerReceiver類生成一個IIntentReceiver例項。這個IIntentReceiver例項在動態註冊BroadcastReceiver的時候會被傳遞給AMS,AMS會為每個IIntentReceiver例項建立一個ReceiverList例項,每個ReceiverList例項中儲存了多個BroadcastFilter例項,而這個BroadcastFilter例項裡面包含了具體的IntentFilter和ReceiverList等相關資訊。

BroadcastReceiver動態註冊關係示意圖

靜態註冊過程原始碼分析

BroadcastReceiver靜態註冊指的是在AndroidManifest.xml中宣告的接收器,在系統啟動的時候,會由PMS去解析。當AMS呼叫PMS的介面來查詢廣播註冊的時候,PMS會查詢記錄並且返回給AMS。

廣播的傳送和接收

以最簡單普通廣播為例,直接跳到Context的實現類ContextImpl的sendBroadcast方法。從原始碼看sendBroadcast裡面基本沒幹什麼事,直接去呼叫的AMS的broadcastIntent方法。

    @Override
    public void sendBroadcast(Intent intent) {
        warnIfCallingFromSystemProcess();
        String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
        try {
            intent.prepareToLeaveProcess(this);
            // 1、呼叫的AMS的broadcastIntent方法傳送廣播
            ActivityManager.getService().broadcastIntent(
                    mMainThread.getApplicationThread(), intent, resolvedType, null,
                    Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, false,
                    getUserId());
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

AMS中的broadcastIntent方法裡面也沒幹什麼,直接呼叫本類的broadcastIntentLocked方法(broadcastIntentLocked方法有600多行程式碼,OMG)。

    final int broadcastIntentLocked(ProcessRecord callerApp,
            String callerPackage, Intent intent, String resolvedType,
            IIntentReceiver resultTo, int resultCode, String resultData,
            Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions,
            boolean ordered, boolean sticky, int callingPid, int callingUid, int userId) {
	// Figure out who all will receive this broadcast.
        List receivers = null;
        // 2、註冊的BroadcastFilter列表,BroadcastFilter裡面包含了IntentFilter和ReceiverList等相關資訊
        List<BroadcastFilter> registeredReceivers = null;
        // Need to resolve the intent to interested receivers...
        if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY)
                 == 0) {
            receivers = collectReceiverComponents(intent, resolvedType, callingUid, users);
        }
        if (intent.getComponent() == null) {
            if (userId == UserHandle.USER_ALL && callingUid == SHELL_UID) {
                // Query one target user at a time, excluding shell-restricted users
                for (int i = 0; i < users.length; i++) {
                    if (mUserController.hasUserRestriction(
                            UserManager.DISALLOW_DEBUGGING_FEATURES, users[i])) {
                        continue;
                    }
                    //3、根據intent從廣播解析器mReceiverResolver中查詢符合的BroadcastFilter列表
                    List<BroadcastFilter> registeredReceiversForUser =
                            mReceiverResolver.queryIntent(intent,
                                    resolvedType, false /*defaultOnly*/, users[i]);
                    if (registeredReceivers == null) {
                        registeredReceivers = registeredReceiversForUser;
                    } else if (registeredReceiversForUser != null) {
                        registeredReceivers.addAll(registeredReceiversForUser);
                    }
                }
            } else {
                //4、根據intent從廣播解析器mReceiverResolver中查詢符合的BroadcastFilter列表
                registeredReceivers = mReceiverResolver.queryIntent(intent,
                        resolvedType, false /*defaultOnly*/, userId);
            }
        }

        final boolean replacePending =
                (intent.getFlags()&Intent.FLAG_RECEIVER_REPLACE_PENDING) != 0;
        int NR = registeredReceivers != null ? registeredReceivers.size() : 0;
        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.
            if (isCallerSystem) {
                checkBroadcastFromSystem(intent, callerApp, callerPackage, callingUid,
                        isProtectedBroadcast, registeredReceivers);
            }
            final BroadcastQueue queue = broadcastQueueForIntent(intent);
            //5、封裝成BroadcastRecord類例項
            BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
                    callerPackage, callingPid, callingUid, callerInstantApp, 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) != null);
            // Note: We assume resultTo is null for non-ordered broadcasts.
            if (!replaced) {
                //6、把BroadcastRecord類例項新增到併發的廣播序列中並準備傳送
                queue.enqueueParallelBroadcastLocked(r);
                queue.scheduleBroadcastsLocked();
            }
            registeredReceivers = null;
            NR = 0;
        }
    }

接下來看一下BroadcastRecord類中的scheduleBroadcastsLocked方法是如何把廣播發送到對應的BroadcastReceiver中的。

/**
 * BROADCASTS
 *
 * We keep two broadcast queues and associated bookkeeping, one for those at
 * foreground priority, and one for normal (background-priority) broadcasts.
 */
public final class BroadcastQueue {
	...
	//7、scheduleBroadcastsLocked方法傳送BROADCAST_INTENT_MSG訊息給handler
	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;
    }
	...
	private final class BroadcastHandler extends Handler {
        public BroadcastHandler(Looper looper) {
            super(looper, null, true);
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case BROADCAST_INTENT_MSG: {
                    if (DEBUG_BROADCAST) Slog.v(
                            TAG_BROADCAST, "Received BROADCAST_INTENT_MSG");
                    //8、handler執行processNextBroadcast方法開始傳送廣播
                    processNextBroadcast(true);
                } break;
                case BROADCAST_TIMEOUT_MSG: {
                    synchronized (mService) {
                        broadcastTimeoutLocked(true);
                    }
                } break;
            }
        }
    }
	...
	final void processNextBroadcast(boolean fromMsg) {
		...
		do {
			...

			if (r.receivers == null || r.nextReceiver >= numReceivers
				|| r.resultAbort || forceReceive) {
				// No more receivers for this broadcast!  Send the final
				// result if requested...
				if (r.resultTo != null) {
					try {
						//9、逐個傳送BroadcastRecord中的廣播
						performReceiveLocked(r.callerApp, r.resultTo,
							new Intent(r.intent), r.resultCode,
							r.resultData, r.resultExtras, false, false, r.userId);
						// Set this to null so that the reference
						// (local and remote) isn't kept in the mBroadcastHistory.
						r.resultTo = null;
					} catch (RemoteException e) {
						r.resultTo = null;
					}
				}
				...
			}
		} while (r == null);
		...
	}
	...
	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.
                try {
                    //10、由ActivityThread中的scheduleRegisteredReceiver方法傳送給相應的的BroadcastReceiver
                    app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
                            data, extras, ordered, sticky, sendingUser, app.repProcState);
                // TODO: Uncomment this when (b/28322359) is fixed and we aren't getting
                // DeadObjectException when the process isn't actually dead.
                //} catch (DeadObjectException ex) {
                // Failed to call into the process.  It's dying so just let it die and move on.
                //    throw ex;
                } catch (RemoteException ex) {
                    // Failed to call into the process. It's either dying or wedged. Kill it gently.
                    synchronized (mService) {
                        Slog.w(TAG, "Can't deliver broadcast to " + app.processName
                                + " (pid " + app.pid + "). Crashing it.");
                        app.scheduleCrash("can't deliver broadcast");
                    }
                    throw ex;
                }
            } 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);
        }
    }
	...
}

在ActivityThread的scheduleRegisteredReceiver方法中執行了IIntentReceiver.performReceive方法。

        // 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, int sendingUser, int processState) throws RemoteException {
            updateProcessState(processState, false);
            receiver.performReceive(intent, resultCode, dataStr, extras, ordered,
                    sticky, sendingUser);
        }

逐個IIntentReceiver介面的實現是在LoadedApk中的,回去呼叫LoadedApk的performReceive方法,最終調到Args.getRunnable方法。

               public final Runnable getRunnable() {
					...
                    try {
                        ClassLoader cl = mReceiver.getClass().getClassLoader();
                        intent.setExtrasClassLoader(cl);
                        intent.prepareToEnterProcess();
                        setExtrasClassLoader(cl);
                        receiver.setPendingResult(this);
                        //11、回撥BroadcastReceiver.onReceive方法
                        receiver.onReceive(mContext, intent);
                    } catch (Exception e) {
                        if (mRegistered && ordered) {
                            if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
                                    "Finishing failed broadcast to " + mReceiver);
                            sendFinished(mgr);
                        }
                        if (mInstrumentation == null ||
                                !mInstrumentation.onException(mReceiver, e)) {
                            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                            throw new RuntimeException(
                                    "Error receiving broadcast " + intent
                                            + " in " + mReceiver, e);
                        }
                    }
					...
                };
  • 傳送和接收小結

應用端呼叫系統服務(AMS)傳送廣播,AMS會去廣播解析器IntentResolver中查詢哪些BroadcastFilter跟這個廣播有關聯,然後把相關資訊封裝成 BroadcastRecord類的例項新增到廣播發送序列BroadcastQueue中逐個廣播。在BroadcastQueue中廣播的時候會從BroadcastRecord中獲得BroadcastFilter進而獲得對應的ReceiverList,ReceiverList中包含了對應的IIntentReceiver例項,通過這個IIntentReceiver例項就可以找到對應的BroadcastReceiver,呼叫其BroadcastReceiver.OnReceive方法把廣播傳遞給對應的BroadcastReceiver。

總結

通過上面的分析,我們深入瞭解了廣播接收器BroadcastReceiver是如何完成動態註冊的,以及廣播是如何傳送和被接收的。中間涉及到諸多個類的多個方法調來調去,看起來比較複雜,但是如果你看懂了之前BroadcastReceiver動態註冊的關係圖,理解起來就相對簡單了。

廣播接收器BroadcastReceiver的動態註冊過程可以簡單的理解為建立兩個對映關係的過程:
- 建立LoadedApk、Context、BroadcastReceiver和ReceiverDispatcher的對映關係。
- 建立ReceiverList和BroadcastFilter的對映關係。
這兩個對映關係共同持有同一個IIntentReceiver,IIntentReceiver是這兩個對映關係中間的橋樑,客戶端和服務端通過IIntentReceiver介面來完成程序間通訊。

知道了上面的兩個對映關係以及這兩個對映關係之間的關聯之處,廣播的傳送和接收就可以簡單的理解為一個反向關係查詢的過程。AMS根據Context廣播的Intent資訊對照對映關係表從BroadcastFilter開始,反向一層一層找到與之對應的BroadcastReceiver,最終完成BroadcastReceiver.OnReceive的呼叫,把Intent傳遞給對應的BroadcastReceiver。

參考文獻

《Android開發藝術探索》——9.4BroadcastReceiver的工作過程
BroadcastReceiver原始碼解析(一)
BroadcastReceiver原始碼解析(二)