溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Android Broadcast原理分析之registerReceiver有什么用

發(fā)布時(shí)間:2021-08-26 11:52:57 來(lái)源:億速云 閱讀:177 作者:小新 欄目:開(kāi)發(fā)技術(shù)

這篇文章主要為大家展示了“Android Broadcast原理分析之registerReceiver有什么用”,內(nèi)容簡(jiǎn)而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領(lǐng)大家一起研究并學(xué)習(xí)一下“Android Broadcast原理分析之registerReceiver有什么用”這篇文章吧。

    1. BroadcastReceiver概述

    廣播作為四大組件之一,在平時(shí)開(kāi)發(fā)過(guò)程中會(huì)大量使用到,使用方式也是多種多樣的,既可以自己在manifest中注冊(cè),也可以在java代碼中動(dòng)態(tài)注冊(cè),既可以接收由系統(tǒng)發(fā)出的廣播,也可以接受自己定義并發(fā)送的廣播。廣播可以實(shí)現(xiàn)進(jìn)程內(nèi)以及跨進(jìn)程之間的通信。從本文開(kāi)始將分別介紹廣播的注冊(cè),廣播的派發(fā),本地廣播(LocalBroadcast)以及Android O上對(duì)廣播的限制,本文主要介紹廣播動(dòng)態(tài)注冊(cè)。

    2. BroadcastReceiver分類

    從注冊(cè)方式上區(qū)分:動(dòng)態(tài)注冊(cè)以及靜態(tài)注冊(cè)(顯示廣播和隱式廣播)
    從發(fā)送方式上區(qū)分:無(wú)序廣播和有序廣播
    從處理類型上區(qū)分:前臺(tái)廣播和后臺(tái)廣播
    從運(yùn)行方式上區(qū)分:普通廣播和Sticky廣播(已棄用)
    從發(fā)送者區(qū)分:系統(tǒng)廣播和自定義廣播
    此外還有protect broadcast(只允許指定應(yīng)用可以發(fā)送)
    sticky廣播:系統(tǒng)保存了一部分廣播的狀態(tài),當(dāng)你注冊(cè)的時(shí)候,不需要等到下次這個(gè)廣播發(fā)出來(lái),直接把最近上一次發(fā)送的這個(gè)廣播返回給你

    以上的這些概念在接下來(lái)的介紹中都會(huì)逐個(gè)涉及。

    3. registerReceiver流程圖

    Android Broadcast原理分析之registerReceiver有什么用

    其中的APP,ContextImpl,LoadedApk,ActivityManagerProxy都在APP本身的進(jìn)程中,ActivityManagerService在system_server進(jìn)程中。

    1. 首先在APP的進(jìn)程中發(fā)起廣播的注冊(cè),通過(guò)registerReceiver接口,這個(gè)方法有很多重載方法,但是最終的入口都是在ContextImpl中,后面會(huì)詳細(xì)介紹

    2. 從之前的Context的學(xué)習(xí)可以知道,registerReceiver最終調(diào)用的實(shí)現(xiàn)在ContextImpl

    3. 如果沒(méi)有指定處理廣播的handler,則默認(rèn)使用主線程的handler

    4. 獲取要注冊(cè)的ReceiverDispatcher,在注冊(cè)的Context相同的情況下,每個(gè)Receiver對(duì)應(yīng)一個(gè)ReceiverDispatcher

    5. 通過(guò)binder call到systemserver進(jìn)行廣播注冊(cè)

    4. 源碼解析

    4.1 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) {
                        // 注冊(cè)receiver的時(shí)候可以指定接受recover的Handler
                        // 如果沒(méi)有指定,則默認(rèn)用主線程的handler處理
                        scheduler = mMainThread.getHandler();
                    }
                    // 獲取IIntentReceiver
                    // 這個(gè)是一個(gè)Binder對(duì)象,當(dāng)廣播來(lái)臨時(shí),用于AMS向客戶端發(fā)起回調(diào)
                    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 {
                // binder call至AMS,進(jìn)行廣播注冊(cè)
                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();
            }
        }

    參數(shù)解析:

    receiver:將要注冊(cè)的receiver
    userId:用戶空間標(biāo)志,默認(rèn)情況下我們都只有一個(gè)user,現(xiàn)在一些手機(jī)推出的分身,其實(shí)就是用的第二個(gè)user,這種情況下userid會(huì)變,否則默認(rèn)主空間的都是0
    IntentFilter:要注冊(cè)的廣播的filter
    broadcastPermission:指定要注冊(cè)的廣播的權(quán)限
    scheduler:指定廣播接受(也就是onReceive)所在的線程,也就是說(shuō)注冊(cè)的時(shí)候就可以指定好廣播處理放在哪個(gè)線程,如果receiver中事情太多,可以放在另外一個(gè)線程,這樣可以避免主線程被卡住
    context:通過(guò)getOuterContext獲取到,前面在介紹context的時(shí)候有提到,application/service/activity中獲取到的是不一樣的
    flags:注冊(cè)廣播所攜帶的flag

    4.2 LoadedApk.getReceiverDispatcher

    public IIntentReceiver getReceiverDispatcher(BroadcastReceiver r,
                Context context, Handler handler,
                Instrumentation instrumentation, boolean registered) {
            synchronized (mReceivers) {
                // 如果Context相同,每個(gè)receiver對(duì)應(yīng)一個(gè)ReceiverDispatcher
                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) {
                            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();
            }
        }

    mReceivers是一個(gè)二級(jí)map,一級(jí)key是context,二級(jí)key是BroadcastReceiver,value是ReceiverDispatcher。

    這里的ReceiverDispatcher又是什么呢?

    它是LoadedApk中的一個(gè)內(nèi)部類,保存了這個(gè)receiver的信息,用于在廣播派發(fā)到本進(jìn)程的時(shí)候執(zhí)行,上面這方法最重要的是getIIntentReceiver,這個(gè)就非常重要了,它是一個(gè)Binder對(duì)象,說(shuō)在廣播注冊(cè)的時(shí)候?qū)⑦@個(gè)binder對(duì)象傳遞到了AMS,然后當(dāng)廣播派發(fā)到本進(jìn)程的時(shí)候,通過(guò)這個(gè)binder對(duì)象再會(huì)調(diào)回來(lái),它在ReceiverDispatcher創(chuàng)建的時(shí)候創(chuàng)建。

    static final class ReceiverDispatcher {
            // 是一個(gè)binder對(duì)象
            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) {
                        // 這里就是廣播真正派發(fā)到本進(jìn)程的時(shí)候從systemserver binder call過(guò)來(lái)執(zhí)行的
                    ...
            }
    
            final IIntentReceiver.Stub mIIntentReceiver;
            final BroadcastReceiver mReceiver;
            final Context mContext;
            final Handler mActivityThread;
            final Instrumentation mInstrumentation;
            final boolean mRegistered;
            final IntentReceiverLeaked mLocation;
            RuntimeException mUnregisterLocation;
            boolean mForgotten;
            ...
        }

    到這里,廣播注冊(cè)在APP進(jìn)程的流程就走完了,主要做了幾件事:

    1. 獲取handler

    2. 獲取ReceiverDispatcher

    3. 獲取InnerReceiver

    4. 將上面這些連帶receiver的相關(guān)信息,發(fā)起binder call到ActivityManagerService

    4.3 ActivityManagerService.registerReceiver

    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) {
                    // 正常來(lái)講caller是發(fā)起binder call的客戶端進(jìn)程對(duì)應(yīng)的ApplicationThread對(duì)象
                    // 如果為null則拋異常
                    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();
                }
    
                // 判斷caller是否為instant app
                instantApp = isInstantApp(callerApp, callerPackage, callingUid);
                userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, true,
                        ALLOW_FULL_ONLY, "registerReceiver", callerPackage);
    
                // 獲取廣播注冊(cè)的filter中的action封裝到list中
                Iterator<String> actions = filter.actionsIterator();
                if (actions == null) {
                    ArrayList<String> noAction = new ArrayList<String>(1);
                    noAction.add(null);
                    actions = noAction.iterator();
                }
    
                // mStickyBroadcasts是一個(gè)二級(jí)map
                // 一級(jí)key是userId,二級(jí)key是廣播對(duì)應(yīng)的action,value是廣播對(duì)應(yīng)intent的list(一般只有一個(gè)intent)
                // 這里是為了查詢對(duì)于當(dāng)前user,本次注冊(cè)的所有action對(duì)應(yīng)的sticky廣播的intent
                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);
                            }
                        }
                    }
                }
            }
    
            ArrayList<Intent> allSticky = null;
            // 這里不為null表示本次注冊(cè)的廣播中有sticky廣播
            if (stickyIntents != null) {
                final ContentResolver resolver = mContext.getContentResolver();
                // 查找匹配的sticky廣播
                for (int i = 0, N = stickyIntents.size(); i < N; i++) {
                    Intent intent = stickyIntents.get(i);
                    // 如果caller是instant app,且intent的flag不允許對(duì)instant可見(jiàn),則跳過(guò)
                    if (instantApp &&
                            (intent.getFlags() & Intent.FLAG_RECEIVER_VISIBLE_TO_INSTANT_APPS) == 0) {
                        continue;
                    }
                    // If intent has scheme "content", it will need to acccess
                    // provider that needs to lock mProviderMap in ActivityThread
                    // and also it may need to wait application response, so we
                    // cannot lock ActivityManagerService here.
                    if (filter.match(resolver, intent, true, TAG) >= 0) {
                        if (allSticky == null) {
                            allSticky = new ArrayList<Intent>();
                        }
                        allSticky.add(intent);
                    }
                }
            }
    
            // 直接把最近的一個(gè)匹配到的sticky廣播返回
            Intent sticky = allSticky != null ? allSticky.get(0) : null;
            // 廣播注冊(cè)的時(shí)候receiver是可以為null的,這種情況下這里直接return
            if (receiver == null) {
                return sticky;
            }
    
            synchronized (this) {
                // 校驗(yàn)caller進(jìn)程是否正常
                if (callerApp != null && (callerApp.thread == null
                        || callerApp.thread.asBinder() != caller.asBinder())) {
                    // Original caller already died
                    return null;
                }
                // mRegisteredReceivers中存放了所有的已注冊(cè)的receiver
                // 每個(gè)BroadcastReceiver對(duì)應(yīng)一個(gè)InnerReceiver,即Binder對(duì)象
                // binder對(duì)象做key,value是ReceiverList
                // ReceiverList是一個(gè)ArrayList
                ReceiverList rl = mRegisteredReceivers.get(receiver.asBinder());
                if (rl == null) {
                    rl = new ReceiverList(this, callerApp, callingPid, callingUid,
                            userId, receiver);
                    if (rl.app != null) {
                        rl.app.receivers.add(rl);
                    } else {
                        try {
                            // 如果是新創(chuàng)建的receiver,還需要linkToDeath
                            receiver.asBinder().linkToDeath(rl, 0);
                        } catch (RemoteException e) {
                            return sticky;
                        }
                        rl.linkedToDeath = true;
                    }
                    // 放入mRegisteredReceivers
                    mRegisteredReceivers.put(receiver.asBinder(), rl);
                } else if (rl.uid != callingUid) {
                    throw new IllegalArgumentException(
                            "Receiver requested to register for uid " + callingUid
                            + " was previously registered for uid " + rl.uid
                            + " callerPackage is " + callerPackage);
                } else if (rl.pid != callingPid) {
                    throw new IllegalArgumentException(
                            "Receiver requested to register for pid " + callingPid
                            + " was previously registered for pid " + rl.pid
                            + " callerPackage is " + callerPackage);
                } else if (rl.userId != userId) {
                    throw new IllegalArgumentException(
                            "Receiver requested to register for user " + userId
                            + " was previously registered for user " + rl.userId
                            + " callerPackage is " + callerPackage);
                }
                // 每一個(gè)IntentFilter對(duì)應(yīng)一個(gè)BroadcastFilter
                BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
                        permission, callingUid, userId, instantApp, visibleToInstantApps);
                // receiverList中存放了通過(guò)這個(gè)receiver注冊(cè)的所有的filter
                // 每調(diào)用一次register就會(huì)add一次
                rl.add(bf);
                // mReceiverResolver中存放所有的BroadcastFilter
                mReceiverResolver.addFilter(bf);
    
                // 有匹配的sticky廣播,則直接開(kāi)始調(diào)度派發(fā)
                if (allSticky != null) {
                    ArrayList receivers = new ArrayList();
                    receivers.add(bf);
    
                    // 對(duì)于每一個(gè)sticky廣播,創(chuàng)建BroadcastRecord并入隊(duì)(并行)
                    final int stickyCount = allSticky.size();
                    for (int i = 0; i < stickyCount; i++) {
                        Intent intent = allSticky.get(i);
                        // 根據(jù)flag是否有FLAG_RECEIVER_FOREGROUND判斷入隊(duì)是前臺(tái)還是后臺(tái)隊(duì)列
                        BroadcastQueue queue = broadcastQueueForIntent(intent);
                        BroadcastRecord r = new BroadcastRecord(queue, intent, null,
                                null, -1, -1, false, null, null, AppOpsManager.OP_NONE, null, receivers,
                                null, 0, null, null, false, true, true, -1);
                        // 入隊(duì),并行隊(duì)列
                        queue.enqueueParallelBroadcastLocked(r);
                        // 啟動(dòng)廣播的調(diào)度,也就是開(kāi)始派發(fā)廣播
                        queue.scheduleBroadcastsLocked();
                    }
                }
                return sticky;
            }
        }

    上面主要做了幾件事情:

    1. 對(duì)caller的判斷

    2. 遍歷action,查詢是否有匹配的sticky廣播

    3. 將本次注冊(cè)的廣播放到mRegisteredReceivers中記錄

    4. 如果是sticky廣播,開(kāi)始派發(fā)

    以上是“Android Broadcast原理分析之registerReceiver有什么用”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對(duì)大家有所幫助,如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道!

    向AI問(wèn)一下細(xì)節(jié)

    免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

    AI