带你从源码学习安卓系统广播机制

广播是安卓四大组件之一,他可以帮助开发者实现进程间通信、线程间通信以及类似事件总线的解耦方式。在安卓系统中,从广播的作用范围来划分的话,可以分为本地广播和全局广播。本地广播是一种在应用进程内的广播方式,全局广播则是一种在进程间的广播方式(当然也支持进程内)。

本地广播和全局广播在使用上没有什么大的区别,但是在实现上,两者确大相径庭。接下来我们来看下安卓系统中是如何实现这两种广播的。

本文涉及的源码都是在安卓API29下分析的

本地广播

本地广播在系统中对应LocalBroadcastManager类,本地广播的实现几乎都在这个类里面。这是一个最终类,并且以单例形式对外提供调用(当然了,如果设计一个优秀的单例,最好不要这么写!可以参考这篇文章)。

  public final class LocalBroadcastManager {
    ......省略部分代码
    @NonNull
    public static LocalBroadcastManager getInstance(@NonNull Context context) {
      synchronized (mLock) {
        if (mInstance == null) {
          mInstance = new LocalBroadcastManager(context.getApplicationContext());
        }
        return mInstance;
      }
    }
    ......省略部分代码
  }

先看下构造方法具体做了哪些工作

    private LocalBroadcastManager(Context context) {
        mAppContext = context;
        mHandler = new Handler(context.getMainLooper()) {

            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case MSG_EXEC_PENDING_BROADCASTS:
                        executePendingBroadcasts();
                        break;
                    default:
                        super.handleMessage(msg);
                }
            }
        };
    }

可以看到,LocalBroadcastManager构造方法中保存了入参的全局上下文对象contextmAppContext,然后创建了主线程的Handler并引用为mHandler成员变量。

然后我们从本地广播的使用方法出发,看下具体的调用流程。

注册

我们先看下广播的注册方法registerReceiver

    LocalBroadcastManager instance = LocalBroadcastManager.getInstance(this);
    IntentFilter filter = new IntentFilter();
    filter.addAction("local_broadcast_test");
    instance.registerReceiver(receiver, filter);
    

该方法接收两个参数,一个BroadCastReceiver类型的接收者对象,一个IntentFilter类型的意图过滤器对象,这两个类开发者应该不陌生,前者是用来接收广播的消息,后者是用来对注册的广播的某种意义上的标识。

接下来,我们继续看下LocalBroadcastManagerregisterReceiver方法具体做了些什么工作。

    ....省略部分代码
    private final HashMap<BroadcastReceiver, ArrayList<ReceiverRecord>> mReceivers
            = new HashMap<>();
    private final HashMap<String, ArrayList<ReceiverRecord>> mActions = new HashMap<>();
    ....省略部分代码



    public void registerReceiver(@NonNull BroadcastReceiver receiver,
            @NonNull IntentFilter filter) {
        synchronized (mReceivers) {
            ReceiverRecord entry = new ReceiverRecord(filter, receiver);
            ArrayList<ReceiverRecord> filters = mReceivers.get(receiver);
            if (filters == null) {
                filters = new ArrayList<>(1);
                mReceivers.put(receiver, filters);
            }
            filters.add(entry);
            for (int i=0; i<filter.countActions(); i++) {
                String action = filter.getAction(i);
                ArrayList<ReceiverRecord> entries = mActions.get(action);
                if (entries == null) {
                    entries = new ArrayList<ReceiverRecord>(1);
                    mActions.put(action, entries);
                }
                entries.add(entry);
            }
        }
    }

registerReceiver方法中,LocalBroadcastManager其实就是将该需要注册的广播接收器以及意图过滤器信息保存到两个Map成员变量中。

mReceivers是以BroadcastReceiver为Key,ArrayList<ReceiverRecord>为Value的HashMap。

mActions是以action为Key,ArrayList<ReceiverRecord>为Value的HashMap。

上述代码中的ReceiverRecord类其实就是封装了广播接收者和过滤器信息的描述对象。看到这里,感觉这种缓存逻辑跟观察者模式的缓存订阅者有些相似。实际上,LocalBroadcastManager的在收到消息时,的确是遍历本地缓存列表,来发送消息的。

发送

接下来我们看下广播的发送逻辑,调用的具体方法为:

    Intent intent = new Intent("local_broadcast_test");
    // intent 可以携带数据
    LocalBroadcastManager localBroadcastManager = LocalBroadcastManager.getInstance(this);
    localBroadcastManager.sendBroadcast(intent);

核心方法就是sendBroadcast,我们来看下源码中具体的调用逻辑。

    public boolean sendBroadcast(@NonNull Intent intent) {
        synchronized (mReceivers) {
            // 下面都是intent携带的信息
            final String action = intent.getAction();
            final String type = intent.resolveTypeIfNeeded(
                    mAppContext.getContentResolver());
            final Uri data = intent.getData();
            final String scheme = intent.getScheme();
            final Set<String> categories = intent.getCategories();

            ArrayList<ReceiverRecord> entries = mActions.get(intent.getAction());
            // 如果检索到对应action已经被注册过
            if (entries != null) {
                // 广播接收者列表
                ArrayList<ReceiverRecord> receivers = null;
                // 遍历对应action检索到的接收者列表
                for (int i=0; i<entries.size(); i++) {
                    ReceiverRecord receiver = entries.get(i);
                    // 如果对应接收者正在被处理中,则跳过,处理下一个
                    if (receiver.broadcasting) {
                        continue;
                    }
                    // 根据intent和之前注册的广播的IntentFilter来匹配
                    int match = receiver.filter.match(action, type, scheme, data,
                            categories, "LocalBroadcastManager");
                    if (match >= 0) {
                        // 第一次的时候,接收者列表为null
                        if (receivers == null) {
                            receivers = new ArrayList<ReceiverRecord>();
                        }
                        // 将匹配上的receiver添加到列表中,并标记broadcasting属性为true
                        receivers.add(receiver);
                        receiver.broadcasting = true;
                    } else {
                        // 源码此处以日志形式输出不匹配原因
                    }
                }
                // 如果至少有一个匹配上的接收者对象,receivers就不为null,否则为null
                if (receivers != null) {
                    // 遍历有效接受者列表,设置broadcasting标记为false
                    for (int i=0; i<receivers.size(); i++) {
                        receivers.get(i).broadcasting = false;
                    }
                    // 将接收者列表和intent添加到广播信息列表中。
                    mPendingBroadcasts.add(new BroadcastRecord(intent, receivers));
                    // 如果没有正在发广播次消息,则发送消息:可看到此处使用的是MessagQueue来实现通信的
                    if (!mHandler.hasMessages(MSG_EXEC_PENDING_BROADCASTS)) {
                        mHandler.sendEmptyMessage(MSG_EXEC_PENDING_BROADCASTS);
                    }
                    return true;
                }
            }
        }
        return false;
    }

可以看到当添加完广播信息对象到列表中后,就发送Message消息通知Hndler来处理。在前面LocalBroadcastManager的构造方法中,我们知道handleMessage方法最终调用的是executePendingBroadcasts方法。我们来看下该方法的具体逻辑。

    void executePendingBroadcasts() {
        while (true) {
            final BroadcastRecord[] brs;
            synchronized (mReceivers) {
                final int N = mPendingBroadcasts.size();
                if (N <= 0) {
                    return;
                }
                brs = new BroadcastRecord[N];
                mPendingBroadcasts.toArray(brs);
                mPendingBroadcasts.clear();
            }
            for (int i=0; i<brs.length; i++) {
                final BroadcastRecord br = brs[i];
                final int nbr = br.receivers.size();
                for (int j=0; j<nbr; j++) {
                    final ReceiverRecord rec = br.receivers.get(j);
                    if (!rec.dead) {
                        rec.receiver.onReceive(mAppContext, br.intent);
                    }
                }
            }
        }
    }

这里面的逻辑也很简单,起了一个死循环,然后判断广播列表是否为空。不为空的话就取出所有的广播,并判断对应的广播是否被解注册,然后依次调用广播接收器的onReceive方法,这时候,之前注册的广播就可以收到消息,回调onReceive方法了。

实际上,由于LocalBroadcastManager是单例的,在应用中会出现多线程访问的问题,所以在源码中有很多代码块被加了同步锁。

解注册

最后,我们看下广播解注册的逻辑:

    public void unregisterReceiver(@NonNull BroadcastReceiver receiver) {
        synchronized (mReceivers) {
        	final ArrayList<ReceiverRecord> filters = mReceivers.remove(receiver);
            ......省略部分代码
            for (int i=filters.size()-1; i>=0; i--) {
                final ReceiverRecord filter = filters.get(i);
                filter.dead = true;
                for (int j=0; j<filter.filter.countActions(); j++) {
                    final String action = filter.filter.getAction(j);
                    final ArrayList<ReceiverRecord> receivers = mActions.get(action);
                    if (receivers != null) {
                        for (int k=receivers.size()-1; k>=0; k--) {
                            final ReceiverRecord rec = receivers.get(k);
                            if (rec.receiver == receiver) {
                                rec.dead = true;
                                receivers.remove(k);
                            }
                        }
                        if (receivers.size() <= 0) {
                            mActions.remove(action);
                        }
		......省略部分代码
    }

这里的逻辑也很简单,就是将指定BroadcastReceiver的相关信息从mReceiversmActions中够移除。

到此,整个LocalBroadcastManager的注册、发送、接收、解注册的逻辑都已经处理完成。整个流程并不复杂,主要就是利用了主线程的消息队列来实现的。

而全局广播就不一样,他涉及到进程间的通信,是利用Bindr机制来实现的,下面我们来看下全局广播的处理流程。

全局广播

首先我们看下全局广播的动态注册是怎么使用的

注册

在Activity中可以直接调用registerReceiver(receiver, filter);方法,该方法是Activtiy的父类ContextWrapper的方法,具体为:

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

这里的mBase本文就不详细解释了,它就是安卓系统中上下文对象Context的实现类ContextImpl,而ContextWrapper只是对ContextImpl进行了一层包装。该类源码路径为:
frameworks/base/core/java/android/appContextImpl.java路径下。

接下来我们看下ContextImpl中的registerReceiver方法,该方法在ContextImpl有很多重载形式,但最终都会调用registerReceiverInternal方法,对应源码为:

    private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,
            IntentFilter filter, String broadcastPermission,
            Handler scheduler, Context context, int flags) {
         // 注释000,这个rd对象比较重要,后面会用到
        IIntentReceiver rd = null;
        // 根据BroadcastReceiver的有效性来创建一个LoadedApk.ReceiverDispatcher.InnerReceiver对象
        if (receiver != null) {
            if (mPackageInfo != null && context != null) {
                if (scheduler == null) {
                    // 这里mMainThread的是一个ActivityThread对象
                    // 该对象的main方法是应用程序进程的入口
                    // getHandler是返回的主线程的Handler
                    scheduler = mMainThread.getHandler();
                }
                rd = mPackageInfo.getReceiverDispatcher(
                    receiver, context, scheduler,
                    mMainThread.getInstrumentation(), true);
            } else {
                if (scheduler == null) {
                    scheduler = mMainThread.getHandler();
                }
                // 封装了receiver信息的描述对象
                rd = new LoadedApk.ReceiverDispatcher(
                        receiver, context, scheduler, null, true).getIIntentReceiver();
            }
        }
        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();
        }
    }

该方法作用是根据当前广播的信息以及应用程序的信息来创建广播描述对象,然后利用aidl跨进程调用ActivityManager.getService()registerReceiver方法。该方法的第一个参数mMainThread.getApplicationThread是一个ApplicationThread对象,我们这里先记一下,后面会使用到。

我们先来看下ActivityManager.getService()返回的是什么?

    public static IActivityManager getService() {
        return IActivityManagerSingleton.get();
    }

返回的是一个IActivityManager对象,我们看下IActivityManagerSingletonget()方法,因为IActivityManagerSingleton是一个Singleton类型,所以就是Singletonget方法。

public abstract class Singleton<T> {
    private T mInstance;

    protected abstract T create();

    public final T get() {
        synchronized (this) {
            if (mInstance == null) {
                mInstance = create();
            }
            return mInstance;
        }
    }
}

是一个抽象类,其get方法的返回值就是create方法的返回值。

我们看下IActivityManagerSingleton对象的create方法

protected IActivityManager create() {
                    final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
                    final IActivityManager am = IActivityManager.Stub.asInterface(b);
                    return am;
                }

可以看到,返回的是ActivtiyManagerService在本地的代理接口IActivityManager对象,也就是说在这里就进入了SystemServer的进程,调用了AMSregisterReceiver方法,并且将本地的ApplicationThread对象作为第一个参数。

接下来,我们看下AMS的相关逻辑,该类的源码路径为:
frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

还是先看下registerReceiver方法,该方法比较长,我们只看其中的重点代码块:

    public Intent registerReceiver(IApplicationThread caller, String callerPackage,
            IIntentReceiver receiver, IntentFilter filter, String permission, int userId,
            int flags) {

        // 检查调用进程是否挂载,没有挂载时,会抛出异常
        enforceNotIsolatedCaller("registerReceiver");
     
        synchronized(this) {
            // 以下代码都是检查调用方进程存活状态和进程信息,省略部分代码
            ......

            // 获取对应IntentFilter的action列表
            Iterator<String> actions = filter.actionsIterator();
            if (actions == null) {
                ArrayList<String> noAction = new ArrayList<String>(1);
                noAction.add(null);
                actions = noAction.iterator();
            }

            // 收集粘性广播,以下省略较多代码
            ......
        }

        synchronized (this) {
            if (callerApp != null && (callerApp.thread == null
                    || callerApp.thread.asBinder() != caller.asBinder())) {
                // Original caller already died
                return null;
            }


            ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
            BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
                    permission, callingUid, userId, instantApp, visibleToInstantApps);
            if (rl.containsFilter(filter)) {
                // 日志
            } else {
                rl.add(bf);
                if (!bf.debugCheck()) {
                    Slog.w(TAG, "==> For Dynamic broadcast");
                }
                // 添加广播到服务端本地列表中,注册到这里就结束了 001
                mReceiverResolver.addFilter(bf);
            }
		......省略部分代码
            return sticky;
        }
    }

在如上代码中,执行到注释001处时,服务端已经保存了本地的广播对象,注册已经结束了。

发送

我们一般调用的方式为:

sendBroadcast(new Intent("broadcast"));

经过上述的分析,我们知道会调用ContextImpl的对应方法。

    @Override
    public void sendBroadcast(Intent intent) {
        warnIfCallingFromSystemProcess();
        String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
        try {
            intent.prepareToLeaveProcess(this);
            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方法。该方法比较长,处理逻辑很复杂,根据不同广播类型、进程状态进行了不同的处理,在最后,会调用

queue.enqueueParallelBroadcastLocked(r);
queue.scheduleBroadcastsLocked();

两个方法,其中queue是一个BrodcastQueue类型。我们看下enqueueParallelBroadcastLocked方法的具体逻辑:

    public void enqueueParallelBroadcastLocked(BroadcastRecord r) {
        mParallelBroadcasts.add(r);
        enqueueBroadcastHelper(r);
    }

逻辑比较简单,就是将BroadcastRecord类型的r添加到本地列表中

ArrayList<BroadcastRecord> mParallelBroadcasts = new ArrayList<>();

然后再看下scheduleBroadcastsLocked()的逻辑:

    public void scheduleBroadcastsLocked() {

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

到此,我们又发现了熟悉的Handler了。实际上,在消息处理过程中,会最终调用
processNextBroadcast(true);方法,在该方法内部又会调用processNextBroadcastLocked方法,这个方法就比较长,逻辑比较复杂,最终会调用deliverToRegisteredReceiverLocked方法,该方法中最后又会调用performReceiveLocked方法,最后会触发如下代码块:

	 app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
                            data, extras, ordered, sticky, sendingUser, app.repProcState);

还记得之前说的ApplicationThread么,这里的app.thread返回的就是IApplicationThread类型,也就是ApplicationThread,到这里就又回到了应用程序进程了。

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

最终会调用receiver.performReceive方法。
我们看下这个IIntentReceiver类型的receiver是由谁创建的呢?其实在上述注册流程中我们就遇到了,它是一个LoadedApk.ReceiverDispatcher.InnerReceiver类型

这里需要看下,这个InnerReceiver其实是一个Binder。

我们看下他的performReceive方法。

 final LoadedApk.ReceiverDispatcher rd;
                if (intent == null) {
                    Log.wtf(TAG, "Null intent received");
                    rd = null;
                } else {
                    rd = mDispatcher.get();
                }
                if (ActivityThread.DEBUG_BROADCAST) {
                    int seq = intent.getIntExtra("seq", -1);
                    Slog.i(ActivityThread.TAG, "Receiving broadcast " + intent.getAction()
                            + " seq=" + seq + " to " + (rd != null ? rd.mReceiver : null));
                }
                if (rd != null) {
                    rd.performReceive(intent, resultCode, data, extras,
                            ordered, sticky, sendingUser);
                } else {
                    // The activity manager dispatched a broadcast to a registered
                    // receiver in this process, but before it could be delivered the
                    // receiver was unregistered.  Acknowledge the broadcast on its
                    // behalf so that the system's broadcast sequence can continue.
                    if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
                            "Finishing broadcast to unregistered receiver");
                    IActivityManager mgr = ActivityManager.getService();
                    try {
                        if (extras != null) {
                            extras.setAllowFds(false);
                        }
                        mgr.finishReceiver(this, resultCode, data, extras, false, intent.getFlags());
                    } catch (RemoteException e) {
                        throw e.rethrowFromSystemServer();
                    }
                }
            }

会调用rd.performReceive方法,也就是LoadedApk.ReceiverDispatcherperformReceive方法。源码如下:

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);
               ....省略部分代码
            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);
                }
            }
        }

此处将接受数据封装成Args对象,并接着调用了mActivityThread.post方法,将消息添加到主线程的消息队列。所以我们只需要看下Args对象的消息是怎么处理的。是LoadedApk一个内部类。

						intent.setExtrasClassLoader(cl);
                        intent.prepareToEnterProcess();
                        setExtrasClassLoader(cl);
                        receiver.setPendingResult(this);
                        receiver.onReceive(mContext, intent);

到这里,我们的接收器就接受到了消息,执行onReceive方法。

到此处,消息的发送就已经结束了。。

解注册

最后,我们看下是怎么解注册的。

我们是通过unregisterReceiver方法来解注册接收器的。同样的,会调用ContextImpl的解注册方法,这里面会在本地和服务端进行双解注册,我们来看下服务端的流程。
也就是AMS的解注册流程。在服务端,会调用到removeReceiverLocked方法。

   void removeReceiverLocked(ReceiverList rl) {
        mRegisteredReceivers.remove(rl.receiver.asBinder());
        for (int i = rl.size() - 1; i >= 0; i--) {
            mReceiverResolver.removeFilter(rl.get(i));
        }
    }

其实就是将服务端存储的接收器信息从哪列表中移除。。就达到了解注册的目的

总结

本文花了很大篇幅说明安卓系统广播的注册、发送、解注册流程,其中本地广播的实现是通过消息队列来实现的,全局的广播实现比较复杂,以AMS为中介,通过客户端和系统服务进程进行交互来实现的。了解系统广播的原理,有助于我们对系统广播机制的熟悉、有助于对广播的高级使用,是非常值得学习的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值