美文网首页Android开发程序员
你想知道的BroadcastReceiver工作原理

你想知道的BroadcastReceiver工作原理

作者: Jesse_zhao | 来源:发表于2018-03-22 00:32 被阅读407次

    一、概要

    BoradcastReceiver在Android中充当的是传递数据和消息的作用。尽管现在有很多框架(比如EventBus、Rxjava等)可以代替它在应用内传递消息和数据,但是不同的是BoradcastReceiver可以跨进新传递数据。所以了解BoradcastReceiver的实现还是比较重要的。这里分析广播机制注意围绕三个方法进行:registerReceiver()、sendBroadcast()和unregisterReceiver()。如果你只是想了解它的使用的话,可以参考下这篇文章Android广播机制总结

    1、涉及主要类

    android.app.ContextImpl.java 
    android.app.Instrumentation.java 
    android.app.ActivityThread.java
    android.app.ActivityManager.java
    com.android.server.am.ActivityManagerService.java
    android.content.BroadcastReceiver.java
    com.android.server.am.BroadcastQueue.java
    com.android.server.am.BroadcastRecord.java
    android.app.LoadedApk.java
    

    2、流程图


    BroadcastReceiver流程.png 3、简要图 BroadcastReceiver运作简单示意图.png

    从这里我们可以看出这个广播机制运用的是发布/订阅者模式来设计的。

    二、具体流程

    1.1、ContextImpl.registerReceiver()

    这里最终会调用registerReceiverInternal()方法:

      private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,
            IntentFilter filter, String broadcastPermission,
            Handler scheduler, Context context, int flags) {
        IIntentReceiver rd = null;
        if (receiver != null) {
        //这里先获取ActivityThread.java中的H实例
            if (mLoadedApk != null && context != null) {
                if (scheduler == null) {
                    scheduler = mMainThread.getHandler();
                }
                rd = mLoadedApk.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 {
        //使用Binder IPC机制和AMS进行通信
            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();
        }
    }           
    

    这里主要就是创建了LoadApk.ReceiverDispatcher的实例,这个实例用于传播广播用的。然后接下来我们看下AMS中的registerReceiver方法:

    1.2、ActivityManageService.registerReceiver()
    public Intent registerReceiver(IApplicationThread caller, String callerPackage,
            IIntentReceiver receiver, IntentFilter filter, String permission, int userId,
            int flags) {
       // 省略部分代码
        // 这里处理Sticky广播
        ...
        synchronized (this) {
          ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
            if (rl == null) {
                rl = new ReceiverList(this, callerApp, callingPid, callingUid,
                        userId, receiver);
                if (rl.app != null) {
                    rl.app.receivers.add(rl);
                } else {
                    try {
                        receiver.asBinder().linkToDeath(rl, 0);
                    } catch (RemoteException e) {
                        return sticky;
                    }
                    rl.linkedToDeath = true;
                }
                mRegisteredReceivers.put(receiver.asBinder(), rl);
            }
            BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
                    permission, callingUid, userId, instantApp, visibleToInstantApps);
            rl.add(bf);
            if (!bf.debugCheck()) {
                Slog.w(TAG, "==> For Dynamic broadcast");
            }
            //添加到Receiver集合中
            mReceiverResolver.addFilter(bf);
            // Enqueue broadcasts for all existing stickies that match
            // this filter.
            //处理Sticky广播,遍历保存的Sticky广播中有满足注册的received,直接发送
            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, false, null, null, AppOpsManager.OP_NONE, null, receivers,
                            null, 0, null, null, false, true, true, -1);
                     //将记录着BroadcastRecevied信息的BroadcastRecord加入到BroadcastQueue中
                    queue.enqueueParallelBroadcastLocked(r);
                    queue.scheduleBroadcastsLocked();
                }
            }
            return sticky;
        }
    }
    

    这里的主要逻辑是先收集用户发送的sticky广播,根据注册的received设置的IntentFilter去匹配相应的sticky广播。接着把注册的BroadcastFilter加入到mReceiverResolver的集合当中,同时也把记录着BroadcastRecevied信息的BroadcastRecord加入到BroadcastQueue中。最后一步就是如果received注册的IntentFilter有对应的sticky广播时,立即将对应的sticky广播发送给接收器,从这里可以看出sticky广播的机制(即注册时,若有相应的粘性广播相匹配时,会立即收到该广播)。

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

    发送广播时,同样用到的是binder IPC机制与AMS进行通信。接着来看下AMS中的broadcastIntent()方法。

    2.2、ActivityManageService.broadcastIntent()

    最终会调用broadcastIntentLocked()方法:

        final int broadcastIntentLocked(ProcessRecord callerApp,
            String callerPackage, 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 userId) {
    // 省略部分代码(检测权限及处理一些特别的广播)
        // Add to the sticky list if requested.
        if (sticky) {
            ...
            // 如果是粘性广播时,检查相应的权限
        }
    
        //找出已注册并满足条件的接收器 
        List receivers = null;
        List<BroadcastFilter> registeredReceivers = null;
        // Need to resolve the intent to interested receivers...
        if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY)
                 == 0) {
            receivers = collectReceiverComponents(intent, resolvedType, callingUid, users);
        }
        if (intent.getComponent() == null) {
            if (userId == UserHandle.USER_ALL && callingUid == SHELL_UID) {
                // Query one target user at a time, excluding shell-restricted users
                for (int i = 0; i < users.length; i++) {
                    if (mUserController.hasUserRestriction(
                            UserManager.DISALLOW_DEBUGGING_FEATURES, users[i])) {
                        continue;
                    }
                    List<BroadcastFilter> registeredReceiversForUser =
                            mReceiverResolver.queryIntent(intent,
                                    resolvedType, false /*defaultOnly*/, users[i]);
                    if (registeredReceivers == null) {
                        registeredReceivers = registeredReceiversForUser;
                    } else if (registeredReceiversForUser != null) {
                        registeredReceivers.addAll(registeredReceiversForUser);
                    }
                }
            } else {
                //从mReceiverResolve找出相应的接收器
                registeredReceivers = mReceiverResolver.queryIntent(intent,
                        resolvedType, false /*defaultOnly*/, userId);
            }
        }
    
        final boolean replacePending =
                (intent.getFlags()&Intent.FLAG_RECEIVER_REPLACE_PENDING) != 0;
    
        if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing broadcast: " + intent.getAction()
                + " replacePending=" + replacePending);
    
        int NR = registeredReceivers != null ? registeredReceivers.size() : 0;
        //处理正常广播
        if (!ordered && NR > 0) {
            if (isCallerSystem) {
                checkBroadcastFromSystem(intent, callerApp, callerPackage, callingUid,
                        isProtectedBroadcast, registeredReceivers);
            }
            final BroadcastQueue queue = broadcastQueueForIntent(intent);
            BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
                    callerPackage, callingPid, callingUid, callerInstantApp, resolvedType,
                    requiredPermissions, appOp, brOptions, registeredReceivers, resultTo,
                    resultCode, resultData, resultExtras, ordered, sticky, false, userId);
            if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Enqueueing parallel broadcast " + r);
            final boolean replaced = replacePending
                    && (queue.replaceParallelBroadcastLocked(r) != null);
            // Note: We assume resultTo is null for non-ordered broadcasts.
            if (!replaced) {
                queue.enqueueParallelBroadcastLocked(r);
                queue.scheduleBroadcastsLocked();
            }
            registeredReceivers = null;
            NR = 0;
        }
        // Merge into one list.
        int ir = 0;
        //处理有序广播,广播接收器按照设置的priority进行排序
        //省略部分代码
        return ActivityManager.BROADCAST_SUCCESS;
    }
    

    这个方法比较长,对粘性广播、系统广播、有序广播和正常广播分别进行了处理,最终会匹配相应的注册了的广播接收器。最后执行BroadcastQueue的scheduleBroadcastsLocked()方法,也就是发送广播给接收器。

    2.3、BroadcastQueue.scheduleBroadcastsLocked()

    最终会执行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) {
                try {
                //调用ActivityThread.scheduleRegisteredReceiver()方法
                    app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
                            data, extras, ordered, sticky, sendingUser, app.repProcState);
                               } catch (RemoteException ex) {
                    synchronized (mService) {
                        Slog.w(TAG, "Can't deliver broadcast to " + app.processName
                                + " (pid " + app.pid + "). Crashing it.");
                        app.scheduleCrash("can't deliver broadcast");
                    }
                    throw ex;
                }
            } else {
                // Application has died. Receiver doesn't exist.
                throw new RemoteException("app.thread must not be null");
            }
        } else {
            receiver.performReceive(intent, resultCode, data, extras, ordered,
                    sticky, sendingUser);
        }
    }
    

    这里的app.thread其实就是ActivityThread,我们再看ActivityThread. 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就是之前在注册广播时获取的(见1.1)LoadApk.ReceiverDispatcher.InnerReceiver(Binder接口 IPC过程)然后再通过LoadApk.ReceiverDispatcher.performReceive()方法通过handle post出一个Runnable:

    2.4、LoadApk.ReceiverDispatcher.performReceive()

         public void performReceive(Intent intent, int resultCode, String data,
                Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
            final Args args = new Args(intent, resultCode, data, extras, ordered,
                    sticky, sendingUser);
        //省略部分代码 ...
            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);
                }
            }
        }
    

    这里通过ActivityThread.H的handler对象post了一个线程,我们看下这个线程的实现的主要代码:

    public final Runnable getRunnable() {
                return () -> {
                    final BroadcastReceiver receiver = mReceiver;
                    final boolean ordered = mOrdered;
                    //省略部分代码
                        ClassLoader cl = mReceiver.getClass().getClassLoader();
                        intent.setExtrasClassLoader(cl);
                        intent.prepareToEnterProcess();
                        setExtrasClassLoader(cl);
                        receiver.setPendingResult(this);
                        receiver.onReceive(mContext, intent);  
    
                    //省略部分代码
                };
            }
        }
    

    最终是通过ActivityThread.H对象post出一个线程,将 receiver.onReceive(mContext, intent); 方法的调用回到了主线程。

    3、ContextImpl.unregisterReceiver()

    取消接收器的注册也是通过AMS将receiver移除容器

    void removeReceiverLocked(ReceiverList rl) {
        mRegisteredReceivers.remove(rl.receiver.asBinder());
        for (int i = rl.size() - 1; i >= 0; i--) {
            mReceiverResolver.removeFilter(rl.get(i));
        }
    }
    

    三、总结

    简单点来说,注册一个接收器的时候会判断有没有粘性广播想匹配,有的话立即发送广播,同时将接收器放入相应的容器A中;当发送一个广播时,会从A中匹配出相应的接收器,然后在将广播挨个发送给接收器。

    相关文章

      网友评论

        本文标题:你想知道的BroadcastReceiver工作原理

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