源碼分析廣播運(yùn)行原理
前言
在 Android 里面有各種各樣的廣播,比如電池的使用狀態(tài),電話的接收和短信的接收都會(huì)產(chǎn)生一個(gè)廣播,應(yīng)用程序開發(fā)者也可以監(jiān)聽這些廣播并做出程序邏輯的處理;
今天我們就來(lái)分析下廣播的運(yùn)行機(jī)制
一、廣播原理機(jī)制詳解
1、靜態(tài)廣播注冊(cè)流程分析
- 靜態(tài)廣播是通過(guò)PackageManagerService在啟動(dòng)的時(shí)候掃描已安裝的應(yīng)用去注冊(cè)的;
- 在PackageManagerService的構(gòu)造方法中,會(huì)去掃描應(yīng)用安裝目錄,順序是先掃描系統(tǒng)應(yīng)用安裝目錄再掃描第三方應(yīng)用安裝目錄;
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");
- }
- 可以看到,它通過(guò)File.list方法列出目錄下的所有后綴為".apk"的文件傳給scanPackageLI去處理;
- 而scanPackageLI(File scanFile,int parseFlags, int scanMode, long currentTime, UserHandle user)內(nèi)部會(huì)調(diào)用它的重載方法;
- 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);
- ...
- }
在這個(gè)scanPackageLIl里面會(huì)解析Package并且將AndroidManifest.xml中注冊(cè)的BroadcastReceiver保存下來(lái):
- ...
- 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");
- ...
- }
- ...
靜態(tài)廣播的流程:
- 系統(tǒng)應(yīng)用的廣播先于第三方應(yīng)用的廣播注冊(cè);
- 而安裝在同一個(gè)目錄下的應(yīng)用的靜態(tài)廣播的注冊(cè)順序是按照File.list列出來(lái)的apk的順序注冊(cè)的;
- 他們的注冊(cè)順序就決定了它們接收廣播的順序;
- 通過(guò)靜態(tài)廣播的注冊(cè)流程,我們已經(jīng)將靜態(tài)廣播注冊(cè)到了PackageManagerService的mReceivers中;
而我們可以使用PackageManagerService.queryIntentReceivers方法查詢intent對(duì)應(yīng)的靜態(tài)廣播
- 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;
- }
- }
2、動(dòng)態(tài)廣播注冊(cè)流程分析
我們調(diào)用Context.registerReceiver最后會(huì)調(diào)到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);
- ...
- }
所以通過(guò)mReceiverResolver.queryIntent就能獲得intent對(duì)應(yīng)的動(dòng)態(tài)廣播了;
3、發(fā)送廣播流程分析
ContextImpl.sendBroadcast中會(huì)調(diào)用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) {
- }
- }
實(shí)際是調(diào)用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;
- }
- }
調(diào)用ActivityManagerService.broadcastIntentLocked,而broadcastIntentLocked中的關(guān)鍵代碼如下:
- broadcastIntentLocked
- // 靜態(tài)廣播
- List receivers = null;
- // 動(dòng)態(tài)廣播
- List<BroadcastFilter> registeredReceivers = null;
- if ((intent.getFlags()&Intent.FLAG_RECEIVER_REGISTERED_ONLY)
- == 0) {
- // 查詢靜態(tài)廣播
- receivers = collectReceiverComponents(intent, resolvedType, users);
- }
- if (intent.getComponent() == null) {
- // 查詢動(dòng)態(tài)廣播
- 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) {
- // 發(fā)送動(dòng)態(tài)廣播
- 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) {
- // 發(fā)送靜態(tài)廣播
- queue.enqueueOrderedBroadcastLocked(r);
- queue.scheduleBroadcastsLocked();
- }
- }
- 動(dòng)態(tài)廣播會(huì)優(yōu)先于靜態(tài)廣播,從上面的代碼我們可以看到;
- 實(shí)際上靜態(tài)廣播靜態(tài)就是從PackageManagerService中查詢的:
- private List<ResolveInfo> collectReceiverComponents(Intent intent, String resolvedType,
- int[] users) {
- ...
- List<ResolveInfo> newReceivers = AppGlobals.getPackageManager()
- .queryIntentReceivers(intent, resolvedType, STOCK_PM_FLAGS, user);
- ...
4、廣播隊(duì)列
- 從ActivityManagerService.broadcastIntentLocked中我們可以看到,實(shí)際上它不是直接將廣播發(fā)送到BroadcastReceiver中;
- 而是將他包裝到BroadcastRecord中,再放進(jìn)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方法用于并發(fā)執(zhí)行廣播的發(fā)送.它很簡(jiǎn)單,就是將BroadcastRecord放到了mParallelBroadcasts中:
- public void enqueueParallelBroadcastLocked(BroadcastRecord r) {
- mParallelBroadcasts.add(r);
- }
scheduleBroadcastsLocked方法同樣很簡(jiǎn)單,就是向mHandler發(fā)送了個(gè)BROADCAST_INTENT_MSG消息:
- public void scheduleBroadcastsLocked() {
- if (mBroadcastsScheduled) {
- return;
- }
- mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
- mBroadcastsScheduled = true;
- }
mHandler在接收到BROADCAST_INTENT_MSG消息的時(shí)候會(huì)做些什么:
- 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方法用于從隊(duì)列中獲取廣播消息并發(fā)送給BroadcastReceiver,它內(nèi)部有兩個(gè)分支,并行處理和串行處理;
5、并行處理
動(dòng)態(tài)注冊(cè)的非有序廣播等就是使用并行處理:
- 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);
- // 發(fā)送消息給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機(jī)制將消息傳遞給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 {
- ......
- //通過(guò)Binder將消息處理傳到應(yīng)用進(jìn)程,應(yīng)用進(jìn)程內(nèi)部再使用Handler機(jī)制,將消息處理放到主線程中
- app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
- data, extras, ordered, sticky, sendingUser, app.repProcState);
- ......
- }
- }
6、串行處理
有序廣播和靜態(tài)廣播等,會(huì)通過(guò)enqueueOrderedBroadcastLocked傳給BroadcastQueue:
- public void enqueueOrderedBroadcastLocked(BroadcastRecord r) {
- mOrderedBroadcasts.add(r);
- }
然后在processNextBroadcast里面會(huì)對(duì)mOrderedBroadcasts進(jìn)行特殊處理;
廣播隊(duì)列傳送廣播給Receiver的原理其實(shí)就是將BroadcastReceiver和消息都放到BroadcastRecord里面,然后通過(guò)Handler機(jī)制遍歷BroadcastQueue里面的BroadcastRecord,將消息發(fā)送給BroadcastReceiver;
二、廣播流程總結(jié)
1、Android廣播分為兩個(gè)方面:廣播發(fā)送者和廣播接收者:
- 廣播作為Android組件間的通信方式,可以使用的場(chǎng)景如下:
- 同一app內(nèi)部的同一組件(Component)內(nèi)的消息通信(單個(gè)或多個(gè)線程之間);
- 同一app內(nèi)部的不同組件之間的消息通信(單個(gè)進(jìn)程);
- 同一app具有多個(gè)進(jìn)程的不同組件之間的消息通信;
- 不同app之間的組件之間消息通信;
- Android系統(tǒng)在特定情況下與App之間的消息通信;
2、實(shí)現(xiàn)原理
從實(shí)現(xiàn)原理上看,Android中的廣播使用了觀察者模式,基于消息的發(fā)布/訂閱事件模型。因此,從實(shí)現(xiàn)的角度來(lái)看,Android中的廣播將廣播的發(fā)送者和接受者極大程度上解耦,使得系統(tǒng)能夠方便集成,更易擴(kuò)展。具體實(shí)現(xiàn)流程要點(diǎn)粗略概括如下:
- 廣播接收者BroadcastReceiver通過(guò)Binder機(jī)制向AMS(Activity Manager Service)進(jìn)行注冊(cè);
- 廣播發(fā)送者通過(guò)binder機(jī)制向AMS發(fā)送廣播;
- AMS查找符合相應(yīng)條件(IntentFilter/Permission等)的BroadcastReceiver,將廣播發(fā)送到BroadcastReceiver相應(yīng)的消息循環(huán)隊(duì)列中;
- 消息循環(huán)隊(duì)列拿到此廣播后,回調(diào)BroadcastReceiver中的onReceive()方法;
- 廣播發(fā)送者和廣播接收者分別屬于觀察者模式中的消息發(fā)布和訂閱兩端,AMS屬于中間的處理中心;
- 廣播發(fā)送者和廣播接收者的執(zhí)行是異步的,發(fā)出去的廣播不會(huì)關(guān)心有無(wú)接收者接收,也不確定接收者到底是何時(shí)才能接收到;
總結(jié)
學(xué)如逆水行舟,不進(jìn)則退。心似平原跑馬,易放難收;
本文轉(zhuǎn)載自微信公眾號(hào)「Android開發(fā)編程」