1. 程式人生 > >Android8.0.0-r4——廣播的註冊、傳送和接收過程

Android8.0.0-r4——廣播的註冊、傳送和接收過程

原網址:https://blog.csdn.net/itachi85/article/details/71629201

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

這裡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(); }

在註釋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;
            }
          ...  
         }
        ... 
   }

回到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
                }
            }
        }
 ...       
}

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

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

接著來看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();
        }
    }

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

我們來檢視註釋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;
    }

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 ActivityManager.BROADCAST_SUCCESS;
}

這裡省略了很多程式碼,前面的工作主要是將動態註冊的廣播接收者和靜態註冊的廣播接收者按照優先順序高低儲存在不同的列表中,再將這兩個列表合併到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;
}

在註釋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;
       ...
        }
    }
}

在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
                }
           ...
            }
  }

從前面的程式碼我們得知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) {
 ...
        }

}

這裡省去了大部分的程式碼,這些程式碼是用來檢查廣播發送者和廣播接收者的許可權。如果通過了許可權的檢查,則會呼叫註釋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);/
        }
    }

註釋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
    }

註釋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 {
             ...
            }
...
}

在註釋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);
                }
            }
        }

在註釋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) {
           ...
        }
       ...
    }

在註釋1處執行了廣播接收者的onReceive方法,這樣註冊的廣播接收者就收到了廣播並得到了intent。 
廣播的註冊、傳送和接收過程就講到這,最後給出剩餘部分的呼叫時序圖。 
繪圖17.png