Android 8.0 原始碼分析 (六) BroadcastReceiver 啟動

DevYK發表於2019-11-02

前言

我們熟知一般 Android 工程師都是在應用層上開發,不會涉及系統原始碼,但是如果你想往底層發展,或者深入外掛化、Framework 系統層等開發工作,如果不瞭解 Android 原始碼可是不行的,那麼接下來我基於自己的理解跟學習來記錄跟 Android 開發息息相關的原始碼分析,大概從 Android 中的 SystemServer 啟動、四大元件啟動、AMS、PMS 等幾個維度來介紹,下面是我的計劃,當然在未來也有可能改變。

還沒有關注的小夥伴,可以先關注一波,系列文章會持續更新。

Android 8.0 原始碼分析 (一) SystemServer 程式啟動

Android 8.0 原始碼分析 (二) Launcher 啟動

Android 8.0 原始碼分析 (三) 應用程式程式建立到應用程式啟動的過程

Android 8.0 原始碼分析 (四) Activity 啟動

Android 8.0 原始碼分析 (五) Service 啟動

Android 8.0 原始碼分析 (六) BroadcastReceiver 啟動

Android 8.0 原始碼分析 (七) ContentProvider 啟動

Android 8.0 原始碼分析 (八) ActivityManagerService

Android 8.0 原始碼分析 (九) WindowManager

Android 8.0 原始碼分析 (十) WindowManagerService 的視窗管理

介紹

廣播作為四大元件之一,使用頻率雖然沒有 Activity 高,但是使用場景也還是比較廣泛,比如監聽開機、息屏亮屏、元件或者程式間傳送訊息等等。接下來將為大家介紹廣播的工作過程,分別從註冊、傳送、接收這三點來介紹。

原始碼分析

廣播的註冊過程

廣播的註冊分為靜態跟動態註冊,靜態註冊需要通過 PMS 來解析 Android 清單檔案等等複雜操作,這裡我們以動態註冊廣播來講解。先來看一下注冊時序圖:

Android 8.0 原始碼分析 (六) BroadcastReceiver 啟動

首先,想要使用廣播就得通過 registerReceiver 方法註冊一個廣播,最終也是在 Activity 的父類 ContextWrapper 中實現,程式碼如下:

//ContextWrapper.java
		/**
     * 應用呼叫註冊廣播的函式
     * @param receiver The BroadcastReceiver to handle the broadcast.
     * @param filter Selects the Intent broadcasts to be received.
     *
     * @return
     */
    @Override
    public Intent registerReceiver(
        BroadcastReceiver receiver, IntentFilter filter) {
        //mBase 是 Context的引用
        return mBase.registerReceiver(receiver, filter);
    }
複製程式碼

我們在講解 Android 8.0 原始碼分析 (五) Service 啟動 的時候,開篇介紹了 Activity,Context,ContextImpl 之間的關係,這裡就不在多說了,有不瞭解可以先去看一下上一篇文章。這裡我們直接看 Context 的實現類 ContextImpl 中的實現,程式碼如下:

    /**
     * Context 呼叫
     * @param receiver The BroadcastReceiver to handle the broadcast.
     * @param filter Selects the Intent broadcasts to be received.
     *
     * @return
     */
    @Override
    public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
        //呼叫內部過載方法
        return registerReceiver(receiver, filter, null, null);
    }



    @Override
    public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
                                   String broadcastPermission, Handler scheduler) {
        //呼叫內部過載方法
        return registerReceiverInternal(receiver, getUserId(),
                filter, broadcastPermission, scheduler, getOuterContext(), 0);
    }


    private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,
                                            IntentFilter filter, String broadcastPermission,
                                            Handler scheduler, Context context, int flags) {
        IIntentReceiver rd = null;
        if (receiver != null) {
            /**
             * 1. 
             */
            if (mPackageInfo != null && context != null) {
                if (scheduler == null) {
                    //獲取 ActivityThread H
                    scheduler = mMainThread.getHandler();
                }
                //獲取 IIntentReceiver 物件,通過它與 AMS 互動,並且通過 Handler 傳遞訊息
                rd = mPackageInfo.getReceiverDispatcher(
                        receiver, context, scheduler,
                        /**
                         * 2. 
                         */
                        mMainThread.getInstrumentation(), true);
            } else {
                if (scheduler == null) {
                    scheduler = mMainThread.getHandler();
                }
                /**
                 * 3. 
                 */
                rd = new LoadedApk.ReceiverDispatcher(
                        receiver, context, scheduler, null, true).getIIntentReceiver();
            }
        }
        try {
            //呼叫 AMS 的 registerReceiver
            /**
             * 4. 
             */
            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();
        }
    }
複製程式碼

前面 2 個函式都是簡單的呼叫內部過載方法,我們直接看 registerReceiverInternal 方法實現,註釋 1 判斷 mPackageInfo 與 context 是否為空,如果不為空將執行註釋 2 程式碼,通過 mPackageInfo 方法呼叫 getReceiverDispatch 方法獲取 rd 物件,否則就呼叫註釋 3 處的程式碼來建立 rd 物件。這 2 步程式碼主要就是來獲取 IIntentReceiver 物件,它是一個 Binder 介面,用於廣播的跨程式通訊,它在 LoadedApk.ReceiverDispatcher.InnerReceiver 中實現,程式碼如下所示:

//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 處呼叫 AMS 代理類 IActivityManagerregisterReceiver 方法,通過程式間通訊最後在 AMS 類的 registerReceiver 方法,並將 IIntentReceiver 型別的 rd 引用也一併傳遞進 AMS 中,這裡之所以不直接傳入客戶端的 BroadcastReceiver 而是傳入 IIntetnReceiver ,因為註冊廣播是一個跨程式的過程,需要具有跨程式通訊功能的 IIntentReceiver ,你也可以把它理解為一箇中間者,registerReceiver 方法內部比較多,這裡分為 2 個部分來講解,先來看 part1,程式碼如下:

1. registerReceiver 方法的 part1

//AMS.java
    /**
     * 這裡是通過 Binder 通知呼叫
     * @return Intent
     */
    public Intent registerReceiver(IApplicationThread caller, String callerPackage,
            IIntentReceiver receiver, IntentFilter filter, String permission, int userId,
            int flags) {
        enforceNotIsolatedCaller("registerReceiver");
        ArrayList<Intent> stickyIntents = null;
        ProcessRecord callerApp = null;
        final boolean visibleToInstantApps
                = (flags & Context.RECEIVER_VISIBLE_TO_INSTANT_APPS) != 0;
        int callingUid;
        int callingPid;
        boolean instantApp;
        synchronized(this) {
            if (caller != null) {
                /**
                 * 1. 通過 getRecordForAppLocked 方法得到 ProcessRecord型別的 callerApp 物件,用描述 AMS 註冊廣播接收者的 Activity 所在的程式
                 */
                callerApp = getRecordForAppLocked(caller);
                if (callerApp == null) {
                    throw new SecurityException(
                            "Unable to find app for caller " + caller
                            + " (pid=" + Binder.getCallingPid()
                            + ") when registering receiver " + receiver);
                }
                if (callerApp.info.uid != SYSTEM_UID &&
                        !callerApp.pkgList.containsKey(callerPackage) &&
                        !"android".equals(callerPackage)) {
                    throw new SecurityException("Given caller package " + callerPackage
                            + " is not running in process " + callerApp);
                }
                callingUid = callerApp.info.uid;
                callingPid = callerApp.pid;
            } else {
                callerPackage = null;
                callingUid = Binder.getCallingUid();
                callingPid = Binder.getCallingPid();
            }

            instantApp = isInstantApp(callerApp, callerPackage, callingUid);
            userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, true,
                    ALLOW_FULL_ONLY, "registerReceiver", callerPackage);

            /**
             * 2. 根據傳入的 IntentFilter 型別的 filter 得到一個 action 列表
             */
            Iterator<String> actions = filter.actionsIterator();
            if (actions == null) {
                ArrayList<String> noAction = new ArrayList<String>(1);
                noAction.add(null);
                actions = noAction.iterator();
            }

            // Collect stickies of users
            int[] userIds = { UserHandle.USER_ALL, UserHandle.getUserId(callingUid) };
            while (actions.hasNext()) {
                
                String action = actions.next();
                for (int id : userIds) {
                    //根據actions 列表和 userIds 得到所有的粘性廣播的 intent,並在註釋 3 處傳入到 stickyIntents 中
                    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>();
                            }
                            /**
                             * 3. 將匹配到的粘性廣播 action 存入容器中
                             */
                            stickyIntents.addAll(intents);
                        }
                    }
                }
            }
        }

        ArrayList<Intent> allSticky = null;
        if (stickyIntents != null) {
            final ContentResolver resolver = mContext.getContentResolver();
            // Look for any matching sticky broadcasts...
            /**
             * 遍歷尋找匹配的粘性廣播
             */
            for (int i = 0, N = stickyIntents.size(); i < N; i++) {
                Intent intent = stickyIntents.get(i);
                // Don't provided intents that aren't available to instant apps.
                if (instantApp &&
                        (intent.getFlags() & Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS) == 0) {
                    continue;
                }
                //開始匹配
                if (filter.match(resolver, intent, true, TAG) >= 0) {
                    if (allSticky == null) {
                        allSticky = new ArrayList<Intent>();
                    }
                    /**
                     * 4. 將intent 存入 allSticky 列表中
                     */
                    allSticky.add(intent);
                }
            }
        }

        ....//後面程式碼 part2 講解

            return sticky;
        }
    }

複製程式碼

在註釋 1 通過 getRecordForAppLocked 方法得到 ProcessRecord 型別的 callerApp 物件,它用於描述請求 AMS 註冊廣播接收者的 Activity 所在的應用程式程式。在註釋 2 處根據傳入的 IntentFilter 型別的 filter 得到一個 actions 列表,根據 actions 列表和 userIds 得到所有的粘性廣播的 intent ,並在註釋 3 處傳入到 stickyIntents 中。接下來從 stickyIntent 中匹配傳入的引數 filter 的粘性廣播的 intent ,在註釋 4 處將這些 intent 存入到 allSticky 列表中,從這裡可以看出粘性廣播是儲存在 AMS 中的。

2. registerReceiver 方法的 part2

接下來看 AMS 的 registerReceiver 方法剩餘內容,程式碼如下所示:

//AMS.java

    /**
     * 這裡是通過 Binder 通知呼叫
     * @return Intent
     */
    public Intent registerReceiver(IApplicationThread caller, String callerPackage,
            IIntentReceiver receiver, IntentFilter filter, String permission, int userId,
            int flags) {
     ...
       
       
          synchronized (this) {
					...
            /**
             * 1. 獲取 ReceiverList
             */
            ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
            if (rl == null) {//如果為空呼叫註釋 2
                /**
                 * 2. 建立一個 ReceiverList 物件,它繼承自 ArrayList,用於存放廣播接收者
                 */
                rl = new ReceiverList(this, callerApp, callingPid, callingUid,
                        userId, receiver);
                if (rl.app != null) {
                    rl.app.receivers.add(rl);
                } else {
                    try {
                        receiver.asBinder().linkToDeath(rl, 0);
                    } catch (RemoteException e) {
                        return sticky;
                    }
                    rl.linkedToDeath = true;
                }
                mRegisteredReceivers.put(receiver.asBinder(), rl);
            } else if (rl.uid != callingUid) {
                ...
            }

            /**
             *  3. 構建 BroadcastFilter 物件,並且將 廣播接收者列表 ReceiverList 新增到進去
             *  用於描述註冊的廣播接收者
             */
            BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
                    permission, callingUid, userId, instantApp, visibleToInstantApps);
            /**
             * 4. 通過 add 函式將自身新增到廣播接收者列表中
             */
            rl.add(bf);
            if (!bf.debugCheck()) {
                Slog.w(TAG, "==> For Dynamic broadcast");
            }
            /**
             * 5. 將 BroadcastFilter 新增到 IntentResolver 型別的 mReceiverResolver 中。
             */
            mReceiverResolver.addFilter(bf);

    
      ...
    }
複製程式碼

在註釋 1 處獲取到了 ReceiverList 列表,如果為空則在註釋 2 處建立一個新的接收列表 ReceiverList 物件,它繼承自 ArrayList,用來儲存廣播接收者。在註釋 3 處建立 BroadcastFilter 並傳入此前建立的 ReceiverList ,BroadcastFilter 用來描述註冊的廣播接收者,並在註釋 4 處通過 add 方法將自身新增到 ReceiverList 中。在註釋 5 處將 BroadcastFilter 新增到 IntentResolver 型別的 mReceiverResolver 中,這樣當 AMS 接收到廣播時就可以從 mReceiverResolver 中找到對應的廣播接收者了。從而達到註冊廣播的目的。

廣播的傳送和接收過程

廣播的傳送和接收分為 2 個階段來分析,通過應用程式到 AMS SystemServer 程式的呼叫,然後 AMS 所在的程式通知應用程式的呼叫,下面我們先來分析應用程式程式到 AMS 的過程。

ContextImpl 到 AMS 的呼叫過程

廣播傳送多種型別的廣播,比如 無序、有序、粘性廣播,這裡以最簡單的廣播無序廣播來講解,也就是傳送一個普通廣播,它的實現也是在 ContextWrapper 中,下面先來看一個整體呼叫時序圖.

Android 8.0 原始碼分析 (六) BroadcastReceiver 啟動

直接來看 ContextWrapper 的 sendBroadcast 方法,程式碼如下:

//ContextWrapper.java
    @Override
    public void sendBroadcast(Intent intent) {
      	//呼叫 Context 的實現類 ContextImpl
        mBase.sendBroadcast(intent);
    }
複製程式碼

這裡的 mBase 是 Context , 在之前文章中將到了 ContextImpl 是 Context 的實現類,我們直接看它的實現,程式碼如下:

//ContextImpl.java
    @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();
        }
    }
複製程式碼

看到上面的程式碼是不是倍感的親切,熟悉,沒錯它又呼叫 AMS 的代理類 IActivityManager 的 broadcastIntent 函式,我們發現跟我們麼之前講解的 Activity,Service 呼叫方式一樣都會經過 AMS 的代理類 IActivityManager ,那麼 AMS 到底是何方神聖,這個我們講解完 四大元件 之後會單獨來介紹,下面我們直接看 AMS 的實現:

//AMS.java

    /**
     * 應用程式程式呼叫
     * @param caller
     * @param intent
     * @param resolvedType
     * @param resultTo
     * @param resultCode
     * @param resultData
     * @param resultExtras
     * @param requiredPermissions
     * @param appOp
     * @param bOptions
     * @param serialized
     * @param sticky
     * @param userId
     * @return
     */
    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) {
            /**
             * 1. 驗證廣播是否合法
             */
            intent = verifyBroadcastLocked(intent);
            /**
             * 拿到所在的程式
             */
            final ProcessRecord callerApp = getRecordForAppLocked(caller);
            final int callingPid = Binder.getCallingPid();
            final int callingUid = Binder.getCallingUid();
            final long origId = Binder.clearCallingIdentity();
            /**
             * 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 內部驗證廣播程式碼實現:

//AMS.java

    final Intent verifyBroadcastLocked(Intent intent) {
        // Refuse possible leaked file descriptors
        /**
         * 1. 驗證 intent 是否不為null 並且有檔案描述符
         */
        if (intent != null && intent.hasFileDescriptors() == true) {
          ...
          }

        /**
         * 2. 獲得 intent 的 flags
         */
        int flags = intent.getFlags();

        if (!mProcessesReady) {
            /**
             * 3. 系統正在啟動過程中。如果是動態廣播者不做處理
             */
            if ((flags&Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT) != 0) {
                
                /**
                 * 4. 如果 flag 沒有設定為 FLAG_RECEIVER_REGISTERED_ONLY 只接受動態註冊的廣播接收者則會丟擲異常
                 */
            } else if ((flags&Intent.FLAG_RECEIVER_REGISTERED_ONLY) == 0) {
            ...    
            }
        }

				...

        return intent;
    }
複製程式碼

根據上面的註釋我們知道就是對 Intent 判斷是否合法性,下面我們繼續回到 broadcastIntent 註釋 2 ,程式碼如下:

//AMS.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) {
      ...
        
            /**
             * 1. 建立 BroadcastRecord 物件將 receivers 傳進去。
             */
            BroadcastRecord r = new BroadcastRecord(queue, intent, callerApp,
                    callerPackage, callingPid, callingUid, callerInstantApp, resolvedType,
                    requiredPermissions, appOp, brOptions, registeredReceivers, resultTo,
                    resultCode, resultData, resultExtras, ordered, sticky, false, userId);
      
                  final boolean replaced = replacePending
                    && (queue.replaceParallelBroadcastLocked(r) != null);
           
            if (!replaced) {
                queue.enqueueParallelBroadcastLocked(r);
                /**
                 * 2.
                 */
                //處理廣播分發
                queue.scheduleBroadcastsLocked();
            }
            registeredReceivers = null;
            NR = 0;
        }
        
      ...
      
    }     
複製程式碼

註釋 1 上面省略了很多程式碼,省略的程式碼如要是把註冊 動態/靜態的廣播接收者按照優先順序高低不同儲存在不同的列表中,在將這 2 個列表合併到 receivers 列表中,這樣 receivers 列表包含了所有的廣播接收者。在註釋 1 處建立 BroadcastReceiver 物件並將 receivers 傳遞進去,在註釋 2 處呼叫 BroadcastQuque 的 scheduleBroadcastsLocked 方法。

AMS 到 BroadcastReceiver 的呼叫過程

AMS 到 BroadcastReceiver 的呼叫過程請先看下面時序圖:

Android 8.0 原始碼分析 (六) BroadcastReceiver 啟動

下面直接看 BroadcastQueue 的 scheduleBroadcastsLocked 函式實現,程式碼如下:

//BroadcastQueue.java

    public void scheduleBroadcastsLocked() {
        if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Schedule broadcasts ["
                + mQueueName + "]: current="
                + mBroadcastsScheduled);

        if (mBroadcastsScheduled) {
            return;
        }
        //通過 Handler 分發
        mHandler.sendMessage(mHandler.obtainMessage(BROADCAST_INTENT_MSG, this));
        mBroadcastsScheduled = true;
    }
複製程式碼

上面的程式碼向 BroadcastHandler 型別的 mHandler 物件傳送了 BROADCAST_INTENT_MSG 型別的訊息,這個訊息在 BroadcastHandler 的 handleMessage 方法中進行處理,如下所示:

    //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;
                case BROADCAST_TIMEOUT_MSG: {
                    synchronized (mService) {
                        broadcastTimeoutLocked(true);
                    }
                } break;
            }
        }
    }
複製程式碼

在 handleMessage 方法中呼叫了 processnextBroadcast 方法,方法對無序廣播和有序廣播分別進行處理,在將廣播傳送給廣播接收者,處理程式碼如下:

//BroadcastQueue.java

    final void processNextBroadcast(boolean fromMsg) {
        synchronized(mService) {
            BroadcastRecord r;

  				...
            mService.updateCpuStats();

            if (fromMsg) {
                /**
                 * 1.  已經處理了  BROADCAST_INTENT_MSG 這條訊息
                 */
            
                mBroadcastsScheduled = false;
            }

            /**
             * 2. 遍歷儲存無序廣播的 mParallelBroadcasts 列表
             */
            while (mParallelBroadcasts.size() > 0) {
                /*** 3.獲取無序廣播 */
                r = mParallelBroadcasts.remove(0);
                ...
                
                for (int i=0; i<N; i++) {
                    Object target = r.receivers.get(i);
                 /**
                 * 4. 將 r 物件描述的廣播傳送給對應的廣播者
                 */
                    deliverToRegisteredReceiverLocked(r, (BroadcastFilter)target, false, i);
                }

                addBroadcastToHistoryLocked(r);
                if (DEBUG_BROADCAST_LIGHT) Slog.v(TAG_BROADCAST, "Done with parallel broadcast ["
                        + mQueueName + "] " + r);
            }

            ...
        }
    }
複製程式碼

從前面 BroadcastHandler 方法中我們得知傳入的引數 fromMsg 的值為 true,因此在註釋1 處將 mBroadcastsScheduled 設定為 flase, 表示對於此前發來的 BROADCAST_INTENT_MSG 型別的訊息已經處理了。在註釋 2 處的 mParallelBroadcasts 列表用來儲存無序廣播,通過 while 迴圈將 mParallelBroadcasts 列表中的無序廣播傳送給對應的廣播接收者。在註釋 3 處獲取每一個 mParallelBroadcasts 列表中儲存的 BroadcastRecord 型別的 r 物件。在註釋 4 處將這些 r 物件描述的廣播傳送給對應的廣播接收者,deliverToRegisteredReceiverLocked 方法,如下所示:

//BroadcastQueue.java


    private void deliverToRegisteredReceiverLocked(BroadcastRecord r,
            BroadcastFilter filter, boolean ordered, int index) {
     ...
       
                 if (filter.receiverList.app != null && filter.receiverList.app.inFullBackup) {
                 if (ordered) {
                    skipReceiverLocked(r);
                }
            } else {
                /***
                 * 1.
                 */
                performReceiveLocked(filter.receiverList.app, filter.receiverList.receiver,
                        new Intent(r.intent), r.resultCode, r.resultData,
                        r.resultExtras, r.ordered, r.initialSticky, r.userId);
            }  
       
     ...
       
      
    }
複製程式碼

deliverToRegisteredReceiverLocked 內部程式碼如要是用來檢查廣播傳送者和廣播接收者的許可權,如果通過了許可權的檢查,則會呼叫註釋 1 處的 performReceiveLocked 方法。

//BroadcastQueue.java

    void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,
            Intent intent, int resultCode, String data, Bundle extras,
            boolean ordered, boolean sticky, int sendingUser) throws RemoteException {
        /**
         * 1. 
         */
        if (app != null) {
            /**
             * 2. 
             */
            if (app.thread != null) {
                
                try {
                    /**
                     * 3. 
                     */
                    app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,
                            data, extras, ordered, sticky, sendingUser, app.repProcState);
                ...
    }
複製程式碼

在註釋 1 處和註釋 2 處的程式碼表示如果廣播的接收者所在的應用程式程式存在並且正在執行,則執行註釋 3 處的程式碼,表示用廣播接收者所在的應用程式程式來接收廣播,這裡 app.thread 指的是 ApplicationThread,它其實在 ActivityThread 內部類中,繼承於 IApplicationThread.Stub 下面我們看它的實現,程式碼如下:

//ActivityThread.java

    private class ApplicationThread extends IApplicationThread.Stub {
    ...
      
            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);
      			/***1. 呼叫 IIntentReceiver 中的 performReceive 函式*/
            receiver.performReceive(intent, resultCode, dataStr, extras, ordered,
                    sticky, sendingUser);
        }
    ...
      
    }
複製程式碼

在註釋 1 中呼叫了 IIntentReceiver 的 performReceive 函式,IItentReceiver 在前面提到過,用於廣播的跨程式的通訊,它的具體實現在 LoadedApk.ReceiverDispatcher.InnerReceiver,程式碼如下:

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

            @Override
            public void performReceive(Intent intent, int resultCode, String data,
                    Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
                final LoadedApk.ReceiverDispatcher rd;
                if (intent == null) {
                    Log.wtf(TAG, "Null intent received");
                    rd = null;
                } else {
                    rd = mDispatcher.get();
                }
              ...
                if (rd != null) {
                  //1. 
                    rd.performReceive(intent, resultCode, data, extras,
                            ordered, sticky, sendingUser);
                } else {
                   ...
                }
            }
        }
      ....
        
    }
複製程式碼

其實 IIntentReceiver 和 IActivityManager 一樣,都使用了 aidl 來實現程式間通訊。InnerReceiver 繼承自 IIntentReceiver.Stub,是 Binder 通訊的伺服器端, IIntentReceiver 則是 Binder 通訊的客服端、InnerReceiver 在本地的代理,它的具體的實現就是 InnerReceiver. 在 InnerReceiver 的 performReceiver 方法的註釋 1 呼叫了 ReceiverDispatch 型別的 rd 物件的 performReceive 方法,程式碼如下:

//LoadedApk.java
        public void performReceive(Intent intent, int resultCode, String data,
                Bundle extras, boolean ordered, boolean sticky, int sendingUser) {
            /**
             * 1. 
             */
            final Args args = new Args(intent, resultCode, data, extras, ordered,
                    sticky, sendingUser);
           ...
            /**
             * 2. 
             */
            if (intent == null || !mActivityThread.post(args.getRunnable())) {
                if (mRegistered && ordered) {
                    IActivityManager mgr = ActivityManager.getService();
                    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 物件的 getRunnable 方法通過 H 傳送到執行緒的訊息佇列中, Args 中的實現,程式碼如下:

//LoadedApk.java

        final class Args extends BroadcastReceiver.PendingResult {
            private Intent mCurIntent;
            private final boolean mOrdered;
            private boolean mDispatched;
            private Throwable mPreviousRunStacktrace; // To investigate b/37809561. STOPSHIP remove.

            public Args(Intent intent, int resultCode, String resultData, Bundle resultExtras,
                    boolean ordered, boolean sticky, int sendingUser) {
                super(resultCode, resultData, resultExtras,
                        mRegistered ? TYPE_REGISTERED : TYPE_UNREGISTERED, ordered,
                        sticky, mIIntentReceiver.asBinder(), sendingUser, intent.getFlags());
                mCurIntent = intent;
                mOrdered = ordered;
            }

            public final Runnable getRunnable() {
                return () -> {
                    final BroadcastReceiver receiver = mReceiver;
                    ...
                    try {
                        ClassLoader cl = mReceiver.getClass().getClassLoader();
                        intent.setExtrasClassLoader(cl);
                        intent.prepareToEnterProcess();
                        setExtrasClassLoader(cl);
                        receiver.setPendingResult(this);
                        //1. 回撥到接收廣播的 onReceiver
                        receiver.onReceive(mContext, intent);
                    } catch (Exception e) {
                        ...
                  
                };
            }
        }
複製程式碼

在註釋 1 處執行了 BroadcastReceiver 回撥 **onReceive(mContext, intent);**方法,這樣註冊廣播接收者就收到了廣播並得到了 intent。

整個流程到這裡就講解完了,下一篇將為大家帶來四大元件中的最後一個元件 ContentProvider 啟動過程,敬請期待!

參考

  • 《Android 進階解密》

相關文章