FrameWork层源码分析(3)-注册广播接收者的源码分析

主目录见:Android高级进阶知识(这是总目录索引)
同时预告下,以后可能会来翻译groovy语言的文档,地址:Groovy Language Documentation,如果想要参与翻译的可以跟我说哈。

 我们知道注册广播有两种方式,一种是动态广播注册,另一种是静态广播注册,正因为这个使得插件化开发中广播的注册也相对简单,我们可以利用动态广播注册的方式把我们插件中的广播进行注册。今天这里也同样为了完整性,也来讲一讲广播在FrameWork中的流程,包括注册和发送两个过程。跟Activity和Service类似,广播也是要与AMS打交道。

一.动态广播注册

在前两篇讲ActivityService中我们应该已经知道了ActivityService都是继承的ContextWrapper类,而且了解装饰模式的应该知道,ContextWrapper在这里只是一个装饰组件角色,具体的组件角色是ContextImpl,所以我们在Activity或者Service中调用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) {
                    //这个Handler是后面用来分发AMS发送的广播用的,可以看到这里是在主线程中分发,
                   //如果你想要在子线程中分发,可以自己指定一个Handler
                    scheduler = mMainThread.getHandler();
                }
                //mPackageInfo是LoadedApk实例,这个方法主要是获取 rd,这是一个IIntentReceiver对象,
                //可以看出这是一个Binder本地对象,具有跨进程的能力
                rd = mPackageInfo.getReceiverDispatcher(
                    receiver, context, scheduler,
                    mMainThread.getInstrumentation(), true);
            } else {
              ......         
            }    
        }
        try {
            final Intent intent = ActivityManagerNative.getDefault().registerReceiver(
                    mMainThread.getApplicationThread(), mBasePackageName,
                    rd, filter, broadcastPermission, userId);
            if (intent != null) {
                intent.setExtrasClassLoader(getClassLoader());
                intent.prepareToEnterProcess();
            }
            return intent;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

我们看到程序里面如果scheduler为空就会从mMainThread中获取,这里的mMainThread是一个ActivityThread实例,在ActivityManagerService发送广播过来用这个来分发的。而且我们看到mPackageInfo是个LoadedApk实例,getReceiverDispatcher()方法获取到的是一个Binder本地对象,具体我们看下这个方法:

  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) {//这个是true
                map = mReceivers.get(context);
                if (map != null) {
                    //查看是否有相应的dispatcher了
                    rd = map.get(r);
                }
            }
            if (rd == null) {
                //没有则新建一个,这里的context可能是Activity,Service等
                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 {
                 //检查广播的context,handler是否一致
                rd.validate(context, handler);
            }
            rd.mForgotten = false;
            return rd.getIIntentReceiver();
        }
    }

从上面的方法可以看出,首先是从mReceivers中查找有没有对应context对应的map,这里的mReceiversLoadedApk的一个成员变量,这里它是一个以Activity即context为key,value为map的数据结构.然后根据我们要发送的BroadcastReceiver我们就可以查找到广播接收发布器ReceiverDispatcher。但是这里的ReceiverDispatcher是个什么东西呢?我们一起来看看:

 static final class ReceiverDispatcher {
        //Binder本地对象,用于跨进程通讯
        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;
            }
            //知道AIDL写法的人应该知道,这是远程会调用到的方法
            @Override
            public void performReceive(Intent intent, int resultCode, String data,
                    Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
                        ......
            }
        }    
        ......
        ReceiverDispatcher(BroadcastReceiver receiver, Context context,
                Handler activityThread, Instrumentation instrumentation,
                boolean registered) {
            //Binder本地对象,用于跨进程通讯
            mIIntentReceiver = new InnerReceiver(this, !registered);
            //广播接收者
             mReceiver = receiver;
            //广播的发送者
            mContext = context;
            //这是主线程,后面消息分发也是在这个类中的H类中
            mActivityThread = activityThread;
            .......
        }
        .....
        IIntentReceiver getIIntentReceiver() {  
            return mIIntentReceiver;  
        } 
        .....
}

我们看到ReceiverDispatcher在构造函数中会创建一个InnerReceiver实例,这是一个Binder对象,实现了IIntentReceiver接口,我们可以通过ReceiverDispatcher#getIIntentReceiver来获取,然后会把这个对象注册到AMS中,以便用来接收广播。同时,构造函数里面会保存activityThread以便广播分发的时候使用,所以到这里我们知道getReceiverDispatcher()方法返回的就是这个InnerReceiver实例。

然后我们回到registerReceiverInternal方法,这个方法获取了InnerReceiver类型的Binder对象之后,就直接注册到AMS了。然后我们看AMS中的registerReceiver方法:

  public Intent registerReceiver(IApplicationThread caller, String callerPackage,
            IIntentReceiver receiver, IntentFilter filter, String permission, int userId) {
        enforceNotIsolatedCaller("registerReceiver");
        ArrayList<Intent> stickyIntents = null;
        ProcessRecord callerApp = null;
        int callingUid;
        int callingPid;
        synchronized(this) {
            if (caller != null) {
                //获取当前进程对象
                callerApp = getRecordForAppLocked(caller);
                ......
            } else {
                callerPackage = null;
                callingUid = Binder.getCallingUid();
                callingPid = Binder.getCallingPid();
            }

            userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, true,
                    ALLOW_FULL_ONLY, "registerReceiver", callerPackage);
            //获取IntentFilter中的action
            Iterator<String> actions = filter.actionsIterator();
            if (actions == null) {
                ArrayList<String> noAction = new ArrayList<String>(1);
                noAction.add(null);
                actions = noAction.iterator();
            }

            // Collect stickies of users
            //从actions中,先把粘性广播帅选出来,放进stickyIntents中
            int[] userIds = { UserHandle.USER_ALL, UserHandle.getUserId(callingUid) };
            while (actions.hasNext()) {
                String action = actions.next();
                for (int id : userIds) {
               //从mStickyBroadcasts中查看用户的sticky Intent,mStickyBroadcasts存了系统所有的
                //粘性广播,为什么叫做Sticky Intent,就是这个最后发出的广播虽然被处理完了,但是仍然被粘住在ActivityManagerService中,以便下一个注册相应Action类型的广播接收器还能继承处理
                    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);
                        }
                    }
                }
            }
        }

        ArrayList<Intent> allSticky = null;
        if (stickyIntents != null) {
            final ContentResolver resolver = mContext.getContentResolver();
            // Look for any matching sticky broadcasts...
            for (int i = 0, N = stickyIntents.size(); i < N; i++) {
                Intent intent = stickyIntents.get(i);
                // If intent has scheme "content", it will need to acccess
                // provider that needs to lock mProviderMap in ActivityThread
                // and also it may need to wait application response, so we
                // cannot lock ActivityManagerService here.
                if (filter.match(resolver, intent, true, TAG) >= 0) {
                    if (allSticky == null) {
                        allSticky = new ArrayList<Intent>();
                    }
                    allSticky.add(intent);
                }
            }
        }

        // The first sticky in the list is returned directly back to the client.
        Intent sticky = allSticky != null ? allSticky.get(0) : null;
        if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Register receiver " + filter + ": " + sticky);
        if (receiver == null) {
            return sticky;
        }

        synchronized (this) {
            if (callerApp != null && (callerApp.thread == null
                    || callerApp.thread.asBinder() != caller.asBinder())) {
                // Original caller already died
                //进程不存在(死亡了),也是不能注册成功的
                return null;
            }
            //这里其实就是把广播接收器receiver保存一个ReceiverList列表中,这个列表的宿主进程是rl.app,
            //这里就是MainActivity所在的进程了,在AMS中,用一个进程记录块来表示这个应用程序进程,
            //它里面有一个列表receivers,专门用来保存这个进程注册的广播接收器。
            ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
            if (rl == null) {
                rl = new ReceiverList(this, callerApp, callingPid, callingUid,
                        userId, receiver);
                if (rl.app != null) {
                    // //把广播接收者列表加到这个进程对象的receivers中
                    rl.app.receivers.add(rl);
                } else {
                    try {
                        //注册的进程不存在则销毁接收列表
                        receiver.asBinder().linkToDeath(rl, 0);
                    } catch (RemoteException e) {
                        return sticky;
                    }
                    rl.linkedToDeath = true;
                }
                mRegisteredReceivers.put(receiver.asBinder(), rl);
            } else if (rl.uid != callingUid) {
             .......
            } else if (rl.pid != callingPid) {
            ........  
            } else if (rl.userId != userId) {
            ........
            }
            //由filter,rl等参数构建一个BroadcastFilter,看名字可以知道每个广播接收者就是
            //一个BroadcastFilter,这是动态广播注册时候封装的广播接收者对象
            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);

            // Enqueue broadcasts for all existing stickies that match
            // this filter.
           //如果是粘性广播,创建BroadcastRecord,并添加到
        //BroadcastQueue的并行广播队列(mParallelBroadcasts),
        //注册后调用AMS来尽快处理该广播。
            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用来记录广播接收者,首先会根据客户端传进来的Binder对象InnerReceiver获取ReceiverList,所以我们知道,一个客户端ReceiverDispatcher对应于一个ReceiverList,最后会把这个广播接收者构建出BroadcastFilter对象,然后保存到ReceiveList中,到这里动态广播注册就已经注册完毕。

2.静态广播注册

其实我们插件化开发中用的都是动态广播来处理,但是既然将广播那这也是不得不说一下,我们知道manifest文件的解析是PackageParser来解析的。所以静态广播也不例外,在程序安装的时候,PackageParser会对apk中的xml进行扫描,然后把组件消息进行记录。我们在PackageParserparseSplitApplication方法中可以看到:

             if (tagName.equals("activity")) {
                Activity a = parseActivity(owner, res, parser, flags, outError, false,
                        owner.baseHardwareAccelerated);
                if (a == null) {
                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                    return false;
                }

                owner.activities.add(a);

            } else if (tagName.equals("receiver")) {
                Activity a = parseActivity(owner, res, parser, flags, outError, true, false);
                if (a == null) {
                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                    return false;
                }

                owner.receivers.add(a);

            } else if (tagName.equals("service")) {
                Service s = parseService(owner, res, parser, flags, outError);
                if (s == null) {
                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                    return false;
                }

                owner.services.add(s);

            } else if (tagName.equals("provider")) {
                Provider p = parseProvider(owner, res, parser, flags, outError);
                if (p == null) {
                    mParseError = PackageManager.INSTALL_PARSE_FAILED_MANIFEST_MALFORMED;
                    return false;
                }

                owner.providers.add(p);

            } else if (tagName.equals("activity-alias")) {
         .....
            } else if (parser.getName().equals("meta-data")) {
         .....
                }else if ....
            }

我们看到这段代码很简单,就是用pullparser解析xml文件,然后把相应的节点解析放在内部类Package相应的内部变量中,这里的receiver是放在owner#receivers中,然后AMS在collectReceiverComponents()会通过调用PMS(PackageManagerService)的方法queryIntentReceivers()来查找注册的广播接收者:

  @Override
    public @NonNull ParceledListSlice<ResolveInfo> queryIntentReceivers(Intent intent,
            String resolvedType, int flags, int userId) {
        return new ParceledListSlice<>(
                queryIntentReceiversInternal(intent, resolvedType, flags, userId));
    }

    private @NonNull List<ResolveInfo> queryIntentReceiversInternal(Intent intent,
            String resolvedType, int flags, int userId) {
        if (!sUserManager.exists(userId)) return Collections.emptyList();
        flags = updateFlagsForResolve(flags, userId, intent);
      //获取组件名称,这里就是receiver的名称
        ComponentName comp = intent.getComponent();
        if (comp == null) {
            if (intent.getSelector() != null) {
                intent = intent.getSelector();
                comp = intent.getComponent();
            }
        }
        if (comp != null) {
            List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);
          //获取广播的信息
            ActivityInfo ai = getReceiverInfo(comp, flags, userId);
            if (ai != null) {
                ResolveInfo ri = new ResolveInfo();
                ri.activityInfo = ai;
                list.add(ri);
            }
            return list;
        }

        // reader
        synchronized (mPackages) {
            String pkgName = intent.getPackage();
            if (pkgName == null) {
              //  根据intent返回manifest中配置的receiver的信息,封装在ResolveInfo
                return mReceivers.queryIntent(intent, resolvedType, flags, userId);
            }
            final PackageParser.Package pkg = mPackages.get(pkgName);
            if (pkg != null) {
                return mReceivers.queryIntentForPackage(intent, resolvedType, flags, pkg.receivers,
                        userId);
            }
            return Collections.emptyList();
        }
    }

我们看到,我们动态广播是动态调用registerReceiver在往AMS注册广播,静态广播是通过PP解析xml文件注册广播,然后AMS通过访问PMS的方法来获取注册的广播。整体流程还是很清晰的,如果不知道,请大家认真再看看。

总结:注册广播的流程不是很复杂,这里我建议大家多关注动态广播注册,因为到时会用到,大家大概有个印象,希望大家不要厌恶看源码,看多了自然就习惯了,希望大家旅途愉快。

    原文作者:ZJ_Rocky
    原文地址: https://www.jianshu.com/p/3481a0c7ba22
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞