美文网首页Android开发Android技术知识Android开发
Android 广播注册发送与接收原理简介与源码分析

Android 广播注册发送与接收原理简介与源码分析

作者: ScottStone | 来源:发表于2021-06-30 20:14 被阅读0次

    源码基于Android 11 API 30

    BroadcastReceiver是一种消息型组件,用于在不同的组件或者是不同的应用之间传递信息。同样的BroadcastReceiver也不能被用户感知,BroadcastReceiver也叫广播,广播的注册有两种方式:静态注册和动态注册。静态注册是指在AndroidManifest中注册广播,这种广播在应用安装时会被系统解析,此种形式的广播不需要应用启动就可以收到相应的广播。动态注册广播需要通过Context.registerReceiver()来实现,并且在不需要的时候要通过Context.unRegisterReceiver()来解除广播,此种形态的广播必须要应用启动才能注册并接收广播,因为应用不启动就无法注册广播,无法注册广播就无法收到相应的广播。在实际开发中通过Context的一系列send方法来发送广播,被发送的广播会被系统发送给感兴趣的广播接收者,发送和接收过程的匹配是通过广播接收者的<intent-filter>来描述的。可以发现,BroadcastReceiver组件可以用来实现低耦合的观察者模式,观察者和被观察者之间可以没有任何耦合。由于BroadcastReceiver的特性,它不适合用来执行耗时操作。BroadcastReceiver组件一般来说不需要停止,它也没有停止的概念。
    下面从广播的注册、发送与接收几个过程展开介绍。

    1. 广播的注册

    广播的注册分为动态注册与静态注册两种。静态注册的注册过程是在应用安装时由PackageManager-Service完成注册的。我们这里主要介绍动态注册的过程。下面是一个典型的动态注册、发送于接受广播的代码:

    ...
        //通常需要自定义一个BroadcastReceiver,这里只是示例避免代码过长
        BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                //Do something
                abortBroadcast();
            }
        };
        IntentFilter intentFilter;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_broadcast);
            intentFilter = new IntentFilter();
            intentFilter.addAction("com.example.myapplication.DEMOBROADCAST");
            registerReceiver(broadcastReceiver,intentFilter);
    
            Button button = findViewById(R.id.button);
            button.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Intent intent = new Intent("com.example.myapplication.DEMOBROADCAST");
                    sendBroadcast(intent);
                }
            });
        }
    ...
    

    上面的代码可以明显的看到,广播的着册方法是registerReceiver,这个方法是在ContextWrapper中定义的,与Service的启动与绑定类似,registerReceiver方法有调用了mBase的方法,mBase的实现是ContextImpl,ContextImpl的registerReceiver方法在嵌套调用后最后调用了registerReceiverInternal方法,具体看下代码:

    //ContextWrapper.java
    ...
     @Override
        public Intent registerReceiver(
            BroadcastReceiver receiver, IntentFilter filter) {
            return mBase.registerReceiver(receiver, filter);
        }
    ...
    
    //ContextImpl.java
    ...
    private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,
                IntentFilter filter, String broadcastPermission,
                Handler scheduler, Context context, int flags) {
            IIntentReceiver rd = null;
            if (receiver != null) {//判断receiver是否存在
                
                 //判断mPackageInfo(LoadedApk类型)与Context是否都存在
                if (mPackageInfo != null && context != null) {
                    if (scheduler == null) {
                        scheduler = mMainThread.getHandler();
                    }
                  //获取IIntentReceiver
                    rd = mPackageInfo.getReceiverDispatcher(
                        receiver, context, scheduler,
                        mMainThread.getInstrumentation(), true);
                } else {
                //mPackageInfo不存在
    
                    if (scheduler == null) {
                        scheduler = mMainThread.getHandler();
                    }
                  //创建IIntentReceiver
                    rd = new LoadedApk.ReceiverDispatcher(
                            receiver, context, scheduler, null, true).getIIntentReceiver();
                }
            }
            try {
                //获取AMS调用registerReceiverWithFeature方法
                final Intent intent = ActivityManager.getService().registerReceiverWithFeature(
                        mMainThread.getApplicationThread(), mBasePackageName, getAttributionTag(), rd,
                        filter, broadcastPermission, userId, flags);
                if (intent != null) {
                    intent.setExtrasClassLoader(getClassLoader());
                    intent.prepareToEnterProcess();
                }
                return intent;
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        }
    ...
    
    

    最后可以看到调用了AMS的registerReceiverWithFeature,上面代码中有一个重要变量rd,IIntentReceiver类型,这显然是一个Binder接口,具体实现在LoadedApk.ReceiverDispatcher.InnerReceiver:

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

    回到AMS的registerReceiverWithFeature方法,方法比较长,核心处理添加了注释:

    public Intent registerReceiverWithFeature(IApplicationThread caller, String callerPackage,
                String callerFeatureId, 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) {
                    //获取ProcessRecord,其用于描述注册的接收者Activity所在应用程序进程
                    callerApp = getRecordForAppLocked(caller);
                   ...
                    callingUid = callerApp.info.uid;
                    callingPid = callerApp.pid;
                } else {
                    callerPackage = null;
                    callingUid = Binder.getCallingUid();
                    callingPid = Binder.getCallingPid();
                }
    
                 ...
                //根据传入的IntentFilter获取Action列表
                Iterator<String> actions = filter.actionsIterator();
                if (actions == null) {
                    ArrayList<String> noAction = new ArrayList<String>(1);
                    noAction.add(null);
                    actions = noAction.iterator();
                }
    
                // userIds表示应用程序的uid,对于Android系统来说,用户就是一个个的应用程序。
                int[] userIds = { UserHandle.USER_ALL, UserHandle.getUserId(callingUid) };
                while (actions.hasNext()) {
                    String action = actions.next();
                    for (int id : userIds) {
                        //根据Action列表与应用程序列表(userIds)获取所有粘性广播(StickyBroadcast)
                        ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(id);
                        if (stickies != null) {
                        //获取所有粘性广播的intent
                            ArrayList<Intent> intents = stickies.get(action);
                            if (intents != null) {
                                if (stickyIntents == null) {
                                    stickyIntents = new ArrayList<Intent>();
                                }
                                //保存粘性广播的intent
                                stickyIntents.addAll(intents);
                            }
                        }
                    }
                }
            }
    
            ArrayList<Intent> allSticky = null;
            if (stickyIntents != null) {
                final ContentResolver resolver = mContext.getContentResolver();
                // 查找匹配的粘性广播
                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;
                    }
                  ...
                }
            }
    
          ...
    
            synchronized (this) {
               ...
                //获取ReceiverList
                ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
                if (rl == null) {
                    //ReceiverList为空新建
                    rl = new ReceiverList(this, callerApp, callingPid, callingUid,
                            userId, receiver);
                    if (rl.app != null) {
                      //判断当前receiver是否超过允许的最的值
                        final int totalReceiversForApp = rl.app.receivers.size();
                        if (totalReceiversForApp >= MAX_RECEIVERS_ALLOWED_PER_APP) {
                            throw new IllegalStateException("Too many receivers, total of "
                                    + totalReceiversForApp + ", registered for pid: "
                                    + rl.pid + ", callerPackage: " + callerPackage);
                        }
                        rl.app.receivers.add(rl);
                    } else {
                       ...
                    }
                }
                //创建BroadcastFilter用来记录已注册的广播接收者信息
                BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage, callerFeatureId,
                        permission, callingUid, userId, instantApp, visibleToInstantApps);
                //判断接收者列表ReceiverList中是否存在对应接收者,不存在则添加
                if (rl.containsFilter(filter)) {
                    Slog.w(TAG, "Receiver with filter " + filter
                            + " already registered for pid " + rl.pid
                            + ", callerPackage is " + callerPackage);
                } else {
                    rl.add(bf);
                    if (!bf.debugCheck()) {
                        Slog.w(TAG, "==> For Dynamic broadcast");
                    }
                    //记录接收者,AMS全局变量,
                    //当AMS接收到广播时会从mReceiverResolver找到所有的接收者,实现注册的效果。
                    mReceiverResolver.addFilter(bf);
                }
    
                // 广播队列中入队所有将符合filter的粘性广播
                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, null, -1, -1, false, null, null, OP_NONE, null, receivers,
                                null, 0, null, null, false, true, true, -1, false,
                                false /* only PRE_BOOT_COMPLETED should be exempt, no stickies */);
                        queue.enqueueParallelBroadcastLocked(r);
                        queue.scheduleBroadcastsLocked();
                    }
                }
    
                return sticky;
            }
        }
    

    以上就是广播注册的过程,具体的步骤在注释中一有所体现。

    2. 广播的发送与接收

    2.1 ContextImp 调用AMS

    我们知道广播有标准广播(无序广播)、有序广播和粘性广播。下面还是以开始那个例子(标准广播)展开介绍。与广播的注册类似,首先是调用了ContextWraper的sendBroadcast:

    ...
     @Override
        public void sendBroadcast(Intent intent) {
            mBase.sendBroadcast(intent);
        }
    ...
    

    sendBroadcast调用了ContextImp的sendBroadcast方法:

    ...
    @Override
        public void sendBroadcast(Intent intent) {
            warnIfCallingFromSystemProcess();
            String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
            try {
                intent.prepareToLeaveProcess(this);
                ActivityManager.getService().broadcastIntentWithFeature(
                        mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType,
                        null, Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false,
                        false, getUserId());
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        }
    ...
    

    上面的代码,很明显调用了AMS的broadcastIntentWithFeature方法:

    public final int broadcastIntentWithFeature(IApplicationThread caller, String callingFeatureId,
                Intent intent, String resolvedType, IIntentReceiver resultTo,
                int resultCode, String resultData, Bundle resultExtras,
                String[] requiredPermissions, int appOp, Bundle bOptions,
                boolean serialized, boolean sticky, int userId) {
            enforceNotIsolatedCaller("broadcastIntent");
            synchronized(this) {
                //验证intent
                intent = verifyBroadcastLocked(intent);
                
                //获取应用程序进程信息
                final ProcessRecord callerApp = getRecordForAppLocked(caller);
                final int callingPid = Binder.getCallingPid();
                final int callingUid = Binder.getCallingUid();
    
                final long origId = Binder.clearCallingIdentity();
                try {
                    return broadcastIntentLocked(callerApp,
                            callerApp != null ? callerApp.info.packageName : null, callingFeatureId,
                            intent, resolvedType, resultTo, resultCode, resultData, resultExtras,
                            requiredPermissions, appOp, bOptions, serialized, sticky,
                            callingPid, callingUid, callingUid, callingPid, userId);
                } finally {
                    Binder.restoreCallingIdentity(origId);
                }
            }
        }
    

    最后调用了AMS的broadcastIntentLocked方法,方法特别的长,这里这给出部分源码:

     final int broadcastIntentLocked(ProcessRecord callerApp, String callerPackage,
                @Nullable String callerFeatureId, Intent intent, String resolvedType,
                IIntentReceiver resultTo, int resultCode, String resultData,
                Bundle resultExtras, String[] requiredPermissions, int appOp, Bundle bOptions,
                boolean ordered, boolean sticky, int callingPid, int callingUid, int realCallingUid,
                int realCallingPid, int userId, boolean allowBackgroundActivityStarts,
                @Nullable int[] broadcastWhitelist) {
                ...
                //获取注册的粘性广播
                ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(userId);
                if (stickies == null) {
                    stickies = new ArrayMap<>();
                    mStickyBroadcasts.put(userId, stickies);
                }
                //找到符合当前Action的广播
                ArrayList<Intent> list = stickies.get(intent.getAction());
                if (list == null) {
                    list = new ArrayList<>();
                    stickies.put(intent.getAction(), list);
                }
                ...
                //接收者存在
                if ((receivers != null && receivers.size() > 0)
                    || resultTo != null) {
                //获取广播队列,获取BroadcastRecord信息
                BroadcastQueue queue = broadcastQueueForIntent(intent);
                BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp, callerPackage,
                        callerFeatureId, callingPid, callingUid, callerInstantApp, resolvedType,
                        requiredPermissions, appOp, brOptions, receivers, resultTo, resultCode,
                        resultData, resultExtras, ordered, sticky, false, userId,
                        allowBackgroundActivityStarts, timeoutExempt);
                  ...
                 queue.enqueueOrderedBroadcastLocked(r);
                //执行广播schedule
                 queue.scheduleBroadcastsLocked();
                  }
    ...
                 return ActivityManager.BROADCAST_SUCCESS;
    }
    

    从上面的代码可以看出,最后AMS调用了BroadcastQueue的scheduleBroadcastsLocked方法。

    2.2 ContextImp 调用AMS调用BroadcastReceiver

    接上一节的代码继续看,BroadcastQueue的scheduleBroadcastsLocked方法:

    public void scheduleBroadcastsLocked() {
            if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Schedule broadcasts ["
                    + mQueueName + "]: current="
                    + mBroadcastsScheduled);
    
            if (mBroadcastsScheduled) {
                return;
            }
            mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
            mBroadcastsScheduled = true;
        }
    

    scheduleBroadcastsLocked方法内部,通过BroadcastHandler发送了一个消息:BROADCAST_INTENT_MSG,这肯定交由对应的handleMessage方法处理,我们再看下BroadcastHandler的handleMessage方法:

     @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case BROADCAST_INTENT_MSG: {
                        if (DEBUG_BROADCAST) Slog.v(
                                TAG_BROADCAST, "Received BROADCAST_INTENT_MSG ["
                                + mQueueName + "]");
                        processNextBroadcast(true);
                    } break;
                    case BROADCAST_TIMEOUT_MSG: {
                        synchronized (mService) {
                            broadcastTimeoutLocked(true);
                        }
                    } break;
                }
            }
    

    上面代码很明显,处理BROADCAST_INTENT_MSG类型消息后,调用了processNextBroadcast方法,然后又调用了processNextBroadcastLocked方法,方法比较长,特殊处理也很多,这里只关注下标准广播的处理:

    final void processNextBroadcast(boolean fromMsg) {
            synchronized (mService) {
                processNextBroadcastLocked(fromMsg, false);
            }
        }
    ...
     final void processNextBroadcastLocked(boolean fromMsg, boolean skipOomAdj) {
      ...   
       if (fromMsg) {//已经处理了BROADCAST_INTENT_MSG类型的消息
                mBroadcastsScheduled = false;
            }
    
            // 处理无序广播,也就是标准的广播
            while (mParallelBroadcasts.size() > 0) {
              //获取广播
                r = mParallelBroadcasts.remove(0);
               ...
                for (int i=0; i<N; i++) {
                    Object target = r.receivers.get(i);
                    if (DEBUG_BROADCAST)  Slog.v(TAG_BROADCAST,
                            "Delivering non-ordered on [" + mQueueName + "] to registered "
                            + target + ": " + r);
                  //广播发送给相应的接收者
                    deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false, i);
                }
                addBroadcastToHistoryLocked(r);
                if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Done with parallel broadcast ["
                        + mQueueName + "] " + r);
            }
    ...
    }
    

    下面继续看下deliverToRegisteredReceiverLocked方法:

    private void deliverToRegisteredReceiverLocked(BroadcastRecord r,
                BroadcastFilter filter, boolean ordered, int index) {
    
                ....//省略一堆验证
            
            // 有序广播的处理.
            if (ordered) {
                r.receiver = filter.receiverList.receiver.asBinder();
                r.curFilter = filter;
                filter.receiverList.curBroadcast = r;
                r.state = BroadcastRecord.CALL_IN_RECEIVE;
                if (filter.receiverList.app != null) {
                    r.curApp = filter.receiverList.app;
                    filter.receiverList.app.curReceivers.add(r);
                    mService.updateOomAdjLocked(r.curApp, true,
                            OomAdjuster.OOM_ADJ_REASON_START_RECEIVER);
                }
            }
            try {
                if (DEBUG_BROADCAST_LIGHT) Slog.i(TAG_BROADCAST,
                        "Delivering to " + filter + " : " + r);
                if (filter.receiverList.app != null && filter.receiverList.app.inFullBackup) {
                    //如果是有序广播,继续传给下一个接收者.
                    if (ordered) {
                        skipReceiverLocked(r);
                    }
                } else {
                    r.receiverTime = SystemClock.uptimeMillis();
                    maybeAddAllowBackgroundActivityStartsToken(filter.receiverList.app, r);
                    //各种检查及验证后最终调用的方法
                    performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
                            new Intent(r.intent), r.resultCode, r.resultData,
                            r.resultExtras, r.ordered, r.initialSticky, r.userId);
                   
                    if (r.allowBackgroundActivityStarts && !r.ordered) {
                        postActivityStartTokenRemoval(filter.receiverList.app, r);
                    }
                }
                if (ordered) {
                    r.state = BroadcastRecord.CALL_DONE_RECEIVE;
                }
            } catch (RemoteException e) {
                ...
            }
    }
    

    继续看performReceiveLocked方法:

    void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,
                Intent intent, int resultCode, String data, Bundle extras,
                boolean ordered, boolean sticky, int sendingUser)
                throws RemoteException {
            // Send the intent to the receiver asynchronously using one-way binder calls.
            if (app != null) {//验证应用程序进程是否存在
                if (app.thread != null) {//验证应用程序进程是否已运行
                    // If we have an app thread, do the call through that so it is
                    // 应用程序进程存在,执行一个单向请求
                    try {
                        app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
                                data, extras, ordered, sticky, sendingUser, app.getReportedProcState());
                    } catch (RemoteException ex) {
                       ...
                    }
                } 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的performReceive方法,IIntentReceiver是通过LoadedApk中的InnerReceiver实现的:

    final static class InnerReceiver extends IIntentReceiver.Stub {
    ...
    @Override
                public void performReceive(Intent intent, int resultCode, String data,
                        Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
                    final LoadedApk.ReceiverDispatcher rd;
                    if (intent == null) {
                        Log.wtf(TAG, "Null intent received");
                        rd = null;
                    } else {
                        rd = mDispatcher.get();
                    }
                  ...
                    if (rd != null) {
                      //ReceiverDispatcher的调用
                        rd.performReceive(intent, resultCode, data, extras,
                                ordered, sticky, sendingUser);
                    } else {
                    ...
                    }
                }
    ...
    }
    

    上面的IIntentReceiver通过AIDL实现进程间通信,最后调用了ReceiverDispatcher的performReceive方法:

    public void performReceive(Intent intent, int resultCode, String data,
                    Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
                //封装广播的intent信息
                final Args args = new Args(intent, resultCode, data, extras, ordered,
                        sticky, sendingUser);
                if (intent == null) {
                    Log.wtf(TAG, "Null intent received");
                } else {
                   ...
                }
                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);
                    }
                }
            }
    

    mActivityThread.post(args.getRunnable())的mActivityThread是个Handler,具体实现是H类,这里将args.getRunnable()作为参数传入Handler的post方法,看下Args的getRunnable方法:

    public final Runnable getRunnable() {
                    return () -> {
                        final BroadcastReceiver receiver = mReceiver;
                        final boolean ordered = mOrdered;
    
                       ...
    
                        //获取AMS
                        final IActivityManager mgr = ActivityManager.getService();
                        final Intent intent = mCurIntent;
                       ...
                        try {
                            //创建BroadcastReceiver
                            ClassLoader cl = mReceiver.getClass().getClassLoader();
                            intent.setExtrasClassLoader(cl);
                            intent.prepareToEnterProcess();
                            setExtrasClassLoader(cl);
                            receiver.setPendingResult(this);
                            //调用onReceive方法
                            receiver.onReceive(mContext, intent);
                        } catch (Exception e) {
                          ...
                        }
                     ...
                    };
                }
            }
    

    代码最后创建了一个BroadcastReceiver并调用了onReceive方法。

    3.总结

    3.1 广播注册 ContextWraper到启动AMS的过程
    image.png
    3.2 广播发送 ContextWraper到启动AMS的过程
    image.png
    3.3广播发送与接收 AMS到启动BroadcastReceiver的过程
    image.png

    相关文章

      网友评论

        本文标题:Android 广播注册发送与接收原理简介与源码分析

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