读源码 - 五分钟理解不了广播机制

前言

了解观察者模式的童鞋,可以感觉到广播是一种典型的订阅-发布模型。不了解观察者模式的童鞋可以先看《读源码-用设计模式解析RecyclerView》。BroadcastReceiver作为四大组件之一,翻译过来就是广播接收者。整个广播机制最核心应该是:广播发送者,广播接收者,广播内容。

我们看源码的时候只要梳理清楚这三个角色的承载者和广播消息的流转过程,就可以大致弄清楚了广播机制。

所有的读源码系列文章都不是一个直接传授结论的过程,而是一个边读边思考边推测总结的过程,因为源码是无尽的,只有掌握方法取其所需即可。又要开始技术文变推理文,那么,真相只有一个。

1.广播接收者

Android中想成为一个广播的接收者,需要完成一个注册的过程。BroadcastReceiver的注册可以分为动态注册和静态注册两种。静态注册就是直接写在xml文件中那种,由PMS(PackageManagerService)完成。这次我们从动态注册入手。

 IntentFilter intentFilter = new IntentFilter();
 intentFilter.addAction("HelloWorld");
 BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
     @Override
     public void onReceive(Context context, Intent intent) {
         Toast.makeText(MainActivity.this, "Broadcast", Toast.LENGTH_SHORT).show();
     }
 };
 registerReceiver(broadcastReceiver, intentFilter)

这是典型的动态注册的代码。那么线索毫无疑问是注册方法registerReceiver。Activity中的registerReceiver来自ContextWrapper,ContextWrapper是Context的包装类,而Context的实现类是ContextImpl。registerReceiver的真正实现是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());
    }

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

最后在registerReceiverInternal方法中完成逻辑过程。发现源码中挺多叫xxxInternal的方法都还挺重要的。这个方法有2点值得注意

  • 有一大段的代码是为了给rd赋值,证明这个对象很重要
  • ActivityManagerNative的出现让我们有理由怀疑真正的注册逻辑由AMS完成,所以注册的过程涉及到IPC(进程间通信)

IIntentReceiver类型的rd到底是什么?
进入给rd赋值的LoadedApk的getReceiverDispatcher方法。

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

这个方法告诉我们LoadedApk中维护了一个ArrayMap >类型的mReceivers,以Content为键保存了BroadcastReceiver, LoadedApk.ReceiverDispatcher两个对象。最后返回值调用了ReceiverDispatcher中的getIIntentReceiver方法。那么看下LoadedApk的内部类ReceiverDispatcher吧。

 static final class ReceiverDispatcher {
        final static class InnerReceiver extends IIntentReceiver.Stub {
            final WeakReference
    
      mDispatcher; final LoadedApk.ReceiverDispatcher mStrongRef; InnerReceiver(LoadedApk.ReceiverDispatcher rd, boolean strong) { mDispatcher = new WeakReference
     
      (rd); mStrongRef = strong ? rd : null; } } final IIntentReceiver.Stub mIIntentReceiver; final BroadcastReceiver mReceiver; final Context mContext; final Handler mActivityThread; 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(); } IIntentReceiver getIIntentReceiver() { return mIIntentReceiver; } }
     
    

源码太长,为了方便观看,我只保留了关键部分。从getIIntentReceiver方法可以看出来我们问题的答案,rd就是个InnerReceiver类型的对象。InnerReceiver继承自IIntentReceiver.Stub,很显然InnerReceiver就是Binder接口IIntentReceiver的实现类。rd分析完了,下面看看怎么通过Binder调用AMS中的方法完成注册的。这里涉及到Binder的知识,ActivityManagerNative, IActivityManager, ActivityManagerProxy, ActivityManagerService这四个类的关系大家有空可以梳理下,本文不详细赘述。这里贴出部分核心的方法。

class ActivityManagerProxy implements IActivityManager
{
    public ActivityManagerProxy(IBinder remote)
    {
        mRemote = remote;
    }

    public IBinder asBinder()
    {
        return mRemote;
    }

     public Intent registerReceiver(IApplicationThread caller, String packageName,
            IIntentReceiver receiver,
            IntentFilter filter, String perm, int userId) throws RemoteException
    {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        data.writeInterfaceToken(IActivityManager.descriptor);
        data.writeStrongBinder(caller != null ? caller.asBinder() : null);
        data.writeString(packageName);
        data.writeStrongBinder(receiver != null ? receiver.asBinder() : null);
        filter.writeToParcel(data, 0);
        data.writeString(perm);
        data.writeInt(userId);
        mRemote.transact(REGISTER_RECEIVER_TRANSACTION, data, reply, 0);
        reply.readException();
        Intent intent = null;
        int haveIntent = reply.readInt();
        if (haveIntent != 0) {
            intent = Intent.CREATOR.createFromParcel(reply);
        }
        reply.recycle();
        data.recycle();
        return intent;
    }
}
public abstract class ActivityManagerNative extends Binder implements IActivityManager
{
    static public IActivityManager asInterface(IBinder obj) {
        if (obj == null) {
            return null;
        }
        IActivityManager in =
            (IActivityManager)obj.queryLocalInterface(descriptor);
        if (in != null) {
            return in;
        }

        return new ActivityManagerProxy(obj);
    }

    @Override
    public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
            throws RemoteException {
       switch (code) {
       case REGISTER_RECEIVER_TRANSACTION:
        {
            data.enforceInterface(IActivityManager.descriptor);
            IBinder b = data.readStrongBinder();
            IApplicationThread app =
                b != null ? ApplicationThreadNative.asInterface(b) : null;
            String packageName = data.readString();
            b = data.readStrongBinder();
            IIntentReceiver rec
                = b != null ? IIntentReceiver.Stub.asInterface(b) : null;
            IntentFilter filter = IntentFilter.CREATOR.createFromParcel(data);
            String perm = data.readString();
            int userId = data.readInt();
            Intent intent = registerReceiver(app, packageName, rec, filter, perm, userId);
            reply.writeNoException();
            if (intent != null) {
                reply.writeInt(1);
                intent.writeToParcel(reply, 0);
            } else {
                reply.writeInt(0);
            }
            return true;
        }
    }
}

千辛万苦,终于可以到AMS中看注册逻辑了。

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

        synchronized (this) {
            if (callerApp != null && (callerApp.thread == null
                    || callerApp.thread.asBinder() != caller.asBinder())) {
                return null;
            }
            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);
            } 

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

            if (allSticky != null) {
                ArrayList receivers = new ArrayList();
                receivers.add(bf);

                final int stickyCount = allSticky.size();
                for (int i = 0; i < stickyCount; i++) {
                    Intent intent = allSticky.get(i);
                    BroadcastQueue queue = broadcastQueueForIntent(intent);
                    BroadcastRecord r = new BroadcastRecord(queue, intent, null,
                            null, -1, -1, null, null, AppOpsManager.OP_NONE, null, receivers,
                            null, 0, null, null, false, true, true, -1);
                    queue.enqueueParallelBroadcastLocked(r);
                    queue.scheduleBroadcastsLocked();
                }
            }

            return sticky;
        }
    }

源码只保留了核心逻辑。AMS中全局变量mRegisteredReceivers,mReceiverResolver分别保存了IIntentReceiver,IntentFilter对象。这样注册的过程就完成了,可以推测当要发送广播消息的时候,只要遍历集合中保存的接收者,逐个把广播消息发过去即可。后面部分的代码是处理粘连性广播的过程。

通过上边分析可以得出以下结论:

  • BroadcastReceiver没有进程中通信的能力,要依靠InnerReceiver完成和AMS的通信。所以BroadcastReceiver和InnerReceiver是成对存在的
  • LoadedApk维护全局变量mReceivers,确保每个Context对象中广播接收者的唯一性
  • 广播接收者的核心注册逻辑是在AMS中完成的

2.广播发送者

通过广播接收者的注册过程,让我们对发送的过程也有了一点推测。估计会有以下推论

  • 发送的核心逻辑是在AMS中完成的
  • 发送过程要通过Binder接口IIntentReceiver把广播消息传到原来进程中的BroadcastReceivcer的回调函数onReceive
  • 还有个疑问,既然广播消息通过Binder对象ActivityManagerNative和AMS通信,那么IIntentReceiver这个Binder接口承担什么样的角色?
        Intent intent = new Intent();
        intent.putExtra("MSG_KEY", "welcome");
        intent.setAction("a");
        sendBroadcast(intent);

这是典型的广播发送代码。从sendBroadcast方法入手。sendBroadcast的具体实现逻辑在ContextImpl中

    @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中的broadcastIntent方法。

    public final int broadcastIntent(IApplicationThread caller,
            Intent intent, String resolvedType, IIntentReceiver resultTo,
            int resultCode, String resultData, Bundle resultExtras,
            String[] requiredPermissions, int appOp, Bundle options,
            boolean serialized, boolean sticky, int userId) {
        enforceNotIsolatedCaller("broadcastIntent");
        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();
            int res = broadcastIntentLocked(callerApp,
                    callerApp != null ? callerApp.info.packageName : null,
                    intent, resolvedType, resultTo, resultCode, resultData, resultExtras,
                    requiredPermissions, appOp, null, serialized, sticky,
                    callingPid, callingUid, userId);
            Binder.restoreCallingIdentity(origId);
            return res;
        }
    }

将符合条件的广播接收者存入BroadcastQueue,并由BroadcastQueue的scheduleBroadcastsLocked方法来发送广播。scheduleBroadcastsLocked通过handler调起processNextBroadcast方法。

    final void processNextBroadcast(boolean fromMsg) {
    ......
            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
    

这是processNextBroadcast中发送无序广播的过程。通过遍历获取每个接收者的信息,再通过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 {
        if (app != null) {
            if (app.thread != null) {
                app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
                        data, extras, ordered, sticky, sendingUser, app.repProcState);
            } else {
                throw new RemoteException("app.thread must not be null");
            }
        } else {
            receiver.performReceive(intent, resultCode, data, extras, ordered,
                    sticky, sendingUser);
        }
    }

当app存在时,通过调用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的实现是内部类ReceiverDispatcher.InnerReceiver。大家如果还记得上面的疑问IIntentReceiver这个Binder对象到底什么作用?这里可以看出一点眉目了。通过Binder通消息类似http请求这样的短连接。注册时,我们的进程A可以通过ActivityManagerNative这个Binder对象完成对AMS中的注册方法的调用,这时用不到IIntentReceiver。而发广播时,AMS得到广播消息后要主动调起广播接收者进程中的方法,把广播消息发布出去,而发布是个主动的过程,所以需要IIntentReceiver。回过头来看看InnerReceiver,估计谜团就解开了。InnerReceiver中的performReceive方法调用ReceiverDispatcher中的performReceive方法,并mActivityThread其实就是一个Handler执行了Runnble类型的Args。我们在Args中看到了,朝思暮想的代码如下。

                    ClassLoader cl =  mReceiver.getClass().getClassLoader();
                    intent.setExtrasClassLoader(cl);
                    setExtrasClassLoader(cl);
                    receiver.setPendingResult(this);
                    receiver.onReceive(mContext, intent);

广播消息终于进入了BroadcastReceiver的onReceive回调。

3.广播内容

广播的载体是Intent,我们只要分析下Intent就知道广播可以发送哪些类型。以下是所有的存储数据的API。

《读源码 - 五分钟理解不了广播机制》

展示几个API的源码

    public Intent putExtra(String name, short value) {
        if (mExtras == null) {
            mExtras = new Bundle();
        }
        mExtras.putShort(name, value);
        return this;
    }

    public Intent putExtra(String name, int value) {
        if (mExtras == null) {
            mExtras = new Bundle();
        }
        mExtras.putInt(name, value);
        return this;
    }

发现Intent都是通过一个Bundle类型的全局变量mExtras来存储数据的。这说明Bundle是支持IPC的。

public final class Bundle extends BaseBundle implements Cloneable, Parcelable {}

原来Bundle实现了Parcelable接口。这也就是说,只要实现序列化接口的对象和基础类型的数据等都可以通过Bundle来传递。我本来存在一个疑问,为什么有Map,Android还要单独实现一个Bundle。Bundle的存储过程都是在父类BaseBundle中实现的,那么来看几个实现。

    public void putLong(@Nullable String key, long value) {
        unparcel();
        mMap.put(key, value);
    }


    void putFloat(@Nullable String key, float value) {
        unparcel();
        mMap.put(key, value);
    }

mMap是ArrayMap类型的,原来Bundle真是借助Map实现的。那么,我只能理解或许Android只是想要一个实现自己序列化接口Parcelable的Map?

4.广播消息流转过程

其实广播接收者这节已经充分展示了消息的流转过程,这里简单梳理下。进程A发送广播消息并携带IntentFilter通过Binder接口,以IPC的方式进入AMS。AMS遍历存储的广播接收者,通过IntentFilter等一系列筛选后,通过第二次IPC过程,借助Binder对象InnerReceiver回到接收者的进程,回调BroadcastReceivcer的回调函数onReceive。而在消息的流转过程中,消息的内容一直静静的躺在Intent里。

后记

这次涉及的问题比较多,正如标题:五分钟理解不了广播机制。有问题欢迎留言讨论,欢迎戳喜欢。

    原文作者:PackageManagerService
    原文地址: https://juejin.im/entry/5788de22d342d30058ab1b26
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞