安卓广播的底层实现原理

相信广播大家都有用过,也知道安卓广播的一些基础知识,如静态广播、动态广播、粘性广播等等,但相信很多人都不知道系统层面是怎样实现这些广播特性的,这篇文章就让我们来聊一聊安卓广播机制的系统实现原理.

静态广播的注册

静态广播是通过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(File scanFile,int parseFlags, int scanMode, long currentTime, UserHandle user)内部会调用它的重载方法scanPackageLI(PackageParser.Package pkg,int parseFlags, int scanMode, long currentTime, UserHandle user):

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

动态广播的注册

我们调用Context.registerReceiver最后会调到ActivityManagerService.registerReceiver:

public Intent registerReceiver(IApplicationThread caller, String callerPackage, IIntentReceiver receiver, IntentFilter filter, String permission, int userId) {
  ...
  ReceiverList rl = (ReceiverList)mRegisteredReceivers.get(receiver.asBinder());
  ...
  BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage, permission, callingUid, userId);
  ...
  mReceiverResolver.addFilter(bf);
  ...
}

所以通过mReceiverResolver.queryIntent就能获得intent对应的动态广播了.

发送广播

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中的关键代码如下:

// 静态广播
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);
    ...

粘性广播的实现原理

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

广播队列

从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);
}

然后在processNextBroadcast里面会对mOrderedBroadcasts进行特殊处理,但是恕我愚钝,这部分代码比较复杂,我现在还没有搞懂它实际的怎么运行的.这块就留下来之后再讲了.

总结

广播队列传送广播给Receiver的原理其实就是将BroadcastReceiver和消息都放到BroadcastRecord里面,然后通过Handler机制遍历BroadcastQueue里面的BroadcastRecord,将消息发送给BroadcastReceiver:

1.png

所以整个广播的机制可以总结成下面这张图:

2.png

推荐阅读更多精彩内容