美文网首页
AMS分析:BroadcastReceiver管理

AMS分析:BroadcastReceiver管理

作者: 泡面先生_Jack | 来源:发表于2018-09-12 09:51 被阅读0次

    Android系统的广播机制是一种基于消息发布和订阅的事件驱动模型,即广播发送者负责发布消息,而接收者需要先订阅消息,才能接收到消息,Android系统中的广播机制是BroadcastReceiver组件。

    广播接收者需要首先将自己注册,最终他们是将自己注册到了AMS服务中,当广播发送者发送一个广播的时候,首先发送到AMS服务中,然后由AMS服务发送给对应的接收者。

    BroadCastReceiver注册分析

    首先我们来分析广播接收者的注册过程。我们知道,在Activity或者Service中注册一个广播接收者调用registerReceiver方法来接收,该方法最终调用ContextImpl方法来实现。

    1. ContextImpl.registerReceiver

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

    注册的时候registerReceiver方法直接调用了registerReceiverInternal方法来实现具体的逻辑,这个方法比较简单,mPackageInfo是一个LoadedApk类型的对象,如果mPackageInfo不为空,则根据receiver到LoadedApk中查找这个Receiver关联的Binder对象,如果mPackageInfo为空着直接创建一个Binder对象和receiver关联起来。最后通过进程间请求,调用AMS服务的registerReceiver方法来注册该广播

    我们知道LoadedApk这个类代表了一个加载到内存中的应用程序,它里边保存了一个应用程序的基本资源信息,包括ActivityThread,ApplicationInfo,包名,资源路径,Lib路径等信息,它同时还保存了所有注册的广播的信息。

    private final ArrayMap<Context, ArrayMap<BroadcastReceiver, ReceiverDispatcher>> mReceivers
            = new ArrayMap<Context, ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>>();
    

    这个mReceivers中以Context为键值保存了一个Map类型的信息ArrayMap<BroadcastReceiver, ReceiverDispatcher>,一个Context代表了一个Activity或者一个Service,因为Activity和Service最终都实现了Context。以Context为键值查找的时候可以直接找到某个Activity或者Service中注册的所有广播接收者。

    接着看ArrayMap<BroadcastReceiver, ReceiverDispatcher>,这个是以BroadCastReceiver为键值保存了一个ReceiverDispatcher的值,主要是为了将BroadcastReceiver和一个ReceiverDispatcher关联起来。

    首先我们来看下LoadedApk.ReceiverDispatcher这个类具体是什么作用。

    static final class ReceiverDispatcher {
     
            final static class InnerReceiver extends IIntentReceiver.Stub {
                final WeakReference<LoadedApk.ReceiverDispatcher> mDispatcher;
                final LoadedApk.ReceiverDispatcher mStrongRef;
     
                InnerReceiver(LoadedApk.ReceiverDispatcher rd, boolean strong) {
                    mDispatcher = new WeakReference<LoadedApk.ReceiverDispatcher>(rd);
                    mStrongRef = strong ? rd : null;
                }
                public void performReceive(Intent intent, int resultCode, String data,
                        Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
                    LoadedApk.ReceiverDispatcher rd = mDispatcher.get();
                    ……
                }
            }
            //一个Binder类型的对象,实现了Binder的服务端
            final IIntentReceiver.Stub mIIntentReceiver;
            //指向一个BoradCastReceiver
            final BroadcastReceiver mReceiver;
            //指向发送者的Context上下文信息
            final Context mContext;
            //指向发送者进程主线程的Hander对象,通过它可以向主线程发送消息
            final Handler mActivityThread;
            final Instrumentation mInstrumentation;
            
            ……
    

    Receiver这个类中保存了一些变量,分别指向了发送者的一些信息,其中InnerReceiver类型的变量最重要,它实现了IIntentReceiver.Stub,是一个Binder类型的本地对象,属于Binder服务端。

    这个类的主要作用就是,把Binder对象通过进程间传递出去,其他进程要和这边的Receiver通信的时候,直接调用该Binder的代理端,这边的InnerReceiver就可以接收到消息,然后在通过主线程的Handler对象发送个主线程来处理这个消息。

    接着看LoadedAPk.getReceiverDispatcher方法的实现。

    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) {
                    //根据当前的Context,查找关于这个Activity或者Service注册的所有广播接收者信息
                    map = mReceivers.get(context);
                    if (map != null) {
                        //找到这个广播接收者关联的一个Binder服务端对象
                        rd = map.get(r);
                    }
                }
                if (rd == null) {
                    //如果LoadedApk中没有保存的话,就先创建一个,然后保存的LoadedAPk的mReceivers集合中
                    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();
            }
        }
    

    根据前面方法的分析看出,这个方法的作用是用来在LoadedApk中查找这个广播接收者关联的Binder对象的。如果有则直接返回,如果找不到则new一个ReceiverDispatcher对象,保存到mReceivers集合中,然后再返回Receiver关联的Binder对象。

    2. AMS.registerReceiver

    public Intent registerReceiver(IApplicationThread caller, String callerPackage,
                IIntentReceiver receiver, IntentFilter filter, String permission, int userId) {
            ArrayList<Intent> stickyIntents = null;
            ProcessRecord callerApp = null;
            int callingUid;
            int callingPid;
            synchronized(this) {
                if (caller != null) {
                     //获取调用注册程序的进程信息和Uid,Pid
                    callerApp = getRecordForAppLocked(caller);
                   ……
                    callingUid = callerApp.info.uid;
                    callingPid = callerApp.pid;
                } 
     
                userId = handleIncomingUser(callingPid, callingUid, userId,
                        true, ALLOW_FULL_ONLY, "registerReceiver", callerPackage);
     
                Iterator<String> actions = filter.actionsIterator();
                ……
                int[] userIds = { UserHandle.USER_ALL, UserHandle.getUserId(callingUid) };
                //查询所有的Action相关的粘性广播,并保存到stickyIntents中
                while (actions.hasNext()) {
                    String action = actions.next();
                    for (int id : userIds) {
                        ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(id);
                        if (stickies != null) {
                            ArrayList<Intent> intents = stickies.get(action);
                            if (intents != null) {
                                if (stickyIntents == null) {
                                    stickyIntents = new ArrayList<Intent>();
                                }
                                stickyIntents.addAll(intents);
                            }
                        }
                    }
                }
            }
    ……
            synchronized (this) {
                //根据注册进程传入的binder对象从mRegisteredReceivers查找对应的ReceiverList
                //mRegisteredReceivers列表,以注册进程传入的receiver.asBinder为key,保存了ReceiverList
                ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
                //如果第一次调用则new一个ReceiverList,并添加到mRegisteredReceivers列表中
                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);
                }
                ……
                //创建一个BroadCastReceiver对象并保存到ReceiverList中,BroadcastFilter表示广播的过滤条件,一个广播接收者可以有多个过滤条件
                BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
                        permission, callingUid, userId);
                rl.add(bf);
                //同时也保存到mReceiverResolve列表中,这个列表保存了系统中所有动态注册的广播接收者所对应的BroadCastFilter
                mReceiverResolver.addFilter(bf);
     
                // Enqueue broadcasts for all existing stickies that match
                // this filter.
                if (allSticky != null) {
                   ……
     
                    final int stickyCount = allSticky.size();
                    for (int i = 0; i < stickyCount; i++) {
                        //遍历所有符合条件的stick广播Intent,然后把它封装成一个BroadcastRecord并添加到BroadcastQueue的ParallelBroadcastLocked列表
                        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;
            }
        }
    

    该方法的主要工作:

    1. 获取调用程序的进程信息ProcessRecord及PID和Uid
    2. 从AMS的mStickyBroadcasts列表中查询所有和action匹配的sticky广播,并保存到allSticky列表中,该Action就是即将要注册的广播的Action。
    3. 我们知道参数receiver是一个IIntentReceiver类型的变量,是binder代理端,指向了应用进程要注册的广播Receiver,registerdReceivers是一个map类型的变量,它以receiver.asBInder为key保存了ReceiverList。这样应用进程的一个Receiver就和AMS服务中的一个ReceiverList关联起来了。

    ReceiverList又是什么呢? 它继承自ArrayList<BroadCastFilter>,同时它内部有一个IntentReceiver类型的变量指向一个Receiver,这样一个Receiver就和多个BroadcastFilter关联起来了。因为一个广播可以有多个过滤条件。

    第三步的主要工作就是从mRegisterReceivers中查找receiver对应的ReceiverList信息,如果为空,就表示该广播没有注册,需要新创建一个ReceiverList并添加到mRegisterReceivers列表中。

    1. 创建一个BroadcastFilter,添加到上面的ReceiverList中,同时也添加到mReceiverResolver集合中,mReceiverResolver集合存储了所有动态注册的广播的过滤条件BroadcastFilter,便于广播发送的时候查找Receiver。
    2. 将第二步找到的和此接收者Action相关的sticky广播遍历,封装成一个BroadcastRecord,加入队列准备发送。

    3. BroadCastQueue. scheduleBroadcastsLocked

    public void scheduleBroadcastsLocked() {
            //发送Handler消息个AMS服务,通知AMS服务发送广播
            mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
            mBroadcastsScheduled = true;
        }
    

    最终调用scheduleBroadcastsLocked方法,发送一个Hander消息给AMS服务,由AMS服务来发送广播。

    同时也了解了sticky广播的实现原理,发送完的sticky广播会保存到mStickyBraodcasts集合中,在注册的时候根据action查找对应的sticky广播,如有有相关的sticky广播的话,注册完成后,立刻发送出去,这样刚注册的Receiver就可以立刻受到sticky的广播了

    这样广播的动态注册就完成了。经过分析可以看出,AMS服务是广播的注册中心,广播接收者要想接收广播消息需要将自己注册到AMS服务中去,指定要接收的广播类型,发送广播也是首先发送给AMS服务,由AMS服务来找到对应的广播接收者,然后在调用对应的广播接收者来处理。

    Broadcast的发送过程分析

    广播的发送方式分为有序广播和无序广播,在注册广播的时候可以指定广播的优先级,这样广播就会优先发送给优先级比较高的广播,然后才会发送给优先级比较低的广播。而无序广播则不管广播接收者的优先级。

    同样要分析广播的发送过程需要从ContextImpl类开始分析,因为它最终实现了sendBroadcast的接口和方法,service和activity发送广播的过程都是调用ContextImpl的方法来实现的。

    1. ContextImpl.sendBroadcast

    public void sendBroadcast(Intent intent) {
            ……
            try {
                intent.prepareToLeaveProcess();
                ActivityManagerNative.getDefault().broadcastIntent(
                        mMainThread.getApplicationThread(), intent, resolvedType, null,
                        Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false, false,
                        getUserId());
            } 
        }
    

    ContextImpl的发送广播的方法最终通过进程间通信请求,调用AMS服务的broadcastIntent方法。同时在参数中将发送者的ApplicationThread和广播的intent传递给了AMS服务。

    这个方法比较简单,进程间通信方式不再解释,我们直接看AMS服务的broadcastIntent方法

    2. AMS.broascastIntent

     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) {
            ……
            synchronized(this) {
                //验证Intent信息
                intent = verifyBroadcastLocked(intent);
                //获取调用进程的信息ProcessRecord
                final ProcessRecord callerApp = getRecordForAppLocked(caller);
                //获取调用应用的uid和pid
                final int callingPid = Binder.getCallingPid();
                final int callingUid = Binder.getCallingUid();
                //通过broadcastIntentLocked方法来继续处理发送广播的逻辑
                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;
            }
        }
    

    这个方式实现也是比较简单,获取调用进程的ProcessRecord信息和调用程序的UID和PID,然后直接调用broadcastIntentLocked方法来继续处理发送广播的逻辑

    3. AMS.broadcastIntentLocked

    这个方法比较长,我们一部分一部分的来看

    intent = new Intent(intent);
     
            //确保要接收的广播的用户仍然存在,否则的话直接返回错误信息
            if (userId != UserHandle.USER_ALL && !isUserRunningLocked(userId, false)) {
                if ((callingUid != Process.SYSTEM_UID
                        || (intent.getFlags() & Intent.FLAG_RECEIVER_BOOT_UPGRADE) == 0)
                        && !Intent.ACTION_SHUTDOWN.equals(intent.getAction())) {
                    Slog.w(TAG, "Skipping broadcast of " + intent
                            + ": user " + userId + " is stopped");
                    return ActivityManager.BROADCAST_FAILED_USER_STOPPED;
                }
            }
            
            //如果当前的广播类型是sticky广播,则把广播添加到List列表中去
            if (sticky) {
                //首先检查相关的权限信息
                if (checkPermission(android.Manifest.permission.BROADCAST_STICKY,
                        callingPid, callingUid)
                        != PackageManager.PERMISSION_GRANTED) {
                    String msg = "Permission Denial: broadcastIntent() requesting a sticky broadcast from pid="
                            + callingPid + ", uid=" + callingUid
                            + " requires " + android.Manifest.permission.BROADCAST_STICKY;
                ……
                
                if (userId != UserHandle.USER_ALL) {
                    //如果这个广播不是发送给所有用户的,则检查发送给所有用户的广播中是否包含这个广播,如果包含则和发送给所有用户的广播有冲突,抛出异常信息
                    ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(
                            UserHandle.USER_ALL);
                    if (stickies != null) {
                        ArrayList<Intent> list = stickies.get(intent.getAction());
                        if (list != null) {
                            int N = list.size();
                            int i;
                            for (i=0; i<N; i++) {
                                if (intent.filterEquals(list.get(i))) {
                                    throw new IllegalArgumentException(
                                            "Sticky broadcast " + intent + " for user "
                                            + userId + " conflicts with existing global broadcast");
                                }
                            }
                        }
                    }
                }
                //找到和这个用户相关的所有的sticky广播的信息
                ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(userId);
                if (stickies == null) {
                    stickies = new ArrayMap<>();
                    mStickyBroadcasts.put(userId, stickies);
                }
                //从得到的广播信息中,找到和当前要发送广播action相同的sticky广播
                ArrayList<Intent> list = stickies.get(intent.getAction());
                if (list == null) {
                    list = new ArrayList<>();
                    stickies.put(intent.getAction(), list);
                }
                //遍历拿到的stick广播信息,如果已经存在,则更新替换掉他
                //如果不存在,则添加进去
                final int stickiesCount = list.size();
                int i;
                for (i = 0; i < stickiesCount; i++) {
                    if (intent.filterEquals(list.get(i))) {
                        // This sticky already exists, replace it.
                        list.set(i, new Intent(intent));
                        break;
                    }
                }
                if (i >= stickiesCount) {
                    list.add(new Intent(intent));
                }
            }
    

    参数sticky用来描述要发送的广播是否是一个sticky类型的广播,如果是,就需要对sticky广播进行一些处理,在前面我们分析过,在注册广播的时候,如果AMS中保存了对应action类型的sticky广播,则直接将保存的sticky广播发送出去。那些sticky广播是如何保存到AMS服务中去的呢?

    代码逻辑就是在这边处理的,在注册一个广播的时候,如果它是sticky类型的广播,就直接将它保存在AMS服务的mStickyBroadcasts集合中。

    首先从AMS系统服务中查询Action相关的sticky广播,然后遍历,如果该sticky广播已经在AMS中注册保存了,则用新的Sticky广播替换掉它,否则直接添加到列表中去。

      List receivers = null;
            List<BroadcastFilter> registeredReceivers = null;
            //如果没有FLAG_RECEIVER_REGISTERED_ONLY标签,说明不是发送给动态注册的广播的,那么调用collectReceiverComponents,查找所有和该Intent相关的广播接收者
            //保存到receivers列表中
            if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY)
                     == 0) {
                receivers = collectReceiverComponents(intent, resolvedType, callingUid, users);
            }
            //如果没有指定特定的广播接收者,则查找所用的动态注册的广播接收者的信息
            if (intent.getComponent() == null) {
                ……
                    registeredReceivers = mReceiverResolver.queryIntent(intent,
                            resolvedType, false, userId);
                }
            }
    

    第二部分的主要处理逻辑就是查找和intent相关的广播接收者。我们知道广播注册的时候有两种注册方法,一种是静态注册方法,在Manifest文件中注册,另一种是动态注册方法,在代码中调用registerBroadcast方法来注册广播。

    这个地方进行了两次查找,判断发送广播的intent的标签,如果不是仅仅发送给动态注册的广播的时候,就调用collectReceiverComponents方法查询所有Intent感兴趣的广播接收者,这里仅仅去PMS服务中查询了感兴趣的静态注册的广播接收者,保存在receivers列表中。

    然后判断这个广播是否指定了值发送给特定的广播接收者,如果没有指定的话,默认发送给所有感兴趣的广播接收者,这时候再去查询一次,这次查询的是所有对这个Intent感兴趣的动态注册的广播接收者,保存在registeredReceivers列表中。

      int NR = registeredReceivers != null ? registeredReceivers.size() : 0;
            if (!ordered && NR > 0) {
                //如果当前不是有序广播,且动态注册的感兴趣的广播不为0,将广播intent封装为一个BroadcastRecord
                final BroadcastQueue queue = broadcastQueueForIntent(intent);
                BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
                        callerPackage, callingPid, callingUid, resolvedType, requiredPermissions,
                        appOp, brOptions, registeredReceivers, resultTo, resultCode, resultData,
                        resultExtras, ordered, sticky, false, userId);
               ……
                //发送该广播给动态注册的广播接收者
                    queue.enqueueParallelBroadcastLocked(r);
                    queue.scheduleBroadcastsLocked();
                }
                //发送完成后,将registeredReceivers 置为 null
                registeredReceivers = null;
                NR = 0;
            }
    

    第三部分的处理工作:

    如果当前发送的广播不是有序广播,且动态注册的广播接收者>0,此时则需要把这个广播优先发送给动态注册的广播接收者,为什么不和静态注册的广播一起发送,却要分开发送呢?这样做是因为,动态注册的广播接收者一般情况下都已经运行起来了,而静态注册的广播接收者只是在Manifest文件中注册,要想接收广播必须要等待要接收的组件启动起来才可,所以这边处理选择分开发送,优先发送动态注册的广播接收者。

    发送的广播的时候,无序广播都添加到了mParallelBroadcats队列中,具体发送的处理逻辑后边在详细分析。

    下一步处理发送给静态注册的广播接收者的逻辑。

      int ir = 0;
            //感兴趣的静态注册广播接收者不为空
            if (receivers != null) {
                //首先对于特殊的ACTION_PACKAGE_ADDED的广播需要特殊处理
                //这样是为了防止一些应用则安装完成之后监听到package_add的广播就立刻运行
                //所以要把它已标记起来,以后从广播接收者信息中移除
                String skipPackages[] = null;
                if (Intent.ACTION_PACKAGE_ADDED.equals(intent.getAction())
                        || Intent.ACTION_PACKAGE_RESTARTED.equals(intent.getAction())
                        || Intent.ACTION_PACKAGE_DATA_CLEARED.equals(intent.getAction())) {
                    Uri data = intent.getData();
                    if (data != null) {
                        String pkgName = data.getSchemeSpecificPart();
                        if (pkgName != null) {
                            skipPackages = new String[] { pkgName };
                        }
                    }
                //同样,如果Intenty有ACTION_EXTERNAL_APPLICATIONS_AVAILABLE标签,则同样移除
                } else if (Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(intent.getAction())) {
                    skipPackages = intent.getStringArrayExtra(Intent.EXTRA_CHANGED_PACKAGE_LIST);
                }
                if (skipPackages != null && (skipPackages.length > 0)) {
                    for (String skipPackage : skipPackages) {
                        if (skipPackage != null) {
                            int NT = receivers.size();
                            for (int it=0; it<NT; it++) {
                                ResolveInfo curt = (ResolveInfo)receivers.get(it);
                                if (curt.activityInfo.packageName.equals(skipPackage)) {
                                    receivers.remove(it);
                                    it--;
                                    NT--;
                                }
                            }
                        }
                    }
    

    处理静态注册的广播接收者逻辑的时候,首先判断该广播事件是否是Package_add的广播事件,如果是的话,需要从广播接收者列表中移除,这样为了防止一些毒瘤应用在安装完成后接收到package_add广播就立刻运行。

        int NT = receivers != null ? receivers.size() : 0;
                int it = 0;
                ResolveInfo curt = null;
                BroadcastFilter curr = null;
                //上面的无序广播处理后,把mRegisterReceivers列表置为null,并且把NR设置成了0
                //而此时要想满足此处条件,NR不为0,说明此处是有序广播的处理逻辑
                //遍历动态注册的广播和静态注册的广播,安priority值排序合并放到receivers列表中
                while (it < NT && ir < NR) {
                    if (curt == null) {
                        curt = (ResolveInfo)receivers.get(it);
                    }
                    if (curr == null) {
                        curr = registeredReceivers.get(ir);
                    }
                    if (curr.getPriority() >= curt.priority) {
                        // Insert this broadcast record into the final list.
                        receivers.add(it, curr);
                        ir++;
                        curr = null;
                        it++;
                        NT++;
                    } else {
                        // Skip to the next ResolveInfo in the final list.
                        it++;
                        curt = null;
                    }
                }
            ……
            //有序广播接收者不为空,发送有序广播
            if ((receivers != null && receivers.size() > 0)
                    || resultTo != null) {
                BroadcastQueue queue = broadcastQueueForIntent(intent);
                BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
                        callerPackage, callingPid, callingUid, resolvedType,
                        requiredPermissions, appOp, brOptions, receivers, resultTo, resultCode,
                        resultData, resultExtras, ordered, sticky, false, userId);
     
                if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing ordered broadcast " + r
                        + ": prev had " + queue.mOrderedBroadcasts.size());
                if (DEBUG_BROADCAST) Slog.i(TAG_BROADCAST,
                        "Enqueueing broadcast " + r.intent.getAction());
     
                boolean replaced = replacePending && queue.replaceOrderedBroadcastLocked(r);
                if (!replaced) {
                    queue.enqueueOrderedBroadcastLocked(r);
                    queue.scheduleBroadcastsLocked();
                }
            }
    

    此处处理逻辑主要为有序广播的处理逻辑。

    在前面的分析中知道,如果是无序广播的时候,动态注册的广播接收者就直接处理了,然后把mRegisteredReceivers列表置为了null,所以在有序广播的时候就不会有动态注册的广播接收者合并进来。此时就会把静态广播放到有序广播类别mOrderedBroadcasts队列中发送。为什么会把静态注册的广播接收和按照有广播的逻辑来发送呢?有序广播发送的处理逻辑是发送完成一个在发送另一个,而无序广播是直接循环发送出去,静态注册的广播接收者的进程不一定已经运行起来了,在发送的过程中可能需要先启动进程,这样很消耗资源,所以一个一个发送会好一点。

    如果发送的是有序广播,动态注册的广播在前面逻辑中则不会处理,此处就会把动态注册的广播和静态注册的广播按照priority值排序,然后合并到receivers列表中。

    最终把receiver列表中的广播接收者封装成一个BroadcastRecord对象放到mOrderedBroadcasts队列中进行发送。

    此处逻辑可以总结出来,如果是无序广播的话。动态注册的广播接收者会比静态注册的广播者优先收到广播
    下面我们来分析具体的广播发送处理逻辑:

      BroadcastQueue queue = broadcastQueueForIntent(intent);
                BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
                        callerPackage, callingPid, callingUid, resolvedType,
                        requiredPermissions, appOp, brOptions, receivers, resultTo, resultCode,
                        resultData, resultExtras, ordered, sticky, false, userId);
     
                queue.enqueueOrderedBroadcastLocked(r);
                queue.scheduleBroadcastsLocked();
    

    对于BroadCast的发送的处理逻辑大体上分为以上几个步骤

    1. 根据广播的Intent找到对应的广播队列
    2. 将Intent和广播接收者封装成一个BroadcastRecord对象
    3. 将BroadcastRecord对象添加拿到queue队列中
    4. 调用queue的scheduleBroadcastsLocked方法发送广播

    4. AMS. broadcastQueueForIntent

      BroadcastQueue broadcastQueueForIntent(Intent intent) {
            final boolean isFg = (intent.getFlags() & Intent.FLAG_RECEIVER_FOREGROUND) != 0;
            return (isFg) ? mFgBroadcastQueue : mBgBroadcastQueue;
        }
    

    这个方法很简单,就是根据Intent中是否有Flag_Receiver_foreground标签,来决定添加到那个队列中去。

    这个标签是什么作用呢?看下SDK中的描述:

    FLAG_RECEIVER_FOREGROUND 接受器以前台优先级运行

    If set, when sending a broadcast the recipient is allowed to run at foreground priority, with a shorter timeout interval. During normal broadcasts the receivers are not automatically hoisted out of the background priority class.

    广播默认都是后台优先级,如果广播中添加了这个标签就会以前台优先级运行,发送到接收到广播的时间间隔会更短。

    也就是说,默认广播都是天剑到mBgBroadcastQueue队列中去的,只有广播添加了这个标签后,才会被添加到mFgBroadcastQueue队列中。这两个队列中处理逻辑的区别我们后面再详细分析。

    第二步,将Intent和广播接收者队列封装成了一个BroadcastRecord对象,也就是说每一个广播在AMS都是对应一个BroadcastRecord对象的。

    5. BroadcastQueue. Enqueue……BroadcastLocked

    BroadcastQueue中有两个添加队列的方法:

     public void enqueueParallelBroadcastLocked(BroadcastRecord r) {
            mParallelBroadcasts.add(r);
            r.enqueueClockTime = System.currentTimeMillis();
        }
     
        public void enqueueOrderedBroadcastLocked(BroadcastRecord r) {
            mOrderedBroadcasts.add(r);
            r.enqueueClockTime = System.currentTimeMillis();
        }
    

    该消息发送了了一个Handler消息,有Hander的handleMessage来处理,最终调用了processNextBroadcast方法来处理。

    7. BroadcastQueue. processNextBroadcast

    动态注册广播接收者的处理逻辑

    这个方法有点复杂,主要是两种广播的处理逻辑不一样,那我们就先分析动态注册的广播接收者处理逻辑,然后在分析静态注册或者有序广播的处理逻辑。

    我们在前面讲过,动态注册的广播接收者处理的时候是一次性的循环发送完国有广播,而静态注册或者有序广播则是一个发送完成在发送另一个。

     final void processNextBroadcast(boolean fromMsg) {
            synchronized(mService) {
                BroadcastRecord r;
     
               ……
                while (mParallelBroadcasts.size() > 0) {
                    r = mParallelBroadcasts.remove(0);
                    r.dispatchTime = SystemClock.uptimeMillis();
                    r.dispatchClockTime = System.currentTimeMillis();
                    final int N = r.receivers.size();
                    
                    for (int i=0; i<N; i++) {
                        Object target = r.receivers.get(i);
                        deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false);
                    }
                    addBroadcastToHistoryLocked(r);
                }
    

    动态广播的处理逻辑是在processNextBroadcast方法的开始处理的,变量mParallelBroadcasts列表,从列表开始位置一次拿到保存的BroadcastRecord对象,每个BroadcastRecord中可能对应多个广播接收者,依次遍历广播接收者,然后调用deliverToRegisteredReceiverLocked方法发送广播

     private void deliverToRegisteredReceiverLocked(BroadcastRecord r,
                BroadcastFilter filter, boolean ordered) {
            boolean skip = false;
            //检查是否具有相关权限,没有权限则skip = true 表示跳过这个广播接收者
            ……
            //检查接收广播的进程是否存在,如果接收广播的进程不存在则跳过这个广播接收者,不向它发送该广播
            if (filter.receiverList.app == null || filter.receiverList.app.crashing) {
                Slog.w(TAG, "Skipping deliver [" + mQueueName + "] " + r
                        + " to " + filter.receiverList + ": process crashing");
                skip = true;
            }
     
            if (!skip) {
                
                if (ordered) {
                    //有序广播处理逻辑
                }
                try {
                   
                    performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
                            new Intent(r.intent), r.resultCode, r.resultData,
                            r.resultExtras, r.ordered, r.initialSticky, r.userId);
                    ……
                } 
            }
        }
    

    我们只关心和我们处理逻辑相关的关键代码。

    检查相关的权限,如果没有权限则skip设置为true,
    检查广播接收者对应的进程是否存在,如果不存在skip设置为true
    我们目前是无序广播,所以ordered为false,最终调用performReceiveLocked方法来处理。

    我么回忆下ReceiverList是什么,ReceiverList是我们在注册广播接收者的时候,一个广播接收者的代表信息,它继承者ArrayList<BroadcastFilter>,这样可以实现一对多的数据结构,因为一个广播接收者可能对应多个BroadcastFilter。所以receiverList.app代表了广播接收者所在的进程,receiverList.receiver则是一个Binder对象,指向了App进程中的一个InnerReceiver的对象。

    继续看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 {
                receiver.performReceive(intent, resultCode, data, extras, ordered,
                        sticky, sendingUser);
            }
        }
    

    参数receiver是一个IIntentReceiver接口的Binder代理对象,它的服务端是应用进程的InnerReceiver,参数app则是接收广播的应用进程的进程对象ProcessRecord,在注册广播接收者的时候,我们知道在创建广播接收者对应的ReceiverList对象的时候会初始化app的值。

    ReceiverList rl = new ReceiverList(this, callerApp, callingPid, callingUid, userId, receiver);
    

    一般情况下receiver的变量app都是不为null的,所以大部分情况下广播都是通过应用进程的ApplicationThread对象的Binder代理对象,通过进程间调用,将这个广播发送到应用进程主线程来处理,应用进程的主线程再调用对应的receiver来处理。而不是直接通过Receiver的Binder代理端来进行进程间的调用。

    接着看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);
            }
    

    此时直接调用receiver的performReceive方法,receiver是一个binder类型的代理对象,这个方法调用最终调用到InnerReceiver的performReceiver方法中。

    public void performReceive(Intent intent, int resultCode, String data,
                        Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
                    //得到当前的dispatcher
                    LoadedApk.ReceiverDispatcher rd = mDispatcher.get();
                    if (rd != null) {
                        rd.performReceive(intent, resultCode, data, extras,
                                ordered, sticky, sendingUser);
                    }
                    ……
                }
    

    这个方法很简单,首先得到对应的dispatcher,然后调用dispatcher的方法来处理。

    public void performReceive(Intent intent, int resultCode, String data,
                    Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
                Args args = new Args(intent, resultCode, data, extras, ordered,
                        sticky, sendingUser);
                if (!mActivityThread.post(args)) {
                    if (mRegistered && ordered) {
                        IActivityManager mgr = ActivityManagerNative.getDefault();
                        args.sendFinished(mgr);
                    }
                }
            }
    

    mActivityThread是一个Handler对象,该对象指向ActivityThread的Hander对象,Handler的post最终调用了Args的run方法。

    final class Args extends BroadcastReceiver.PendingResult implements Runnable {
               ……
     
                public void run() {
                    final BroadcastReceiver receiver = mReceiver;
                   ……
                    try {
                        ClassLoader cl =  mReceiver.getClass().getClassLoader();
                        intent.setExtrasClassLoader(cl);
                        setExtrasClassLoader(cl);
                        receiver.setPendingResult(this);
                        receiver.onReceive(mContext, intent);
                    } 
                    ……
                }
            }
    

    变量receiver指向了一个广播接收者BroadcastReceiver,然后调用receiver.onReceive()方法。到此动态注册的广播接收者接收广播的逻辑就处理完成了。

    下一步看静态注册或者有序广播的发送逻辑。

    静态注册或者有序广播接收者的处理逻辑

    我们再回到processNextBroadcast方法中,接着往下看

                //判断当前是否正在等待静态注册的广播接收者控件启动并接收任务
                if (mPendingBroadcast != null) {
     
                    boolean isDead;
                    synchronized (mService.mPidsSelfLocked) {
                        ProcessRecord proc = mService.mPidsSelfLocked.get(mPendingBroadcast.curApp.pid);
                        isDead = proc == null || proc.crashing;
                    }
                    if (!isDead) {
                        //如果有个任务正在启动则直接返回继续等待
                        return;
                    }
                }
     
                boolean looped = false;
     
                do {
                    //从mOrderedBroadcasts列表中获取下一个要发送的广播
                    r = mOrderedBroadcasts.get(0);
                    boolean forceReceive = false;
     
                    //判断有序广播是否发送超时,如果发送超时则直接强制停止发送这个广播
                    int numReceivers = (r.receivers != null) ? r.receivers.size() : 0;
                    if (mService.mProcessesReady && r.dispatchTime > 0) {
                        long now = SystemClock.uptimeMillis();
                        if ((numReceivers > 0) &&
                                (now > r.dispatchTime + (2*mTimeoutPeriod*numReceivers))) {
                            broadcastTimeoutLocked(false); //强制停止这个广播
                            forceReceive = true;
                            r.state = BroadcastRecord.IDLE;
                        }
                    }
     
                    ……
     
                    if (r.receivers == null || r.nextReceiver >= numReceivers
                            || r.resultAbort || forceReceive) {
                        //如果这个广播接收者receivers已经处理完了,或者这个广播已经强制停止了
                        if (r.resultTo != null) {
                            ……
                        }
     
                        //从Handler延时消息中移除timeout的消息
                        cancelBroadcastTimeoutLocked();
     
                        //添加到history中
                        addBroadcastToHistoryLocked(r);
                        //从mOrderedBroadcasts列表中移除
                        mOrderedBroadcasts.remove(0);
                        //置为null
                        r = null;
                        looped = true; 
                        continue;
                    }
                } while (r == null);
    

    首先判断mPendingBroadcast是不是为空,这个mPendingBroadcast的作用是什么呢?

    我们知道,mOrderedBroadcast中保存的广播接收者都是需要一个发送完成另一个才可以发送的。其中静态注册的广播接收者有可能对应的进程还未启动,者就需要首先来启动该进程。如果是这种情况的话mPendingBroadcast就是描述了一个正在等待静态注册的对应进程启动的广播。

    如果他不为空,说明当前有任务正在处理,在对这个正在启动的进程检查之后就直接退出了。

    如果mPendingBroadcast不为空,AMS就会继续等待,否则的话就会准备处理mOrderedBroadcast的下一个广播。

    接下来是一个while循化,主要工作是从mOrderedBroadcasts列表中找到下一个要处理的广播,退出条件是 r != null,即找到下一个合适的广播。

    1.首先从列表的开始拿出第一个BroadcastRecord,第一个有可能是正在处理的广播,判断他是否已经开始分发广播,如果开始但是已经超时,则直接强制停止这个广播。
    2.然后,判断这个广播是否已经处理完成,或者是否强制停止,然后直接停止该广播。
    3.将发送给AMS的关于超时的延时消息移除
    4.将第一个广播移除掉,并把r设置为null,表示没有找到合适的广播,然后继续循环查找,知道找到下一个合适的广播,退出循环。

       //获取下一个要执行的Receiver信息
                int recIdx = r.nextReceiver++;
     
                r.receiverTime = SystemClock.uptimeMillis();
                //如果recIdx = 0,说明这个广播的才刚开始发送广播任务,则记录下当前的时间为分发广播的时间
                if (recIdx == 0) {
                    r.dispatchTime = r.receiverTime;
                    r.dispatchClockTime = System.currentTimeMillis();
                }
                if (! mPendingBroadcastTimeoutMessage) {
                    //向AMS的handler发送一个延时的超时消息,到超时的时间后自动发送
                    long timeoutTime = r.receiverTime + mTimeoutPeriod;
                    setBroadcastTimeoutLocked(timeoutTime);
                }
    ……
                final Object nextReceiver = r.receivers.get(recIdx);
     
                if (nextReceiver instanceof BroadcastFilter) {
                    BroadcastFilter filter = (BroadcastFilter)nextReceiver;
                    deliverToRegisteredReceiverLocked(r, filter, r.ordered);
                    return;
                }
    

    我们知道一个广播可能对应多个广播接收者,在mOrderedBroadcasts列表处理的时候是需要向一个Receiver发送成功后,在向另一个发送。BroadcastRecord的变量nextReceiver表示下一个广播接收者的序号。

    如果为0,说明这个广播的广播接收者一个都没有发送,也就是说这个广播才刚开始发送,此时就要记录下当前广播分发的时间,然后计算下超时时间然后向AMS服务发送一个延时消息。接着,获取到下一个Receiver,判断它是否是BroadcastFilter类型,如果是,然后调用deliverToRegisteredReceiverLocked方法来处理,我们在动态注册的广播处理的时候已经分析过这个方法了,这边不再讨论。

     ResolveInfo info =
                    (ResolveInfo)nextReceiver;
                ComponentName component = new ComponentName(
                        info.activityInfo.applicationInfo.packageName,
                        info.activityInfo.name);
                //判断权限相关及其他来觉是否跳过这个广播接收者
                boolean skip = false;
                int perm = mService.checkComponentPermission(info.activityInfo.permission,
                ……
     
                //判断这个广播接收者对应的进程是否已经启动
                ProcessRecord app = mService.getProcessRecordLocked(targetProcess,
                        info.activityInfo.applicationInfo.uid, false);
                if (app != null && app.thread != null) {
                    //如果已经启动则发送广播给当前的广播接收者
                    try {
                        app.addPackage(info.activityInfo.packageName,
                                info.activityInfo.applicationInfo.versionCode, mService.mProcessStats);
                        processCurBroadcastLocked(r, app);
                        return;
                    } 
                    ……
                }
     
                //否则的话则新建一个进程,并把mPendingBroadcast设置为当前的广播接收者,等待新进程启动完成
                if ((r.curApp=mService.startProcessLocked(targetProcess,
                        info.activityInfo.applicationInfo, true,
                        r.intent.getFlags() | Intent.FLAG_FROM_BACKGROUND,
                        "broadcast", r.curComponent,
                        (r.intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0, false, false))
                                == null) {
                    ……
                }
                
                mPendingBroadcast = r;
                mPendingBroadcastRecvIndex = recIdx;
    

    广播接收者分为两种类型,动态注册和静态注册,静态注册的广播是ResolveInfo类型,所以此处主要来处理静态注册的广播接收者发送广播的逻辑。
    1.首先,判断相关的权限及其他信息来决定是否可以发送广播给这个广播接收者
    2.如果可以,查询广播接收者对应的进程是否存在,如果已经存在,直接 调用processCurBroadcastLocked方法来处理当前广播

    如果不存在,则启动一个新的进程,将mPendingBroadcast的值设置为当前的BroadcastRecord的值,然后等待新的进程启动完成。

    后边发送的过程和动态注册的广播mParallelBroadcasts列表处理逻辑一致,不再详细介绍。由于mOrderedBroadcasts列表中的广播是一个处理完成在处理另一个,那我们看下发送广播给receiver后,如何处理的。

    在前面分析过,Args的run方法中最终调用了BroadcastReceiver的onReceive方法,在调用完receiver方法后会调用AMS的finishReceiver方法。其实不仅仅是这个地方调用了finishReceiver方法,在发送失败等情况下也会调用。

    IActivityManager mgr = ActivityManagerNative.getDefault();
    mgr.finishReceiver(this, resultCode, data, extras, false, intent.getFlags());
    

    AMS的finishReceiver方法中则会继续处理后续的广播接收者的发送或者调用processNextBroadcast方法,继续执行下一个广播的发送。

    到此为止广播的发送过程也就简单的分析完了。从分析过程中可以看出AMS服务具体是如何管理Broadcast广播。

    相关文章

      网友评论

          本文标题:AMS分析:BroadcastReceiver管理

          本文链接:https://www.haomeiwen.com/subject/htvcgftx.html