第九章-四大組件的工作過程(BroadcastReceiver的工作過程-基於Android8.1源碼)

本章介紹BroadcastReceiver的工作過程,主要包含兩方面的內容,一個是廣播的註冊過程,另一個是廣播的發送和接收過程。

一、BroadcastReceiver的使用
這裏簡單回顧下廣播的使用方法:

   class MyReceiver extends BroadcastReceiver{

        @Override
        public void onReceive(Context context, Intent intent) {
			//onReceive函數不能做耗時的事情,參考值:10s以內
            String action = intent.getAction();
            Log.d(TAG,"on receive action=" + action);
			//do your works
        }
    }

定義好了廣播接收者,接着還需要註冊廣播接收者,註冊分爲兩種方式,AndroidManifest文件中靜態註冊,也可以通過代碼動態註冊。

  • 靜態註冊的示例如下:
	<receiver android:name="com.example.broadcast.MyReceiver">
		<intent-filter>
			<action android:name="com.example.receiver.LANUCH"/>
		</intent-filter>
	</receiver>
  • 代碼註冊示例如下:
    IntentFilter filter = new IntentFilter();
    filter.addAction("com.example.receiver.LANUCH");
    registerReceiver(new MyReceiver(),filter);

需要注意的是,動態註冊的廣播需要在合適的時機進行解註冊,解註冊採用unregisterReceiver方法。

  • send發送廣播
Intent intent = new Intent();
intent.setAction("com.example.receiver.LANUCH");
sendBroadcast(intent);

下面開始分析廣播的工作過程。代碼都是基於Android8.1的,可能部分和書上不太一樣。但差別很小(比如我們代碼獲取AMS是通過getService這個方法)。

二、廣播的註冊過程
在這裏插入圖片描述

//android.content.ContextWrapper#registerReceiver
@Override
public Intent registerReceiver(
	BroadcastReceiver receiver, IntentFilter filter) {
	return mBase.registerReceiver(receiver, filter);
}

ContextImpl的registerReceiver方法調用自己的registerReceiverInternal方法

//android.app.ContextImpl#registerReceiverInternal
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();
	}
}

在這裏插入圖片描述

//android.app.LoadedApk#getReceiverDispatcher
public IIntentReceiver getReceiverDispatcher(BroadcastReceiver r,
		Context context, Handler handler,
		Instrumentation instrumentation, boolean registered) {
	synchronized (mReceivers) {
		LoadedApk.ReceiverDispatcher rd = null;
		ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher> map = null;
		if (registered) {
			map = mReceivers.get(context);
			if (map != null) {
				rd = map.get(r);
			}
		}
		if (rd == null) {
			rd = new ReceiverDispatcher(r, context, handler,
					instrumentation, registered);
			if (registered) {
				if (map == null) {
					//可以看到這個和service很像。也是通過一個ArrayMap保存註冊的廣播
					map = new ArrayMap<BroadcastReceiver, LoadedApk.ReceiverDispatcher>();
					mReceivers.put(context, map);
				}
				map.put(r, rd);
			}
		} else {
			rd.validate(context, handler);
		}
		rd.mForgotten = false;
		return rd.getIIntentReceiver();
	}
}

由於註冊廣播的真正實現過程是在AMS中,因此我們需要看下AMS的具體實現。AMS的registerReceiver方法看起來很長,其實關鍵的就只有下面一部分,最終會把遠程的InnerReceiver對象以及IntentFilter對象存儲起來,這樣整個廣播的註冊過程就完成了

//com.android.server.am.ActivityManagerService#registerReceiver
public Intent registerReceiver(IApplicationThread caller, String callerPackage,
            IIntentReceiver receiver, IntentFilter filter, String permission, int userId,
		int flags) {
	...
	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");
	}
	mReceiverResolver.addFilter(bf);

	...
}

註冊就分析完成了。

三、廣播的發送和接收
在這裏插入圖片描述

	//android.app.ContextImpl#sendBroadcast
	@Override
	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的sendBroadcast
	//com.android.server.am.ActivityManagerService#broadcastIntent
	public final int broadcastIntent(IApplicationThread caller,
			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 = 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, resultExtras,
					requiredPermissions, appOp, bOptions, serialized, sticky,
					callingPid, callingUid, userId);//調用到這個方法
			Binder.restoreCallingIdentity(origId);
			return res;
		}
	}

從上面的代碼看,broadcastIntent調用了broadcastIntentLocked方法,AMS的broadcastIntentLocked方法中有一行:

	// By default broadcasts do not go to stopped apps.
	intent.addFlags(Intent.FLAG_EXCLUDE_STOPPED_PACKAGES);

這裏是引用
在這裏插入圖片描述

在broadcastIntentLocked的內部,會根據intent-filter查找出匹配的廣播接收者並經過一系列的條件過濾,最終會將滿足條件的廣播接收者添加到BroadcastQueue中,接着BroadcastQueue就會將廣播發送給相應的廣播接收者,代碼如下:

	//com.android.server.am.ActivityManagerService#broadcastIntentLocked 代碼片段
	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;
	}
	
	BroadcastQueue的發送實現如下
	//com.android.server.am.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;
    }
	
	BroadcastQueue的scheduleBroadcastsLocked方法並沒有立即發送廣播,而是發送了BROADCAST_INTENT_MSG類型的消息,消息的處理會調用processNextBroadcast方法
	//com.android.server.am.BroadcastQueue#processNextBroadcast 代碼片段
	while (mParallelBroadcasts.size() > 0) {
		r = mParallelBroadcasts.remove(0);
		r.dispatchTime = SystemClock.uptimeMillis();
		r.dispatchClockTime = System.currentTimeMillis();

		if (Trace.isTagEnabled(Trace.TRACE_TAG_ACTIVITY_MANAGER)) {
			Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER,
				createBroadcastTraceTitle(r, BroadcastRecord.DELIVERY_PENDING),
				System.identityHashCode(r));
			Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
				createBroadcastTraceTitle(r, BroadcastRecord.DELIVERY_DELIVERED),
				System.identityHashCode(r));
		}

		final int N = r.receivers.size();
		if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Processing parallel broadcast ["
				+ mQueueName + "] " + r);
		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);
	}

可以看到,無序廣播存儲在mParallelBroadcasts中,系統會遍歷mParallelBroadcasts並將其中的廣播發送給它們所有的接收者,具體的發送過程是通過deliverToRegisteredReceiverLocked方法來實現的。

	//com.android.server.am.BroadcastQueue#deliverToRegisteredReceiverLocked 代碼片段
	performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
                        new Intent(r.intent), r.resultCode, r.resultData,
                        r.resultExtras, r.ordered, r.initialSticky, r.userId);
						
	performReceiveLocked方法實現如下。由於接收廣播會調起應用程序,因此app.thread不爲null,前面分析知道app.thread就是ApplicationThread。	
	//com.android.server.am.BroadcastQueue#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
                // correctly ordered with other one-way calls.
                try {
                    app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
                            data, extras, ordered, sticky, sendingUser, app.repProcState);//看到這裏就調回了ApplicationThread中了
              
                } catch (RemoteException ex) {

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

到這裏調回了ApplicationThread中了,看看ApplicationThread#scheduleRegisteredReceiver方法

	//android.app.ActivityThread.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);
	}
	
	InnerReceiver的performReceive方法又會調用LoadApk.ReceiverDispatcher的performReceive方法,具體實現:
	//android.app.LoadedApk.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) {
			Log.wtf(TAG, "Null intent received");
		} else {
			if (ActivityThread.DEBUG_BROADCAST) {
				int seq = intent.getIntExtra("seq", -1);
				Slog.i(ActivityThread.TAG, "Enqueueing broadcast " + intent.getAction()
						+ " seq=" + seq + " to " + mReceiver);
			}
		}
		if (intent == null || !mActivityThread.post(args.getRunnable())) {//將args的runnable post主線程執行
			if (mRegistered && ordered) {
				IActivityManager mgr = ActivityManager.getService();
				if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
						"Finishing sync broadcast to " + mReceiver);
				args.sendFinished(mgr);
			}
		}
	}

這裏是引用
在這裏插入圖片描述

分析完成,可以發現廣播的註冊,發送,接收和Service的啓動很類似。都是IPC和AMS交互。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章