Android 廣播內部機制詳解(一)

前言

相信大家在應用開發中都用過Broadcast,單純從使用的角度來說,是非常容易的,但對於系統開發工程師來說,需要了解廣播運行的機制,那還是有必要去閱讀它的源碼。
關於Broadcast的代碼也是非常非常多的,我這裏將分成幾個部分來講,分別是:
廣播的類型、廣播的註冊、廣播的發送、廣播的處理、廣播總結和建議。

傳送門:
Android 廣播內部機制詳解(二)
Android 廣播內部機制詳解(三)

1. 廣播的類型

廣播分爲:
普通廣播:通過Context.sendBroadcast()發送
有序廣播:通過Context.sendOrderedBroadcast()發送
粘性廣播:通過Context.sendStickyBroadcast()發送

2. 廣播的註冊

衆所周知,註冊有兩種方法,分別爲動態註冊和靜態註冊,這裏我們先來說說動態註冊。

2.1 動態註冊

動態註冊調用的是context中的registerReceiver,這個方法在ContextImpl中實現

    @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) {
            //見2.1.1
        return registerReceiverInternal(receiver, getUserId(),
                filter, broadcastPermission, scheduler, getOuterContext());
    }

    @Override
    public Intent registerReceiverAsUser(BroadcastReceiver receiver, UserHandle user,
            IntentFilter filter, String broadcastPermission, Handler scheduler) {
        return registerReceiverInternal(receiver, user.getIdentifier(),
                filter, broadcastPermission, scheduler, getOuterContext());
    }

大家可以看到這些註冊的方法最終都調用了registerReceiverInternal,接下來,我們再來看看registerReceiverInternal

2.1.1 registerReceiverInternal

java文件:ContextImpl.java

    private Intent registerReceiverInternal(BroadcastReceiver receiver, int userId,
            IntentFilter filter, String broadcastPermission,
            Handler scheduler, Context context) {
        IIntentReceiver rd = null;
        if (receiver != null) {
            //mPackageInfo的類型爲Loade
            if (mPackageInfo != null && context != null) {
                //沒有設置scheduler的話,就使用主線程的Handler
                if (scheduler == null) {
                    scheduler = mMainThread.getHandler();
                }
                //見2.1.2
                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 {
            //見2.1.4
            final Intent intent = ActivityManagerNative.getDefault().registerReceiver(
                    mMainThread.getApplicationThread(), mBasePackageName,
                    rd, filter, broadcastPermission, userId);
            if (intent != null) {
                intent.setExtrasClassLoader(getClassLoader());
                intent.prepareToEnterProcess();
            }
            return intent;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

一般情況下,mPackageInfo和context不爲null,scheduler用的主線程的Handler,接着會調用rd = mPackageInfo.getReceiverDispatcher(receiver, context, scheduler,mMainThread.getInstrumentation(), true);

2.1.2 getReceiverDispatcher

java文件:LoadedApk.java

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);
                }
            }
            //第一次註冊肯定會走這裏,rd == null
            if (rd == null) {
                //見2.1.3
                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,mReceivers一張存儲了所有所有receiver的哈希表,在這裏通過context和對應的receiver可以得到對應的ReceiverDispatcher,進而返回IIntentReceiver。
大家都知道,其實廣播的發送其實靠AMS來傳遞的,AMS通過匹配註冊的信息然後然後將廣播發給對應的進程,對應進程的Receiver再調用它的onReceive(),這利用的是binder機制,上文中的rd對象(IIntentReceiver rd)就是binder實體,並用ReceiveDispatcher這個類來管理它,接下來我們來看看ReceiveDispatcher

2.1.3 ReceiverDispatcher

文件: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 (ActivityThread.DEBUG_BROADCAST) {
                    int seq = intent.getIntExtra("seq", -1);
                    Slog.i(ActivityThread.TAG, "Receiving broadcast " + intent.getAction()
                            + " seq=" + seq + " to " + (rd != null ? rd.mReceiver : null));
                }
                if (rd != null) {
                    rd.performReceive(intent, resultCode, data, extras,
                            ordered, sticky, sendingUser);
                } else {
                    // The activity manager dispatched a broadcast to a registered
                    // receiver in this process, but before it could be delivered the
                    // receiver was unregistered.  Acknowledge the broadcast on its
                    // behalf so that the system's broadcast sequence can continue.
                    if (ActivityThread.DEBUG_BROADCAST) Slog.i(ActivityThread.TAG,
                            "Finishing broadcast to unregistered receiver");
                    IActivityManager mgr = ActivityManagerNative.getDefault();
                    try {
                        if (extras != null) {
                            extras.setAllowFds(false);
                        }
                        mgr.finishReceiver(this, resultCode, data, extras, false, intent.getFlags());
                    } catch (RemoteException e) {
                        throw e.rethrowFromSystemServer();
                    }
                }
            }
        }
        //它就是上面的rd
        final IIntentReceiver.Stub mIIntentReceiver;
        //持有對應的BroacastReceiver,和BroacastReceiver一一對應
        final BroadcastReceiver mReceiver;
        final Context mContext;
        final Handler mActivityThread;
        final Instrumentation mInstrumentation;
        final boolean mRegistered;
        final IntentReceiverLeaked mLocation;
        RuntimeException mUnregisterLocation;
        boolean mForgotten;

        .........

        ReceiverDispatcher(BroadcastReceiver receiver, Context context,
                Handler activityThread, Instrumentation instrumentation,
                boolean registered) {
            if (activityThread == null) {
                throw new NullPointerException("Handler must not be null");
            }

            mIIntentReceiver = new InnerReceiver(this, !registered);
            mReceiver = receiver;
            mContext = context;
            mActivityThread = activityThread;
            mInstrumentation = instrumentation;
            mRegistered = registered;
            mLocation = new IntentReceiverLeaked(null);
            mLocation.fillInStackTrace();
        }

        ..........

    }

BroadcastReceiver和ReceiverDispatcher是一一對應的,AMS通過對應的mIIntentReceiver(ReceiverDispatcher類裏)來響應對應廣播的onReceiver。

2.1.4 registerReceiver

            final Intent intent = ActivityManagerNative.getDefault().registerReceiver(
                    mMainThread.getApplicationThread(), mBasePackageName,
                    rd, filter, broadcastPermission, userId);

看這代碼我們可以知道registerReceiver的實現肯定是在AMS裏面,我們在去AMS裏面看看:
java文件:ActivityManagerService.java

    public Intent registerReceiver(IApplicationThread caller, String callerPackage,
            IIntentReceiver receiver, IntentFilter filter, String permission, int userId) {

        ......

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

            Iterator<String> actions = filter.actionsIterator();
            if (actions == null) {
                ArrayList<String> noAction = new ArrayList<String>(1);
                noAction.add(null);
                actions = noAction.iterator();
            }

            // 收集所有與註冊用戶userId相關所有的已經廣播過的粘性廣播,並將它放到stickyIntents裏面
            // 這裏僅僅只是先做一層關於action的篩選
            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);
                        }
                    }
                }
            }
        }
        // 通過IntentFilter.match方法對上面篩選出來的stickyIntents進行一次精準的篩選
        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);
                //將匹配成功的粘性廣播的intent放入allSticky中
                if (filter.match(resolver, intent, true, TAG) >= 0) {
                    if (allSticky == null) {
                        allSticky = new ArrayList<Intent>();
                    }
                    allSticky.add(intent);
                }
            }
        }

        // The first sticky in the list is returned directly back to the client.
        Intent sticky = allSticky != null ? allSticky.get(0) : null;
        if (DEBUG_BROADCAST) Slog.v(TAG_BROADCAST, "Register receiver " + filter + ": " + sticky);
        if (receiver == null) {
            return sticky;
        }

        synchronized (this) {
            if (callerApp != null && (callerApp.thread == null
                    || callerApp.thread.asBinder() != caller.asBinder())) {
                // 如果調用進程已經死了,返回null
                return null;
            }
            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 {
                        receiver.asBinder().linkToDeath(rl, 0);
                    } catch (RemoteException e) {
                        return sticky;
                    }
                    rl.linkedToDeath = true;
                }
                //存儲了當前系統中所與receiver註冊的所有的filter
                mRegisteredReceivers.put(receiver.asBinder(), rl);
            } 

            ......

            BroadcastFilter bf = new BroadcastFilter(filter, rl, callerPackage,
                    permission, callingUid, userId);
            rl.add(bf);
            if (!bf.debugCheck()) {
                Slog.w(TAG, "==> For Dynamic broadcast");
            }
            // 存儲了當前系統中所有的BraodcastFilter
            mReceiverResolver.addFilter(bf);

            // Enqueue broadcasts for all existing stickies that match
            // this filter.
            if (allSticky != null) {
                ArrayList receivers = new ArrayList();
                receivers.add(bf);

                final int stickyCount = allSticky.size();
                for (int i = 0; i < stickyCount; i++) {
                    Intent intent = allSticky.get(i);
                    BroadcastQueue queue = broadcastQueueForIntent(intent);
                    BroadcastRecord r = new BroadcastRecord(queue, intent, null,
                            null, -1, -1, null, null, AppOpsManager.OP_NONE, null, receivers,
                            null, 0, null, null, false, true, true, -1);
                    //將BroadcastRecord加到BroadcastQueue.ParallelBroadcasts中
                    queue.enqueueParallelBroadcastLocked(r);
                    //發送廣播,如果當前還有廣播正在處理髮送,這次推動不會執行
                    queue.scheduleBroadcastsLocked();
                }
            }
            return sticky;
        }
    }

這部分代碼比較長,重點的地方我在代碼中都有註釋,我這裏再總結一下:

  • 將發送過的粘性廣播和註冊的filter的action進行匹配,匹配成功的放到stickyIntents,然後再進行一個精準的篩選,包括type、scheme、data等,匹配成功放到allSticky,接着如果傳入的receiver爲null,返回sticky(如果allSticky不爲null,則返回allSticky的第一個,否則返回)

  • 調用進程如果爲已經死了,則返回null;返回對應receiver.asBinder()(也就是ReceiverDispatcher)的ReceiverList

  • 根據當前的IntentFilter創建BroadcastFilter對象,創建完畢之後,將BroadcastFilter對象對象放入mReceiverResolver中

  • 發送篩選出來的sticky廣播

2.2 靜態註冊

廣播的靜態註冊是通過在AdroidManifest.xml中聲明receiver來實現的,用法非常簡單。這些信息會在系統啓動的時候,由PKMS解析並記錄下來,並對外提供接口,比如:AMS就可以用過queryIntentReceivers()函數來察看有多少receiver對目標action感興趣

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