前言 我們接著來學習Android四大組件中的BroadcastReceiver,廣播主要就是分為注冊、接收和發送過程。建議閱讀此文前請先閱讀Android深入理解四大組件系列的文章,知識重復的部分,本文不再贅述。
1.廣播的注冊過程 BroadcastReceiver的注冊分為兩種,分別是靜態注冊和動態注冊,靜態注冊在應用安裝時由PackageManagerService來完成注冊過程,關于這一過程,我會在后續的介紹PackageManagerService文章中詳細介紹。這里只介紹BroadcastReceiver的動態注冊。 要想動態注冊BroadcastReceiver,需要調用registerReceiver方法,它的實現在ContextWrapper中,代碼如下所示。
frameworks/base/core/java/android/content/ContextWrapper.java
@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) { return mBase.registerReceiver(receiver, filter);
} View Code 這里mBase具體指向就是ContextImpl,不明白的請查看Android深入四大組件(二)Service的啟動過程這篇文章。ContextImpl的registerReceiver方法有很多重載的方法最終會調用registerReceiverInternal方法:frameworks/base/core/java/android/app/ContextImpl.java
private Intent registerReceiverInternal(BroadcastReceiver receiver,
int userId,IntentFilter filter, String broadcastPermission,Handler scheduler, Context context) {IIntentReceiver rd =
null ; if (receiver !=
null ) { if (mPackageInfo !=
null && context !=
null ) {
// 1 if (scheduler ==
null ) {scheduler =
mMainThread.getHandler();}rd =
mPackageInfo.getReceiverDispatcher(receiver, context, scheduler,mMainThread.getInstrumentation(), true );
// 2 }
else { if (scheduler ==
null ) {scheduler =
mMainThread.getHandler();}rd =
new LoadedApk.ReceiverDispatcher(receiver, context, scheduler, null ,
true ).getIIntentReceiver();
// 3
}} try {final Intent intent =
ActivityManagerNative.getDefault().registerReceiver(mMainThread.getApplicationThread(), mBasePackageName,rd, filter, broadcastPermission, userId); // 4 if (intent !=
null ) {intent.setExtrasClassLoader(getClassLoader());intent.prepareToEnterProcess();} return intent;} catch (RemoteException e) { throw e.rethrowFromSystemServer();} View Code 在注釋1處判斷如果LoadedApk類型的mPackageInfo不等于null并且context不等null就調用注釋2處的代碼通過mPackageInfo的getReceiverDispatcher方法獲取rd對象,否則就調用注釋3處的代碼來創建rd對象。注釋2和3的代碼的目的都是要獲取IIntentReceiver類型的rd對象,IIntentReceiver是一個Binder接口,用于進行跨進程的通信,它的具體實現在 LoadedApk.ReceiverDispatcher.InnerReceiver,如下所示。
frameworks/base/core/java/android/app/LoadedApk.java
static final
class ReceiverDispatcher {final static class InnerReceiver extends IIntentReceiver.Stub {final WeakReference <LoadedApk.ReceiverDispatcher>
mDispatcher;final LoadedApk.ReceiverDispatcher mStrongRef;InnerReceiver(LoadedApk.ReceiverDispatcher rd, boolean strong) {mDispatcher =
new WeakReference<LoadedApk.ReceiverDispatcher>
(rd);mStrongRef = strong ? rd :
null ;}... }... } View Code 回到registerReceiverInternal方法,在注釋4處調用了ActivityManagerProxy(AMP)的registerReceiver方法,最終會調用AMS的registerReceiver方法,并將rd傳就去。不明白的同學請查看Android深入四大組件(一)應用程序啟動過程(前篇),這里不再贅述。 查看AMS的registerReceiver方法,如下所示。frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
public Intent registerReceiver(IApplicationThread caller, String callerPackage,IIntentReceiver receiver, IntentFilter filter, String permission, int userId) {
...synchronized( this ) {...Iterator <String> actions = filter.actionsIterator();
// 1
... // Collect stickies of users int [] userIds =
{ UserHandle.USER_ALL, UserHandle.getUserId(callingUid) }; while (actions.hasNext()) {String action =
actions.next(); for (
int id : userIds) {ArrayMap <String, ArrayList<Intent>> stickies = mStickyBroadcasts.
get (id); if (stickies !=
null ) {ArrayList <Intent> intents = stickies.
get (action); if (intents !=
null ) { if (stickyIntents ==
null ) {stickyIntents =
new ArrayList<Intent>
();}stickyIntents.addAll(intents); // 2
}}}}}ArrayList <Intent> allSticky =
null ; if (stickyIntents !=
null ) {final ContentResolver resolver =
mContext.getContentResolver(); for (
int i =
0 , N = stickyIntents.size(); i < N; i++
) {Intent intent = stickyIntents.
get (i); if (filter.match(resolver, intent,
true , TAG) >=
0 ) { if (allSticky ==
null ) {allSticky =
new ArrayList<Intent>
();}allSticky.add(intent); // 3
}}}...
} View Code 注釋1處根據傳入的IntentFilter類型的filter的得到actions列表,根據actions列表和userIds(userIds可以理解為應用程序的uid)得到所有的粘性廣播的intent,并在注釋2處傳入到stickyIntents中,在注釋3處將這些粘性廣播的intent存入到allSticky列表中,從這里可以看出粘性廣播是存儲在AMS中的。 接著查看AMS的registerReceiver方法的剩余內容:frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
public Intent registerReceiver(IApplicationThread caller, String callerPackage,IIntentReceiver receiver, IntentFilter filter, String permission, int userId) {
...synchronized ( this ) {...ReceiverList rl = mRegisteredReceivers.
get (receiver.asBinder());
// 1 if (rl ==
null ) {rl =
new ReceiverList(
this , callerApp, callingPid, callingUid,userId, receiver); // 2 if (rl.app !=
null ) {rl.app.receivers.add(rl);} ...}...BroadcastFilter bf =
new BroadcastFilter(filter, rl, callerPackage,permission, callingUid, userId); // 3 rl.add(bf);
// 4 if (!
bf.debugCheck()) {Slog.w(TAG, " ==> For Dynamic broadcast " );}mReceiverResolver.addFilter(bf); // 5
... return sticky;}
} View Code 注釋1處獲取ReceiverList列表,如果為空則在注釋2處創建,ReceiverList繼承自ArrayList,用來存儲廣播接收者。在注釋3處創建BroadcastFilter并傳入此前創建的ReceiverList,BroadcastFilter用來描述注冊的廣播接收者,并在注釋4通過add方法將自身添加到ReceiverList中。注釋5處將BroadcastFilter添加到mReceiverResolver中,這樣當AMS接收到廣播時就可以從mReceiverResolver中找到對應的廣播接收者了。下面給出廣播的注冊過程的時序圖。
繪圖1_副本.png
2.廣播的發送和接收過程 ContextImpl到AMS的調用過程 廣播可以發送多種類型,包括無序廣播(普通廣播)、有序廣播和粘性廣播,這里以無序廣播為例,來講解廣播的發送過程。 要發送無序廣播需要調用sendBroadcast方法,它的實現同樣在ContextWrapper中:frameworks/base/core/java/android/content/ContextWrapper.java
@Override public void sendBroadcast(Intent intent) {mBase.sendBroadcast(intent);} View Code 接著來看ContextImpl中的sendBroadcast方法,如下所示。frameworks/base/core/java/android/app/ContextImpl.java
@Override public void sendBroadcast(Intent intent) {warnIfCallingFromSystemProcess();String resolvedType =
intent.resolveTypeIfNeeded(getContentResolver()); try {intent.prepareToLeaveProcess( this );ActivityManagerNative.getDefault().broadcastIntent(mMainThread.getApplicationThread(), intent, resolvedType, null ,Activity.RESULT_OK, null ,
null ,
null , AppOpsManager.OP_NONE,
null ,
false ,
false ,getUserId()); // 1 }
catch (RemoteException e) { throw e.rethrowFromSystemServer();}} View Code 注釋1處又是熟悉的代碼,最終會調用AMS的broadcastIntent方法:frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
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);
// 1
... /* ** 2 */ 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;}} View Code 我們來查看注釋1處的verifyBroadcastLocked方法:frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
final Intent verifyBroadcastLocked(Intent intent) { // Refuse possible leaked file descriptors if (intent !=
null && intent.hasFileDescriptors() ==
true ) {
// 1 throw new IllegalArgumentException(
" File descriptors passed in Intent " );} int flags = intent.getFlags();
// 2 if (!
mProcessesReady) { if ((flags&Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT) !=
0 ) {
// 3 }
else if ((flags&Intent.FLAG_RECEIVER_REGISTERED_ONLY) ==
0 ) {
// 4 Slog.e(TAG,
" Attempt to launch receivers of broadcast intent " +
intent +
" before boot completion " ); throw new IllegalStateException(
" Cannot broadcast before boot completed " );}} if ((flags&Intent.FLAG_RECEIVER_BOOT_UPGRADE) !=
0 ) { throw new IllegalArgumentException( " Can't use FLAG_RECEIVER_BOOT_UPGRADE here " );} return intent;} View Code verifyBroadcastLocked方法主要是驗證廣播是否合法,在注釋1處驗證intent是否不為null并且有文件描述符。注釋2處獲得intent中的flag。注釋3處如果系統正在啟動過程中,判斷如果flag設置為FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT(啟動檢查時只接受動態注冊的廣播接收者)則不做處理,如果不是則在注釋4處判斷如果flag沒有設置為FLAG_RECEIVER_REGISTERED_ONLY(只接受動態注冊的廣播接收者)則會拋出異常。 我們再回到broadcastIntent方法,在注釋2處調用了broadcastIntentLocked方法,代碼如下所示。frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
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) {... if ((receivers !=
null && receivers.size() >
0 ) || resultTo !=
null ) {BroadcastQueue queue =
broadcastQueueForIntent(intent); /* ** 1 */ BroadcastRecord r =
new BroadcastRecord(queue, intent, callerApp,callerPackage, callingPid, callingUid, resolvedType,requiredPermissions, appOp, brOptions, receivers, resultTo, resultCode,resultData, resultExtras, ordered, sticky, false , userId);... boolean replaced = replacePending &&
queue.replaceOrderedBroadcastLocked(r); if (!
replaced) {queue.enqueueOrderedBroadcastLocked(r);queue.scheduleBroadcastsLocked(); // 2
}} ...} return Act
View Code 這里省略了很多代碼,前面的工作主要是將動態注冊的廣播接收者和靜態注冊的廣播接收者按照優先級高低存儲在不同的列表中,再將這兩個列表合并到receivers列表中,這樣receivers列表包含了所有的廣播接收者(無序廣播和有序廣播)。在注釋1處創建BroadcastRecord對象并將receivers傳進去,在注釋2處調用BroadcastQueue的scheduleBroadcastsLocked方法。 這里先給出ContextImpl到AMS的調用過程的時序圖。
繪圖8_副本.png
AMS到BroadcastReceiver的調用過程 BroadcastQueue的scheduleBroadcastsLocked方法的代碼如下所示。frameworks/base/services/core/java/com/android/server/am/BroadcastQueue.java
public void scheduleBroadcastsLocked() {
...mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this ));
// 1 mBroadcastsScheduled =
true ;
} View Code 在注釋1處向BroadcastHandler類型的mHandler對象發送了BROADCAST_INTENT_MSG類型的消息,這個消息在BroadcastHandler的handleMessage方法中進行處理,如下所示。frameworks/base/services/core/java/com/android/server/am/BroadcastQueue.java
private final
class BroadcastHandler extends Handler { public BroadcastHandler(Looper looper) {super(looper, null ,
true );}@Override public void handleMessage(Message msg) { switch (msg.what) { case BROADCAST_INTENT_MSG: { if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, " Received BROADCAST_INTENT_MSG " );processNextBroadcast( true );} break ;...}}
} View Code 在handleMessage方法中調用了processNextBroadcast方法,processNextBroadcast方法對無序廣播和有序廣播分別進行處理,旨在將廣播發送給廣播接收者,下面給出processNextBroadcast方法中對無序廣播的處理部分。frameworks/base/services/core/java/com/android/server/am/BroadcastQueue.java
final
void processNextBroadcast(boolean fromMsg) {
... if (fromMsg) {mBroadcastsScheduled =
false ;
// 1
} // First, deliver any non-serialized broadcasts right away. while (mParallelBroadcasts.size() >
0 ) {
// 2 r = mParallelBroadcasts.remove(
0 );
// 3
... 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);
// 4
}...}
} View Code 從前面的代碼我們得知fromMsg的值為true,因此注釋1處會將mBroadcastsScheduled 設置為flase,表示對于此前發來的BROADCAST_INTENT_MSG類型的消息已經處理了。注釋2處的mParallelBroadcasts列表用來存儲無序廣播,通過while循環將mParallelBroadcasts列表中的無序廣播發送給對應的廣播接收者。在注釋3處獲取每一個mParallelBroadcasts列表中存儲的BroadcastRecord類型的r對象。注釋4處將這些r對象描述的廣播發送給對應的廣播接收者,deliverToRegisteredReceiverLocked方法如下所示。frameworks/base/services/core/java/com/android/server/am/BroadcastQueue.java
private void deliverToRegisteredReceiverLocked(BroadcastRecord r,BroadcastFilter filter, boolean ordered, int index) {
... try { if (DEBUG_BROADCAST_LIGHT) Slog.i(TAG_BROADCAST, " Delivering to " + filter +
" : " +
r); if (filter.receiverList.app !=
null &&
filter.receiverList.app.inFullBackup) {... } else {performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver, new Intent(r.intent), r.resultCode, r.resultData,r.resultExtras, r.ordered, r.initialSticky, r.userId); // 1
} if (ordered) {r.state =
BroadcastRecord.CALL_DONE_RECEIVE;}} catch (RemoteException e) {...}} View Code 這里省去了大部分的代碼,這些代碼是用來檢查廣播發送者和廣播接收者的權限。如果通過了權限的檢查,則會調用注釋1處的performReceiveLocked方法:frameworks/base/services/core/java/com/android/server/am/BroadcastQueue.java
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 ) {
// 1 if (app.thread !=
null ) {
// 2 // 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); // 3
} } ...} else {receiver.performReceive(intent, resultCode, data, extras, ordered,sticky, sendingUser); /
}} View Code 注釋1和2處的代碼表示如果廣播接收者所在的應用程序進程存在并且正在運行,則執行注釋3處的代碼,表示用廣播接收者所在的應用程序進程來接收廣播,這里app.thread指的是ApplicationThread,我們來查看ApplicationThread的scheduleRegisteredReceiver方法,代碼如下所示。frameworks/base/core/java/android/app/ActivityThread.java
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); // 1 }
View Code 注釋1處調用了IIntentReceiver類型的對象receiver的performReceive方法,這里實現receiver的類為LoadedApk.ReceiverDispatcher.InnerReceiver,代碼如下所示。frameworks/base/core/java/android/app/LoadedApk.java
static final
class ReceiverDispatcher {final static class InnerReceiver extends IIntentReceiver.Stub {...InnerReceiver(LoadedApk.ReceiverDispatcher rd, boolean strong) {mDispatcher =
new WeakReference<LoadedApk.ReceiverDispatcher>
(rd);mStrongRef = strong ? rd :
null ;}@Override public void performReceive(Intent intent,
int resultCode, String data,Bundle extras, boolean ordered, boolean sticky, int sendingUser) {final LoadedApk.ReceiverDispatcher rd;... if (rd !=
null ) {rd.performReceive(intent, resultCode, data, extras,ordered, sticky, sendingUser); // 1 }
else {...}
...
} View Code 在注釋1處調用了ReceiverDispatcher類型的rd對象的performReceive方法:frameworks/base/core/java/android/app/LoadedApk.java
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); // 1
... if (intent ==
null || !mActivityThread.post(args)) {
// 2 if (mRegistered &&
ordered) {IActivityManager mgr =
ActivityManagerNative.getDefault(); if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG, " Finishing sync broadcast to " +
mReceiver);args.sendFinished(mgr);}} View Code 在注釋1處將廣播的intent等信息封裝為Args對象,并在注釋2處調用mActivityThread的post方法并傳入了Args對象。這個mActivityThread是一個Handler對象,具體指向的就是H,注釋2處的代碼就是將Args對象通過H發送到主線程的消息隊列中。Args繼承自Runnable,這個消息最終會在Args的run方法執行,Args的run方法如下所示。
frameworks/base/core/java/android/app/LoadedApk.java
public void run() {... try {ClassLoader cl =
mReceiver.getClass().getClassLoader();intent.setExtrasClassLoader(cl);intent.prepareToEnterProcess();setExtrasClassLoader(cl);receiver.setPendingResult( this );receiver.onReceive(mContext, intent); // 1 }
catch (Exception e) {...}...} View Code 在注釋1處執行了廣播接收者的onReceive方法,這樣注冊的廣播接收者就收到了廣播并得到了intent。 廣播的注冊、發送和接收過程就講到這,最后給出剩余部分的調用時序圖。
?
轉載于:https://www.cnblogs.com/ganchuanpu/p/8597397.html
總結
以上是生活随笔 為你收集整理的Android深入四大组件(八)广播的注册、发送和接收过程 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。