美文网首页
【Android组件内核面试题】广播的分类与工作原理

【Android组件内核面试题】广播的分类与工作原理

作者: 小城哇哇 | 来源:发表于2023-10-09 18:22 被阅读0次

    广播的分类与工作原理

    这道题想考察什么?

    1. 是否了解广播相关的知识?

    考察的知识点

    1. 广播的基本知识
    2. 广播的原理

    考生应该如何回答

    1. 广播的分类以及使用在《有序广播》中有讲解,这里不再重复。其中讲解内容包含AMS和PMS,如果不了解的同学可以先阅读下对应的章节。
    1. 静态广播注册的原理

      静态广播是通过PackageManagerService在启动的时候扫描已安装的应用去注册的。在PackageManagerService的构造方法中,会去扫描应用安装目录,会先扫描系统应用安装目录再扫描第三方应用安装目录。PackageManagerService.scanDirLI就是用于扫描目录的方法,由于代码比较少,这里我们直接把它贴了上来。

       private void scanDirLI(File dir, int flags, int scanMode, long currentTime) {
          String[] files = dir.list();
          if (files == null) {
              return;
          }
    
          int i;
          for (i=0; i<files.length; i++) {
              File file = new File(dir, files[i]);
              if (!isPackageFilename(files[i])) {
                  continue;
              }
              PackageParser.Package pkg = scanPackageLI(file,
                      flags|PackageParser.PARSE_MUST_BE_APK, scanMode, currentTime, null);
              if (pkg == null && (flags & PackageParser.PARSE_IS_SYSTEM) == 0 &&
                      mLastScanError == PackageManager.INSTALL_FAILED_INVALID_APK) {
                  file.delete();
              }
          }
       }
    
       private static final boolean isPackageFilename(String name) {
          return name != null && name.endsWith(".apk");
       }
    
        可以看到,它通过File.list方法列出目录下的所有后缀为”.apk”的文件传给scanPackageLI去处理。
    

    而scanPackageLI()内部会执行它的重载方法scanPackageLI():

    private PackageParser.Package scanPackageLI(File scanFile,int parseFlags, int scanMode, long currentTime, UserHandle user) {
      ...
      final PackageParser.Package pkg = pp.parsePackage(scanFile,scanPath, mMetrics, parseFlags);
      ...
      PackageParser.Package scannedPkg = scanPackageLI(pkg, parseFlags, scanMode | SCAN_UPDATE_SIGNATURE, currentTime, user);
      ...
    }
    
        在这个scanPackageLIl里面会解析Package并且将AndroidManifest.xml中声明的BroadcastReceiver保存下来:
    
    ...
    N = pkg.receivers.size();
    r = null;
    for (i=0; i<N; i++) {
       PackageParser.Activity a = pkg.receivers.get(i);
       a.info.processName = fixProcessName(pkg.applicationInfo.processName,
               a.info.processName, pkg.applicationInfo.uid);
       mReceivers.addActivity(a, "receiver");
       ...
    }
    ...
    
        所以从上面得到静态广播的流程可以看出来:系统应用的广播先于第三方应用的广播注册,而安装在同一个目录下的应用的静态广播的注册顺序是按照File.list列出来的apk的顺序注册的。他们的注册顺序就是它们接收广播的顺序。
    
        通过静态广播的注册流程,我们已经将静态广播注册到了PackageManagerService的mReceivers中,而我们可以使用PackageManagerService.queryIntentReceivers方法查询intent对应的静态广播
    
    public List<ResolveInfo> queryIntentReceivers(Intent intent, String resolvedType, int flags, int userId) {
       if (!sUserManager.exists(userId)) return Collections.emptyList();
       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;
       }
    
       synchronized (mPackages) {
           String pkgName = intent.getPackage();
           if (pkgName == null) {
               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 null;
       }
    }
    
    1. 动态广播注册的原理

      Android四大组件中的BroadcastReceiver,在我们实际工作中使用十分频繁,我们可以利用系统的开机广播,网络状态改变的广播等等实现我们的业务逻辑,也可以通过广播实现跨进程间的通信(实际上是利用binder机制)。那么广播的原理是什么?肯定值得我们深入学习一下。广播,简单来说分为两种,动态注册、静态注册,本文将着重讲解动态注册。

    我们从一段代码入手:

     @Override
        protected void onStart() {
            super.onStart();
            IntentFilter filter = new IntentFilter();
            filter.addAction("come.letv.gaomh");
            gaomhReceiver = new GaomhReceiver();
            this.registerReceiver(gaomhReceiver,filter);
        }
    
        public class GaomhReceiver extends BroadcastReceiver {
        
            @Override
            public void onReceive(Context context, Intent intent) {
        
            }
        }
        
        @Override
        protected void onDestroy() {
            super.onDestroy();
            unregisterReceiver(gaomhReceiver);
        }
    

    注意,为了防止出现内存泄露问题,在onDestroy()中需要执行注销。我们由registerReceiver方法一直往下走。

    frameworks/base/core/java/android/content/ContextWrapper.java

    @Override
        public Intent registerReceiver(
            BroadcastReceiver receiver, IntentFilter filter) {
            return mBase.registerReceiver(receiver, filter);
        }
    

    frameworks/base/core/java/android/app/ContextImpl.java

     @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) {
                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 {
                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();
            }
        }
    

    我们很清楚的看到啊activityManager.getService().registerReceiver(),这个方法马上就要执行到ActivityManagerService(之后一律写成AMS)了,此时我们就注册成功了。

    1. 发送广播
      ContextImpl.sendBroadcast中会执行ActivityManagerNative.getDefault().broadcastIntent()
    public void sendBroadcast(Intent intent) {
        warnIfCallingFromSystemProcess();
        String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());
        try {
            intent.prepareToLeaveProcess();
            ActivityManagerNative.getDefault().broadcastIntent(
                mMainThread.getApplicationThread(), intent, resolvedType, null,
                Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, false, false,getUserId());
        } catch (RemoteException e) {
        }
    }
    

    实际是执行ActivityManagerService.broadcastIntent:

    public final int broadcastIntent(IApplicationThread caller,
            Intent intent, String resolvedType, IIntentReceiver resultTo,
            int resultCode, String resultData, Bundle map,
            String requiredPermission, int appOp, boolean serialized, boolean sticky, int userId) {
        enforceNotIsolatedCaller("broadcastIntent");
        synchronized(this) {
            intent = verifyBroadcastLocked(intent);
            final ProcessRecord callerApp = getRecordForAppLocked(caller);
            final int callingPid = Binder.getCallingPid();
            final int callingUid = Binder.getCallingUid();
            final long origId = Binder.clearCallingIdentity();
            int res = broadcastIntentLocked(callerApp,
                    callerApp != null ? callerApp.info.packageName : null,
                    intent, resolvedType, resultTo,
                    resultCode, resultData, map, requiredPermission, appOp, serialized, sticky,
                    callingPid, callingUid, userId);
            Binder.restoreCallingIdentity(origId);
            return res;
        }
    }
    

    ActivityManagerService.broadcastIntent中又会执行ActivityManagerService.broadcastIntentLocked,而broadcastIntentLocked中的关键代码如下:

    ActivityManagerService.broadcastIntent中又会调用ActivityManagerService.broadcastIntentLocked,而broadcastIntentLocked中的关键代码如下:// 静态广播
    List receivers = null;
    // 动态广播
    List<BroadcastFilter> registeredReceivers = null;
    if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY)
          == 0) {
       // 查询静态广播
       receivers = collectReceiverComponents(intent, resolvedType, users);
    }
    if (intent.getComponent() == null) {
       // 查询动态广播
       registeredReceivers = mReceiverResolver.queryIntent(intent,
               resolvedType, false, userId);
    }
    
    final boolean replacePending =
           (intent.getFlags()&Intent.FLAG_RECEIVER_REPLACE_PENDING) != 0;
    
    int NR = registeredReceivers != null ? registeredReceivers.size() : 0;
    if (!ordered && NR > 0) {
       final BroadcastQueue queue = broadcastQueueForIntent(intent);
       BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
               callerPackage, callingPid, callingUid, resolvedType, requiredPermission,
               appOp, registeredReceivers, resultTo, resultCode, resultData, map,
               ordered, sticky, false, userId);
       final boolean replaced = replacePending && queue.replaceParallelBroadcastLocked(r);
       if (!replaced) {
           // 发送动态广播
           queue.enqueueParallelBroadcastLocked(r);
           queue.scheduleBroadcastsLocked();
       }
       registeredReceivers = null;
       NR = 0;
    }
    ...
    if ((receivers != null && receivers.size() > 0)
        || resultTo != null) {
       BroadcastQueue queue = broadcastQueueForIntent(intent);
       BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
               callerPackage, callingPid, callingUid, resolvedType,
               requiredPermission, appOp, receivers, resultTo, resultCode,
               resultData, map, ordered, sticky, false, userId);
       boolean replaced = replacePending && queue.replaceOrderedBroadcastLocked(r);
       if (!replaced) {
           // 发送静态广播
           queue.enqueueOrderedBroadcastLocked(r);
           queue.scheduleBroadcastsLocked();
       }
    }
    

    大家应该知道动态广播会优先于静态广播,从上面的代码我们可以看到,这实际是因为安卓的源代码就是按这个顺序写的…

    最后我们来看一下ActivityManagerService.collectReceiverComponents方法,实际上静态广播静态就是从PackageManagerService中查询的:

    private List<ResolveInfo> collectReceiverComponents(Intent intent, String resolvedType,
                int[] users) {
        ...
        List<ResolveInfo> newReceivers = AppGlobals.getPackageManager()
                            .queryIntentReceivers(intent, resolvedType, STOCK_PM_FLAGS, user);
        ...
    
    1. 粘性广播
      ActivityManagerService.broadcastIntentLocked有下面这样一段代码,它将粘性广播保存到mStickyBroadcasts中。
    if (sticky) {
        ...
        ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(userId);
        if (stickies == null) {
            stickies = new ArrayMap<String, ArrayList<Intent>>();
            mStickyBroadcasts.put(userId, stickies);
        }
        ArrayList<Intent> list = stickies.get(intent.getAction());
        if (list == null) {
            list = new ArrayList<Intent>();
            stickies.put(intent.getAction(), list);
        }
        int N = list.size();
        int i;
        for (i=0; i<N; i++) {
            if (intent.filterEquals(list.get(i))) {
                // This sticky already exists, replace it.
                list.set(i, new Intent(intent));
                break;
            }
        }
        if (i >= N) {
            list.add(new Intent(intent));
        }
    }
    

    而ManagerService.registerReceiver会获取之前发送的粘性广播,再次发送给刚刚注册的receiver:

    List allSticky = null;
    
    // 获取符合的粘性广播
    Iterator actions = filter.actionsIterator();
    if (actions != null) {
        while (actions.hasNext()) {
            String action = (String)actions.next();
            allSticky = getStickiesLocked(action, filter, allSticky,
                    UserHandle.USER_ALL);
            allSticky = getStickiesLocked(action, filter, allSticky,
                    UserHandle.getUserId(callingUid));
        }
    } else {
        allSticky = getStickiesLocked(null, filter, allSticky,
                UserHandle.USER_ALL);
        allSticky = getStickiesLocked(null, filter, allSticky,
                UserHandle.getUserId(callingUid));
    }
    

    //向新注册的receiver发送粘性广播

    if (allSticky != null) {
        ArrayList receivers = new ArrayList();
        receivers.add(bf);
    
        int N = allSticky.size();
        for (int i=0; i<N; i++) {
            Intent intent = (Intent)allSticky.get(i);
            BroadcastQueue queue = broadcastQueueForIntent(intent);
            BroadcastRecord r = new BroadcastRecord(queue, intent, null,
                    null, -1, -1, null, null, AppOpsManager.OP_NONE, receivers, null, 0,
                    null, null, false, true, true, -1);
            queue.enqueueParallelBroadcastLocked(r);
            queue.scheduleBroadcastsLocked();
        }
    }
    

    getStickiesLocked即从mStickyBroadcasts中查询之前发送过的粘性广播

    private final List getStickiesLocked(String action, IntentFilter filter,
            List cur, int userId) {
        final ContentResolver resolver = mContext.getContentResolver();
        ArrayMap<String, ArrayList<Intent>> stickies = mStickyBroadcasts.get(userId);
        if (stickies == null) {
            return cur;
        }
        final ArrayList<Intent> list = stickies.get(action);
        if (list == null) {
            return cur;
        }
        int N = list.size();
        for (int i=0; i<N; i++) {
            Intent intent = list.get(i);
            if (filter.match(resolver, intent, true, TAG) >= 0) {
                if (cur == null) {
                    cur = new ArrayList<Intent>();
                }
                cur.add(intent);
            }
        }
        return cur;
    }
    
    1. 广播队列
      从ActivityManagerService.broadcastIntentLocked中我们可以得出结论,实际上它不是直接将广播发送到BroadcastReceiver中的。而是将他包装到BroadcastRecord中,再放进BroadcastQueue:
    BroadcastQueue queue = broadcastQueueForIntent(intent);
    BroadcastRecord r = new BroadcastRecord(queue, intent, null,
            null, -1, -1, null, null, AppOpsManager.OP_NONE, receivers, null, 0,
            null, null, false, true, true, -1);
    queue.enqueueParallelBroadcastLocked(r);
    queue.scheduleBroadcastsLocked();
    

    enqueueParallelBroadcastLocked方法用于并发执行广播的发送。它很简单,就是将BroadcastRecord放到了mParallelBroadcasts中:

    public void enqueueParallelBroadcastLocked(BroadcastRecord r) {
        mParallelBroadcasts.add(r);
    }
    

    scheduleBroadcastsLocked方法同样很简单,就是向mHandler发送了个BROADCAST_INTENT_MSG消息:

    public void scheduleBroadcastsLocked() {
       if (mBroadcastsScheduled) {
           return;
       }
       mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
       mBroadcastsScheduled = true;
    }
    

    这个时候我们就需要再去看看mHandler在接收到BROADCAST_INTENT_MSG消息的时候会做些什么:

    final Handler mHandler = new Handler() {
       public void handleMessage(Message msg) {
           switch (msg.what) {
               case BROADCAST_INTENT_MSG: {
                   processNextBroadcast(true);
               } break;
               case BROADCAST_TIMEOUT_MSG: {
                   synchronized (mService) {
                       broadcastTimeoutLocked(true);
                   }
               } break;
           }
       }
    };
    

    processNextBroadcast方法用于从队列中得到广播消息并发送给BroadcastReceiver,它内部有两个分支,并行处理和串行处理。

    final void processNextBroadcast(boolean fromMsg) {
      synchronized(mService) {
          BroadcastRecord r;
          mService.updateCpuStats();
          if (fromMsg) {
             mBroadcastsScheduled = false;
          }
          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);
                 // 发送消息给Receiver
                 deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false);
             }
             addBroadcastToHistoryLocked(r);
          }
          ...
       }
       ...
    }
    
    private final void deliverToRegisteredReceiverLocked(BroadcastRecord r,
                BroadcastFilter filter, boolean ordered) {
        ...
        // 获取BroadcastReceiver的Binder
        r.receiver = filter.receiverList.receiver.asBinder();
        ...
        // 使用Binder机制将消息传递给BroadcastReceiver
        performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
                        new Intent(r.intent), r.resultCode, r.resultData,
                        r.resultExtras, r.ordered, r.initialSticky, r.userId);
        ...
    }
    
    void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,
            Intent intent, int resultCode, String data, Bundle extras,
            boolean ordered, boolean sticky, int sendingUser) throws RemoteException {
        ......
            //通过Binder将消息处理传到应用进程,应用进程内部再使用Handler机制,将消息处理放到主线程中
            app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
                            data, extras, ordered, sticky, sendingUser, app.repProcState);
        ......
        }
    }
    

    有序广播串行处理,会通过enqueueOrderedBroadcastLocked传给BroadcastQueue:

    public void enqueueOrderedBroadcastLocked(BroadcastRecord r) {
       mOrderedBroadcasts.add(r);
    }
    

    总结

    • 从实现原理看上,Android中的广播使用了观察者模式,使用消息的发布/订阅事件模型。因此,从实现的角度来看,Android中的广播将广播的发送者和接受者实现了极大程度上解耦,使得系统能够方便集成,更易扩展。具体实现流程要点粗略概括如下:

      1.广播接收者BroadcastReceiver通过Binder机制向AMS(Activity Manager Service)进行注册;

      2.广播发送者通过binder机制向AMS发送广播;

      3.AMS查找符合相应条件(IntentFilter/Permission等)的BroadcastReceiver,将广播发送到BroadcastReceiver(一般情况下是Activity)相应的消息循环队列中;

      4.消息循环执行拿到此广播,回调BroadcastReceiver中的onReceive()方法。
      对于不同的广播类型,以及不同的BroadcastReceiver注册方式,具体实现上会有不同。

    • 广播发送者和广播接收者分别属于观察者模式中的消息发布和订阅两端,AMS属于中间的处理中心。广播发送者和广播接收者的执行是异步的,发出去的广播不会关心有无接收者接收,也不确定接收者到底是何时才能接收到。


    最后

    有需要面试题的朋友可以关注一下哇哇,以上都可以分享!!!

    相关文章

      网友评论

          本文标题:【Android组件内核面试题】广播的分类与工作原理

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