前言

你好!
我是一只修仙的猿,欢迎阅读我的文章。

本篇文章的内容是讲解Broadcast的注册与发送的源码流程,不涉及Broadcast的使用。如果还没了解过广播如何使用的读者可以先去了解一下,但我相信不会的,因为广播在开发是很经常使用的。作为四大组件之一,广播的地位不言而喻。而了解其源码流程,可以更好地帮助我们了解他的工作流程,进而更好地了解Broadcast这个组件。这样在开发中,就会更加有自信去写代码,出现了bug也能更快地定位与解决。

本篇文章涉及大量的代码讲解,因而会显得非常枯燥无味。笔者加了大量的代码注释以及流程图来帮助读者理解。注释是非常重要的,代码外的讲解仅仅只是整体流程的把握,注释才是重点细节。还愿读者可以耐心阅读。

文章我以先把握整体流程,再到细节源码讲解的思路讲解。读者要先对整体流程有个把握,知道整体的方向再去阅读源码,这样不会在源码中丢失了方向,也不会不知道哪个地方是重点。读者亦可以边阅读边查看源码,加深印象。

笔者才疏学浅,有不同观点欢迎评论区或私信讨论。如需转载私信告知即可。
另外欢迎阅读笔者的个人博客一只修仙的猿的个人博客,更精美的UI,拥有更好的阅读体验。

本文大纲:

本文大纲.png

Broadcast注册流程

整体流程概述

broadcast注册整体流程

注册流程相对来说比较简单,这里涉及到两个进程:

  • Activity进程,即注册广播所在进程。
  • AMS进程,AMS即ActivityManagerService,该进程为AMS所在进程。

注册的流程为Activity进程把广播接收器发送给AMS,AMS把广播接收器缓存在内部。流程比较简单。不同的进程当然就涉及到跨进程通信,API29源码使用的是AIDL(Android Interface Define Language),如果不熟悉的读者可以先去了解一下,有帮助对源码的理解。当然我也会在源码讲解中适当做解释,但不会深入讲,因为本文的主题是Broadcast工作流程讲解。

源码讲解

源码流程图

Broadcast注册源码流程.png
  1. 首先直接调用到ContextWrapper的registerReceiver方法,Activity是继承自ContextWrapper的。registerReceiver是Context的抽象方法,ContentWrapper继承自Content,但ContextWrapper本身并没有真正实现了Context的抽象方法,而是使用了装饰者模式。Context的真正实现是他内部的一个变量:mBase。这是ContextImpl的实例,所以最终的真正实现是在ContextImpl中。

    /frameworks/base/core/java/android/content/ContextWrapper.java;
    public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
        // 直接调用mBase的方法。mBase是ContextImpl的实例,也是Context的真正实现。
        return mBase.registerReceiver(receiver, filter);
    }

    这里补充一下为什么mBase是ContextImpl的实例。如果你阅读过Activity的启动流程(点击前往Activity的启动流程文章),可以知道是在ActivityThread创建Activity的时候,会给Activity初始化Context。下面看一下这部分的源码:

    /frameworks/base/core/java/android/app/ActivityThread.java
    
    // ActivityThread调用此方法来启动Activity
    private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
       ...
       // 在这里创建了Content,可以看到他的类型是ContentImpl
       ContextImpl appContext = createBaseContextForActivity(r);
    
       ...
       // 在这里把appContext给到了activity。说明这个就是Activity里面的Context的具体实现
       appContext.setOuterContext(activity);
       activity.attach(appContext, this, getInstrumentation(), r.token,
         r.ident, app, r.intent, r.activityInfo, title, r.parent,
         r.embeddedID, r.lastNonConfigurationInstances, config,
         r.referrer, r.voiceInteractor, window, r.configCallback,
         r.assistToken);
       ...
    }
  1. 这一步是ContextImpl的逻辑。主要的任务就是构建IIntentReceiver并请求AMS来注册广播接收器。这里涉及到两个知识点:如何请求AMS,为什么不能直接把BroadcastReceiver传递给AMS。

    第一个问题采用的是跨进程通信技术AIDL(Android Interface Define Language)安卓接口定义语言,主要用于跨进程通信,这里就不详细展开,不然就篇幅过大。读者可自行了解。

    第二个问题,是因为广播涉及跨进程通信,而BoradcastReceiver本身并不支持跨进程,所以要进行封装一下。这个问题会在这一步的源码下面进行补充说明。

    后面的逻辑就交给AMS去处理了。

    /frameworks/base/core/java/android/app/ContextImpl.java;
    
    // 直接跳转
    public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
        return registerReceiver(receiver, filter, null, null);
    }
    
    // 再跳转
    public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
            String broadcastPermission, Handler scheduler) {
        return registerReceiverInternal(receiver, getUserId(),
                filter, broadcastPermission, scheduler, getOuterContext(), 0);
    }
    
    // 此方法主要是获取IIntentReceiver对象
    // 为什么要获取IIntentReceiver上面已经有讲了。
    // 并调用AMS在本地代理对象IActivityManagerService的方法来注册广播接收器
    private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,
            IntentFilter filter, String broadcastPermission,
            Handler scheduler, Context context, int flags) {
        IIntentReceiver rd = null;
        if (receiver != null) {
            // 判断mPackageInfo和context是否为空,选择不同的方式获得IIntentReceiver
            if (mPackageInfo != null && context != null) {
                if (scheduler == null) {
                    // 注意这里的scheduler是mMainThread.getHandler()
                    // 他是ActivityThread的内部类H,这里先不管有什么用,注意一下就好,后面会讲到。
                    scheduler = mMainThread.getHandler();
                }
                rd = mPackageInfo.getReceiverDispatcher(
                    receiver, context, scheduler,
                    mMainThread.getInstrumentation(), true);
            } else {
                ...
            }
        }
        try {
            // ActivityManager.getService()获取到的对象是AMS在本地的代理对象
            // 通过代理对象可以直接跨进程访问AMS
            // 调用IActivityManagerService的方法来启动
            final Intent intent = ActivityManager.getService().registerReceiver(
                    mMainThread.getApplicationThread(), mBasePackageName, rd, filter,
                    broadcastPermission, userId, flags);
            ...
        } 
        ...
    }
    

    这里深入讲一下IIntentReceiver。IIntentReceiver是一个接口,他的真正实现是InnerReceiver。InnerReceiver是ReceiverDispatcher的静态内部类。ReceiverDispatcher负责维护BroadcastReceiver和IIntentReceiver之间的通信。远程AMS调用InnerReceiver的方法,ReceiverDispatcher把逻辑转到BroadcastReceiver中。

    有读过Service启动流程(点击前往Service启动流程)的读者应该知道Service的绑定也是采用了类似的设计。可能你觉得有点绕,下面画一个图帮助你理解一下:

    IIntentReceiver各类的关系.png

    Map是LoadedApk类中维持的一个HashMap,key是BroadcastReceiver,value是ReceiverDispatcher。

  1. 这一步在AMS中的处理主要分为两个:注册粘性广播和注册普通广播。由于粘性广播在android5.0已经被废弃,所以这里只讲注册普通广播。AMS的工作主要是把传进来的广播接收器存起来。

    /frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java;
    
    public Intent registerReceiver(IApplicationThread caller, String callerPackage,
            IIntentReceiver receiver, IntentFilter filter, String permission, int userId,
            int flags) {
        ...;
        synchronized (this) {
            // 获取ReceiverList,如果不存在就创建一个
            ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
            if (rl == null) {
                rl = new ReceiverList(this, callerApp, callingPid, callingUid,
                        userId, receiver);
                ...
                mRegisteredReceivers.put(receiver.asBinder(), rl);
            } 
            ...
            // 创建BroadcastFilter并存起来
            BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
                    permission, callingUid, userId, instantApp, visibleToInstantApps);
            if (rl.containsFilter(filter)) {
                ...
            } else {
                rl.add(bf);
                ...
    
                mReceiverResolver.addFilter(bf);
            }
        }
        ...
    }

Broadcast广播流程

整体流程概述

广播有普通广播、有序广播和粘性广播三种。本文只讲普通广播的发送流程。

广播发送的整体流程

广播的发送涉及三个进程:

  • Activity进程,即发出广播的进程
  • AMS进程,上面讲过
  • 广播接收器所在进程

大体的流程是:

  • Activity把广播发送到AMS中
  • AMS首先检测广播是否合法,然后根据IntentFilter规则,把所有符合条件的广播接收器整理成一个队列
  • 依次遍历队列中的广播接收器,判断是否拥有权限
  • 把广播发送到广播接收器所在进程,回调广播的onReceive方法

源码讲解

第一步是从Activity所在进程,即发出广播的进程到AMS

广播发送流程图-1
  1. 这里同样是先调用ContextWrapper的方法,然后再调用ContextImp的方法,原因和前面一样,不再赘述,主要看ContextImp的实现。ContextImp的逻辑也很简单,简单处理后直接调用AMS的方法。

    /frameworks/base/core/java/android/content/ContextWrapper.java;
    public void sendBroadcast(Intent intent) {
        mBase.sendBroadcast(intent);
    }
    
    /frameworks/base/core/java/android/app/ContextImpl.java;
    public void sendBroadcast(Intent intent) {
        ...
        try {
            intent.prepareToLeaveProcess(this);
            // 直接调用AMS的方法进行广播
            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对广播的处理

广播发送流程图-2
  1. 这一步主要是检查广播是否合法,然后获取信息后跳转下个方法。

    /frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java;
    
    public final int broadcastIntent(IApplicationThread caller,
            Intent intent, String resolvedType, IIntentReceiver resultTo,
            int resultCode, String resultData, Bundle resultExtras,
            String[] requiredPermissions, int appOp, Bundle bOptions,
            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();
            try {
                // 跳转下个方法
                return broadcastIntentLocked(callerApp,
                        callerApp != null ? callerApp.info.packageName : null,
                        intent, resolvedType, resultTo, resultCode, resultData, resultExtras,
                        requiredPermissions, appOp, bOptions, serialized, sticky,
                        callingPid, callingUid, callingUid, callingPid, userId);
            }
            ...
        } 
    }

    这里看一下验证广播合法性的方法:

    final Intent verifyBroadcastLocked(Intent intent) {
    // 检查Intent不为null且有文件修饰符
    if (intent != null && intent.hasFileDescriptors() == true) {
      throw new IllegalArgumentException("File descriptors passed in Intent");
    }
    
    // 获取Intent的Flag
    int flags = intent.getFlags();
    
    // 当系统还没有启动时,判断这个广播是否有权限
    if (!mProcessesReady) {
      if ((flags&Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT) != 0) {
      } else if ((flags&Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) {
          Slog.e(TAG, "Attempt to launch receivers of broadcast intent " + intent
                      + " before boot completion");
              throw new IllegalStateException("Cannot broadcast before boot completed");
          }
      }
      ...
    }
  1. 这一步的方法代码非常多。主要完成的任务是:根据intentFilter筛选出符合条件的receiver,并按照优先级进行排序后存到队列中。并调用方法进行广播。

    /frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java;
    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 realCallingUid,
            int realCallingPid, int userId, boolean allowBackgroundActivityStarts) {
        ...
         // 默认不发送给已经停止的app
        intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);
        ...;
        // 获取广播接收器队列
        final BroadcastQueue queue = broadcastQueueForIntent(intent);
        BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
                callerPackage, callingPid, callingUid, callerInstantApp, resolvedType,
                requiredPermissions, appOp, brOptions, registeredReceivers, resultTo,
                resultCode, resultData, resultExtras, ordered, sticky, false, userId,
                allowBackgroundActivityStarts, timeoutExempt);    
        ...
        if (!replaced) {
            queue.enqueueParallelBroadcastLocked(r);
            // 调用方法进行广播
            queue.scheduleBroadcastsLocked();
        }
    
    }
  1. BroadcastQueue通过Handle来执行广播事务。

    /frameworks/base/services/core/java/com/android/server/am/BroadcastQueue.java;
    final BroadcastHandler mHandler;
    public void scheduleBroadcastsLocked() {
        ...
        // 判断是否正在执行
        if (mBroadcastsScheduled) {
            return;
        }
        // 通过handle来执行事务。BroadcastHandler是BroadcastQueue的一个内部类
        mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
        // 设置标志位表示正在执行
        mBroadcastsScheduled = true;
    }
    
    /frameworks/base/services/core/java/com/android/server/am/BroadcastQueue.java/BroadcastHandler.class;
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case BROADCAST_INTENT_MSG: {
                ...
                // 继续跳转
                processNextBroadcast(true);
            } break;
            ...
        }
    }
  1. 这一步BroadcastQueue会把列表中的无序与有序广播遍历执行进行广播。有序广播就不深入讲了,感兴趣的读者可自行去阅读源码。

    /frameworks/base/services/core/java/com/android/server/am/BroadcastQueue.java;
    final void processNextBroadcast(boolean fromMsg) {
        synchronized (mService) {
            // 继续跳转
            processNextBroadcastLocked(fromMsg, false);
        }
    }
    // 此方法会处理有序广播和无序广播,这里只讲解如何分发无序广播
    final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) {
            BroadcastRecord r;
            ...
            // 表示从Handle发送过来的消息已经处理了
            if (fromMsg) {
                mBroadcastsScheduled = false;
            }
    
            // 遍历无序广播列表,然后发送广播
            while (mParallelBroadcasts.size() > 0) {
                // 获取到无序广播
                r = mParallelBroadcasts.remove(0);
                ...
    
                for (int i=0; i<N; i++) {
                    Object target = r.receivers.get(i);
                    ...
                    // 这里进行发送广播
                    deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false, i);
                }
                addBroadcastToHistoryLocked(r);
                if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Done with parallel broadcast ["
                        + mQueueName + "] " + r);
            }
    }
    
  1. 这一部分的源码也非常多,主要的逻辑就是进行权限判断,如果通过权限判断,那么就会去调用广播接收器所在进程的ApplicationThread的方法进行广播。下面的逻辑就到了广播接收器所在进程了。

    /frameworks/base/services/core/java/com/android/server/am/BroadcastQueue.java;
    private void deliverToRegisteredReceiverLocked(BroadcastRecord r,
            BroadcastFilter filter, boolean ordered, int index) {
        ...
        try {
            if (DEBUG_BROADCAST_LIGHT) Slog.i(TAG_BROADCAST,
                    "Delivering to " + filter + " : " + r);
            if (filter.receiverList.app != null && filter.receiverList.app.inFullBackup) {
               ...
            } else {
                ...
                // 最后执行此方法进行广播
                performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
                        new Intent(r.intent), r.resultCode, r.resultData,
                        r.resultExtras, r.ordered, r.initialSticky, r.userId);
                ...
            }
            if (ordered) {
                r.state = BroadcastRecord.CALL_DONE_RECEIVE;
            }
        }
        ...
    }
    
    void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,
            Intent intent, int resultCode, String data, Bundle extras,
            boolean ordered, boolean sticky, int sendingUser)
            throws RemoteException {
        // app是ProcessRecord,表示广播接收器所在进程信息,app.thread是IApplicationThread
        // 这是ApplicationThread给AMS的Binder接口,通过他可以跨进程访问接收器所在进程
        // 这里主要就是判断进程以及IApplicationThread是否存在,直接调用ApplicationThread的方法。
        // 下面的逻辑就到了广播接收器的进程了。
        if (app != null) {
            if (app.thread != null) {
                try {
                    app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
                            data, extras, ordered, sticky, sendingUser, app.getReportedProcState());
                } 
                ...
            } 
            ...
        } else {
            receiver.performReceive(intent, resultCode, data, extras, ordered,
                    sticky, sendingUser);
        }
    }

这一部分是广播接收器所在进程的处理

广播发送流程图-3
  1. 这一步来到ApplicationThread,会直接调用InnerReceiver对请求进行处理,InnerReceiver会调用ReceiverDispatcher进行处理,最终会调用ActivityThread的方法,利用H切换逻辑到主线程进行处理。

    /frameworks/base/core/java/android/app/ActivityThread.java/ApplicationThread.class;
    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);
        // 还记得IIntentReceiver吗?这是一个ALDL接口,他的实现类是InnerReceiver
        // 忘记的话可以回到上面的内容看一下.下面跳转到他的方法
        receiver.performReceive(intent, resultCode, dataStr, extras, ordered,
                sticky, sendingUser);
    }
    
    /frameworks/base/core/java/android/app/LoadedApk.java/ReceiverDispatcher.java/InnerReceiver.java;
    public void performReceive(Intent intent, int resultCode, String data,
            Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
        ...
        if (rd != null) {
            // 这里的rd就是ReceiverDispatcher
            rd.performReceive(intent, resultCode, data, extras,
                              ordered, sticky, sendingUser);
        }    
        ...
    
    }
    
    /frameworks/base/core/java/android/app/LoadedApk.java/ReceiverDispatcher.java;
    public void performReceive(Intent intent, int resultCode, String data,
            Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
        // 这里把广播信息进行封装
        final Args args = new Args(intent, resultCode, data, extras, ordered,
                sticky, sendingUser);
        ...;
        // 这里的mActivityThread是ActivityThread的Handle内部类H,
        // 负责把逻辑切换到主线程进行事务处理
        // 这个Handle在我们实例化ReceiverDispatcher的时候会传进来,不知你是否还有印象
        // 下面我们就直接看这个Runable是什么
        if (intent == null || !mActivityThread.post(args.getRunnable())) {
            if (mRegistered && ordered) {
                IActivityManager mgr = ActivityManager.getService();
                if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
                        "Finishing sync broadcast to " + mReceiver);
                args.sendFinished(mgr);
            }
        }
    }
    
  1. 最后一步是在主线程中执行的,由上一步已经知道使用H类进行切线程。这一步是看给到H的Runable是怎么样的。这一步会在里面回调receiver的onReceive方法。到这里流程就走完了。

    /frameworks/base/core/java/android/app/LoadedApk.java/ReceiverDispatcher.java/Args.class;
    public final Runnable getRunnable() {
        return () -> {
            ...;
            try {
                ...;
                // 可以看到这里回调了receiver的方法,这样整个接收广播的流程就走完了。
                receiver.onReceive(mContext, intent);
            }
        }
    }

总结

文章整理了Android广播的注册与发送的源码流程,并简析了过程中涉及到的一些细节。相信读者现在对于广播的工作流程已经有了一定的理解了。

全文到此,希望对你有帮助.
笔者才疏学浅,有不同观点欢迎评论区或私信讨论。如需转载私信告知即可。
另外欢迎阅读笔者的个人博客一只修仙的猿的个人博客,更精美的UI,拥有更好的阅读体验。

参考文献

  • 《Android进阶解密》
  • 《Android开发艺术探索》