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> 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 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();
                        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 mRegisteredReceivers = new HashMap<>();
    //8、存储BroadcastFilter的广播解析器
    /**
     * Resolver for broadcast intents to registered receivers.
     * Holds BroadcastFilter (subclass of IntentFilter).
     */
    final IntentResolver mReceiverResolver
            = new IntentResolver() {...};
    ...
    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静态注册指的是在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 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 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源码解析(二)

你可能感兴趣的:(Android,源码解析,android)