Android源码分析 BroadcastReceiver 注册过程

Posted 小图包

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android源码分析 BroadcastReceiver 注册过程相关的知识,希望对你有一定的参考价值。

广播的注册过程

首先,想要使用广播就得通过 registerReceiver 方法注册一个广播,最终也是在 Activity 的父类 ContextWrapper 中实现,代码如下:

//ContextWrapper.java
		/**
     * 应用调用注册广播的函数
     * @param receiver The BroadcastReceiver to handle the broadcast.
     * @param filter Selects the Intent broadcasts to be received.
     *
     * @return
     */
    @Override
    public Intent registerReceiver(
        BroadcastReceiver receiver, IntentFilter filter) 
        //mBase 是 Context的引用
        return mBase.registerReceiver(receiver, filter);
    

直接看 Context 的实现类 ContextImpl 中的实现,代码如下:

    /**
     * Context 调用
     * @param receiver The BroadcastReceiver to handle the broadcast.
     * @param filter Selects the Intent broadcasts to be received.
     *
     * @return
     */
    @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(), 0);
    


    private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,
                                            IntentFilter filter, String broadcastPermission,
                                            Handler scheduler, Context context, int flags) 
        IIntentReceiver rd = null;
        if (receiver != null) 
             1
            if (mPackageInfo != null && context != null) 
                if (scheduler == null) 
                    //获取 ActivityThread H
                    scheduler = mMainThread.getHandler();
                
                //获取 IIntentReceiver 对象,通过它与 AMS 交互,并且通过 Handler 传递消息
                rd = mPackageInfo.getReceiverDispatcher(
                        receiver, context, scheduler,
                       2
                        mMainThread.getInstrumentation(), true);
             else 
                if (scheduler == null) 
                    scheduler = mMainThread.getHandler();
                
                3
                rd = new LoadedApk.ReceiverDispatcher(
                        receiver, context, scheduler, null, true).getIIntentReceiver();
            
        
        try 
            //调用 AMS 的 registerReceiver
            4
            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();
        
    

总结如上步骤

1 判断 mPackageInfo 与 context 是否为空

2 通过 mPackageInfo 方法调用 getReceiverDispatch 方法获取 rd 对象

3 处的代码来创建 rd 对象

2 步代码主要就是来获取 IIntentReceiver 对象,它是一个 Binder 接口,用于广播的跨进程通信,它在在 LoadedApk.ReceiverDispatcher.InnerReceiver 中实现,代码如下所示:

//LoadedApk.java
    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;
            
          
          ....
            
        

回到 registerReceiverInternal 方法,在注释 4 处调用 AMS 代理类 IActivityManager 的 registerReceiver 方法,通过进程间通信最后在 AMS 类的 registerReceiver 方法,并将 IIntentReceiver 类型的 rd 引用也一并传递进 AMS 中,这里之所以不直接传入客户端的 BroadcastReceiver 而是传入 IIntetnReceiver ,因为注册广播是一个跨进程的过程,需要具有跨进程通信功能的 IIntentReceiver ,你也可以把它理解为一个中间者,registerReceiver 方法内部比较多,这里分为两部分

我们先看

registerReceiver 方法的第一部分

//AMS.java
    /**
     * 这里是通过 Binder 通知调用
     * @return Intent
     */
    public Intent registerReceiver(IApplicationThread caller, String callerPackage,
            IIntentReceiver receiver, IntentFilter filter, String permission, int userId,
            int flags) 
        enforceNotIsolatedCaller("registerReceiver");
        ArrayList<Intent> stickyIntents = null;
        ProcessRecord callerApp = null;
        final boolean visibleToInstantApps
                = (flags & Context.RECEIVER_VISIBLE_TO_INSTANT_APPS) != 0;
        int callingUid;
        int callingPid;
        boolean instantApp;
        synchronized(this) 
            if (caller != null) 
                1
                callerApp = getRecordForAppLocked(caller);
                if (callerApp == null) 
                    throw new SecurityException(
                            "Unable to find app for caller " + caller
                            + " (pid=" + Binder.getCallingPid()
                            + ") when registering receiver " + receiver);
                
                if (callerApp.info.uid != SYSTEM_UID &&
                        !callerApp.pkgList.containsKey(callerPackage) &&
                        !"android".equals(callerPackage)) 
                    throw new SecurityException("Given caller package " + callerPackage
                            + " is not running in process " + callerApp);
                
                callingUid = callerApp.info.uid;
                callingPid = callerApp.pid;
             else 
                callerPackage = null;
                callingUid = Binder.getCallingUid();
                callingPid = Binder.getCallingPid();
            

            instantApp = isInstantApp(callerApp, callerPackage, callingUid);
            userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, true,
                    ALLOW_FULL_ONLY, "registerReceiver", callerPackage);

            2
            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
            int[] userIds =  UserHandle.USER_ALL, UserHandle.getUserId(callingUid) ;
            while (actions.hasNext()) 
                
                String action = actions.next();
                for (int id : userIds) 
                    //根据actions 列表和 userIds 得到所有的粘性广播的 intent,并在注释 3 处传入到 stickyIntents 中
                    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>();
                            
                            3 
                            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);
                // Don't provided intents that aren't available to instant apps.
                if (instantApp &&
                        (intent.getFlags() & Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS) == 0) 
                    continue;
                
                //开始匹配
                if (filter.match(resolver, intent, true, TAG) >= 0) 
                    if (allSticky == null) 
                        allSticky = new ArrayList<Intent>();
                    
                    4
                    allSticky.add(intent);
                
            
        

        ....//后面代码 第二部分 讲解

            return sticky;
        
    

总结如上

1 通过 getRecordForAppLocked 方法得到 ProcessRecord 类型的 callerApp 对象,它用于描述请求 AMS 注册广播接收者的 Activity 所在的应用程序进程

2 处根据传入的 IntentFilter 类型的 filter 得到一个 actions 列表,根据 actions 列表和 userIds 得到所有的粘性广播的 intent 

3 处传入到 stickyIntents 中。接下来从 stickyIntent 中匹配传入的参数 filter 的粘性广播的 intent

4 处将这些 intent 存入到 allSticky 列表中,从这里可以看出粘性广播是存储在 AMS 中的。

registerReceiver 方法的 第二部分

接下来看 AMS 的 registerReceiver 方法剩余内容,代码如下所示:

//AMS.java

    /**
     * 这里是通过 Binder 通知调用
     * @return Intent
     */
    public Intent registerReceiver(IApplicationThread caller, String callerPackage,
            IIntentReceiver receiver, IntentFilter filter, String permission, int userId,
            int flags) 
     ...
       
       
          synchronized (this) 
					...
            1
            ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
            if (rl == null) //如果为空调用注释 2
               2
                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);
             else if (rl.uid != callingUid) 
                ...
            

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

    
      ...
    

总结 如上代码

1. 获取 ReceiverList 

2. 创建一个 ReceiverList 对象,它继承自 ArrayList,用于存放广播接收者

3 构建 BroadcastFilter 对象,并且将 广播接收者列表 ReceiverList 添加到进去 * 用于描述注册的广播接收者

4. 通过 add 函数将自身添加到广播接收者列表中

5. 将 BroadcastFilter 添加到 IntentResolver 类型的 mReceiverResolver 中

在注释 1 处获取到了 ReceiverList 列表,如果为空则在注释 2 处创建一个新的接收列表 ReceiverList 对象,它继承自 ArrayList,用来存储广播接收者。在注释 3 处创建 BroadcastFilter 并传入此前创建的 ReceiverList ,BroadcastFilter 用来描述注册的广播接收者,并在注释 4 处通过 add 方法将自身添加到 ReceiverList 中。在注释 5 处将 BroadcastFilter 添加到 IntentResolver 类型的 mReceiverResolver 中,这样当 AMS 接收到广播时就可以从 mReceiverResolver 中找到对应的广播接收者了。从而达到注册广播的目的。

以上是关于Android源码分析 BroadcastReceiver 注册过程的主要内容,如果未能解决你的问题,请参考以下文章

Android 音频源码分析——AudioFlinger

Android 音频源码分析——AudioTrack设备选择

Android 音频源码分析——AudioTrack设备选择

Android 通过广播获取网络状态

Android 音频源码分析——audioserver启动

Android5.1源码分析系列Settings源码分析