LocalBroadcastManager本地廣播是如何工作的赎离??

分析廣播工作原理端辱,我們大致分為兩個(gè)模塊梁剔,一個(gè)是LocalBroadcastManager控制的虽画、只能在進(jìn)程內(nèi)通信的廣播機(jī)制,另外一個(gè)還是通過ActivityManagerService作為中轉(zhuǎn)的荣病、可以在進(jìn)程間通信的廣播機(jī)制码撰。

LocalBroadcastManager控制的應(yīng)用內(nèi)廣播機(jī)制

本地廣播整體邏輯比較簡(jiǎn)單,關(guān)鍵代碼基本都在LocalBroadcastManager類中个盆,基本上簡(jiǎn)略瀏覽一遍源碼就能搞清楚里面的實(shí)現(xiàn)邏輯脖岛,此處先給出LocalBroadcastManager的源碼:

public final class LocalBroadcastManager {
    private static final class ReceiverRecord {
        final IntentFilter filter;
        final BroadcastReceiver receiver;
        boolean broadcasting;
        boolean dead;

        ReceiverRecord(IntentFilter _filter, BroadcastReceiver _receiver) {
            filter = _filter;
            receiver = _receiver;
        }

        @Override
        public String toString() {
            StringBuilder builder = new StringBuilder(128);
            builder.append("Receiver{");
            builder.append(receiver);
            builder.append(" filter=");
            builder.append(filter);
            if (dead) {
                builder.append(" DEAD");
            }
            builder.append("}");
            return builder.toString();
        }
    }

    private static final class BroadcastRecord {
        final Intent intent;
        final ArrayList<ReceiverRecord> receivers;

        BroadcastRecord(Intent _intent, ArrayList<ReceiverRecord> _receivers) {
            intent = _intent;
            receivers = _receivers;
        }
    }

    private static final String TAG = "LocalBroadcastManager";
    private static final boolean DEBUG = false;

    private final Context mAppContext;

    private final HashMap<BroadcastReceiver, ArrayList<ReceiverRecord>> mReceivers
            = new HashMap<>();
    private final HashMap<String, ArrayList<ReceiverRecord>> mActions = new HashMap<>();

    private final ArrayList<BroadcastRecord> mPendingBroadcasts = new ArrayList<>();

    static final int MSG_EXEC_PENDING_BROADCASTS = 1;

    private final Handler mHandler;

    private static final Object mLock = new Object();
    private static LocalBroadcastManager mInstance;

    public static LocalBroadcastManager getInstance(Context context) {
        synchronized (mLock) {
            if (mInstance == null) {
                mInstance = new LocalBroadcastManager(context.getApplicationContext());
            }
            return mInstance;
        }
    }

    private LocalBroadcastManager(Context context) {
        mAppContext = context;
        mHandler = new Handler(context.getMainLooper()) {

            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case MSG_EXEC_PENDING_BROADCASTS:
                        executePendingBroadcasts();
                        break;
                    default:
                        super.handleMessage(msg);
                }
            }
        };
    }

    /**
     * Register a receive for any local broadcasts that match the given IntentFilter.
     *
     * @param receiver The BroadcastReceiver to handle the broadcast.
     * @param filter Selects the Intent broadcasts to be received.
     *
     * @see #unregisterReceiver
     */
    public void registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
        synchronized (mReceivers) {
            ReceiverRecord entry = new ReceiverRecord(filter, receiver);
            ArrayList<ReceiverRecord> filters = mReceivers.get(receiver);
            if (filters == null) {
                filters = new ArrayList<>(1);
                mReceivers.put(receiver, filters);
            }
            filters.add(entry);
            for (int i=0; i<filter.countActions(); i++) {
                String action = filter.getAction(i);
                ArrayList<ReceiverRecord> entries = mActions.get(action);
                if (entries == null) {
                    entries = new ArrayList<ReceiverRecord>(1);
                    mActions.put(action, entries);
                }
                entries.add(entry);
            }
        }
    }

    /**
     * Unregister a previously registered BroadcastReceiver.  <em>All</em>
     * filters that have been registered for this BroadcastReceiver will be
     * removed.
     *
     * @param receiver The BroadcastReceiver to unregister.
     *
     * @see #registerReceiver
     */
    public void unregisterReceiver(BroadcastReceiver receiver) {
        synchronized (mReceivers) {
            final ArrayList<ReceiverRecord> filters = mReceivers.remove(receiver);
            if (filters == null) {
                return;
            }
            for (int i=filters.size()-1; i>=0; i--) {
                final ReceiverRecord filter = filters.get(i);
                filter.dead = true;
                for (int j=0; j<filter.filter.countActions(); j++) {
                    final String action = filter.filter.getAction(j);
                    final ArrayList<ReceiverRecord> receivers = mActions.get(action);
                    if (receivers != null) {
                        for (int k=receivers.size()-1; k>=0; k--) {
                            final ReceiverRecord rec = receivers.get(k);
                            if (rec.receiver == receiver) {
                                rec.dead = true;
                                receivers.remove(k);
                            }
                        }
                        if (receivers.size() <= 0) {
                            mActions.remove(action);
                        }
                    }
                }
            }
        }
    }

    /**
     * Broadcast the given intent to all interested BroadcastReceivers.  This
     * call is asynchronous; it returns immediately, and you will continue
     * executing while the receivers are run.
     *
     * @param intent The Intent to broadcast; all receivers matching this
     *     Intent will receive the broadcast.
     *
     * @see #registerReceiver
     *
     * @return Returns true if the intent has been scheduled for delivery to one or more
     * broadcast receivers.  (Note tha delivery may not ultimately take place if one of those
     * receivers is unregistered before it is dispatched.)
     */
    public boolean sendBroadcast(Intent intent) {
        synchronized (mReceivers) {
            final String action = intent.getAction();
            final String type = intent.resolveTypeIfNeeded(
                    mAppContext.getContentResolver());
            final Uri data = intent.getData();
            final String scheme = intent.getScheme();
            final Set<String> categories = intent.getCategories();

            final boolean debug = DEBUG ||
                    ((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0);
            if (debug) Log.v(
                    TAG, "Resolving type " + type + " scheme " + scheme
                    + " of intent " + intent);

            ArrayList<ReceiverRecord> entries = mActions.get(intent.getAction());
            if (entries != null) {
                if (debug) Log.v(TAG, "Action list: " + entries);

                ArrayList<ReceiverRecord> receivers = null;
                for (int i=0; i<entries.size(); i++) {
                    ReceiverRecord receiver = entries.get(i);
                    if (debug) Log.v(TAG, "Matching against filter " + receiver.filter);

                    if (receiver.broadcasting) {
                        if (debug) {
                            Log.v(TAG, "  Filter's target already added");
                        }
                        continue;
                    }

                    int match = receiver.filter.match(action, type, scheme, data,
                            categories, "LocalBroadcastManager");
                    if (match >= 0) {
                        if (debug) Log.v(TAG, "  Filter matched!  match=0x" +
                                Integer.toHexString(match));
                        if (receivers == null) {
                            receivers = new ArrayList<ReceiverRecord>();
                        }
                        receivers.add(receiver);
                        receiver.broadcasting = true;
                    } else {
                        if (debug) {
                            String reason;
                            switch (match) {
                                case IntentFilter.NO_MATCH_ACTION: reason = "action"; break;
                                case IntentFilter.NO_MATCH_CATEGORY: reason = "category"; break;
                                case IntentFilter.NO_MATCH_DATA: reason = "data"; break;
                                case IntentFilter.NO_MATCH_TYPE: reason = "type"; break;
                                default: reason = "unknown reason"; break;
                            }
                            Log.v(TAG, "  Filter did not match: " + reason);
                        }
                    }
                }

                if (receivers != null) {
                    for (int i=0; i<receivers.size(); i++) {
                        receivers.get(i).broadcasting = false;
                    }
                    mPendingBroadcasts.add(new BroadcastRecord(intent, receivers));
                    if (!mHandler.hasMessages(MSG_EXEC_PENDING_BROADCASTS)) {
                        mHandler.sendEmptyMessage(MSG_EXEC_PENDING_BROADCASTS);
                    }
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * Like {@link #sendBroadcast(Intent)}, but if there are any receivers for
     * the Intent this function will block and immediately dispatch them before
     * returning.
     */
    public void sendBroadcastSync(Intent intent) {
        if (sendBroadcast(intent)) {
            executePendingBroadcasts();
        }
    }

    private void executePendingBroadcasts() {
        while (true) {
            final BroadcastRecord[] brs;
            synchronized (mReceivers) {
                final int N = mPendingBroadcasts.size();
                if (N <= 0) {
                    return;
                }
                brs = new BroadcastRecord[N];
                mPendingBroadcasts.toArray(brs);
                mPendingBroadcasts.clear();
            }
            for (int i=0; i<brs.length; i++) {
                final BroadcastRecord br = brs[i];
                final int nbr = br.receivers.size();
                for (int j=0; j<nbr; j++) {
                    final ReceiverRecord rec = br.receivers.get(j);
                    if (!rec.dead) {
                        rec.receiver.onReceive(mAppContext, br.intent);
                    }
                }
            }
        }
    }
}

LocalBroadcastManager 采用單例模式,從源頭上統(tǒng)一了廣播的發(fā)送和接收颊亮。從上面源碼可以看出柴梆,LocalBroadcastManager 創(chuàng)建了兩個(gè)內(nèi)部類ReceiverRecord 和BroadcastRecord 來記錄廣播接收者和廣播Intent對(duì)應(yīng)的實(shí)體,并且初始化了三個(gè)HashMap對(duì)象mReceivers终惑、mActions和mPendingBroadcasts來分別保存BroadcastReceiver與ReceiverRecord 的關(guān)系绍在、Action與ReceiverRecord 的關(guān)系以及待發(fā)送Intent內(nèi)容對(duì)應(yīng)的BroadcastRecord ;其中mReceivers在發(fā)送廣播邏輯中作用并不是特別大雹有,主要作為同步鎖和保存偿渡、移除數(shù)據(jù)用。
其實(shí)當(dāng)我們看到上面的HashMap時(shí)基本上就可以自己在大腦中有個(gè)基本的實(shí)現(xiàn)邏輯了霸奕,無非就是通過對(duì)mActions溜宽、mPendingBroadcasts集合進(jìn)行增刪改查,最終實(shí)現(xiàn)對(duì)應(yīng)的注冊(cè)铅祸、發(fā)送以及反注冊(cè)等操作坑质,事實(shí)上里面的邏輯也就是這些操作組合而成的合武。
接下來我們就按平時(shí)的接口調(diào)用順序來一一查看源碼:

registerReceiver方法

一般我們使用本地廣播首先就是先注冊(cè)临梗,而注冊(cè)方法registerReceiver的邏輯其實(shí)也很簡(jiǎn)單,無非就是往mReceivers和mActions添加預(yù)置關(guān)系稼跳,方便之后發(fā)送廣播時(shí)快速查找對(duì)應(yīng)的廣播接收者:

    public void registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
        synchronized (mReceivers) {
            ReceiverRecord entry = new ReceiverRecord(filter, receiver);
            ArrayList<ReceiverRecord> filters = mReceivers.get(receiver);
            if (filters == null) {
                filters = new ArrayList<>(1);
                mReceivers.put(receiver, filters);
            }
            filters.add(entry);
            for (int i=0; i<filter.countActions(); i++) {
                String action = filter.getAction(i);
                ArrayList<ReceiverRecord> entries = mActions.get(action);
                if (entries == null) {
                    entries = new ArrayList<ReceiverRecord>(1);
                    mActions.put(action, entries);
                }
                entries.add(entry);
            }
        }
    }
unregisterReceiver方法

有注冊(cè)就有反注冊(cè)盟庞,通常反注冊(cè)就是為了釋放相關(guān)對(duì)象的關(guān)聯(lián),減少相應(yīng)時(shí)間和內(nèi)存開銷汤善,避免產(chǎn)生內(nèi)存泄漏的問題什猖。而此處的反注冊(cè)也不例外,主要用來清除掉不再使用的廣播接收者相關(guān)信息红淡,

    public void unregisterReceiver(BroadcastReceiver receiver) {
        synchronized (mReceivers) {
            final ArrayList<ReceiverRecord> filters = mReceivers.remove(receiver);
            if (filters == null) {
                return;
            }
            for (int i=filters.size()-1; i>=0; i--) {
                final ReceiverRecord filter = filters.get(i);
                filter.dead = true;
                for (int j=0; j<filter.filter.countActions(); j++) {
                    final String action = filter.filter.getAction(j);
                    final ArrayList<ReceiverRecord> receivers = mActions.get(action);
                    if (receivers != null) {
                        for (int k=receivers.size()-1; k>=0; k--) {
                            final ReceiverRecord rec = receivers.get(k);
                            if (rec.receiver == receiver) {
                                rec.dead = true;
                                receivers.remove(k);
                            }
                        }
                        if (receivers.size() <= 0) {
                            mActions.remove(action);
                        }
                    }
                }
            }
        }
    }
sendBroadcast方法

整個(gè)流程中不狮,最重要的方法就是sendBroadcast,里面的流程是本地廣播的核心機(jī)制在旱,但其實(shí)里面的邏輯卻也很簡(jiǎn)單摇零!無非就是對(duì)mActions的“查”以及對(duì)mPendingBroadcasts的“增”和“查”。

    public boolean sendBroadcast(Intent intent) {
        synchronized (mReceivers) {
            final String action = intent.getAction();
            final String type = intent.resolveTypeIfNeeded(
                    mAppContext.getContentResolver());
            final Uri data = intent.getData();
            final String scheme = intent.getScheme();
            final Set<String> categories = intent.getCategories();

            final boolean debug = DEBUG ||
                    ((intent.getFlags() & Intent.FLAG_DEBUG_LOG_RESOLUTION) != 0);
            if (debug) Log.v(
                    TAG, "Resolving type " + type + " scheme " + scheme
                    + " of intent " + intent);

            ArrayList<ReceiverRecord> entries = mActions.get(intent.getAction());
            if (entries != null) {
                if (debug) Log.v(TAG, "Action list: " + entries);

                ArrayList<ReceiverRecord> receivers = null;
                for (int i=0; i<entries.size(); i++) {
                    ReceiverRecord receiver = entries.get(i);
                    if (debug) Log.v(TAG, "Matching against filter " + receiver.filter);

                    if (receiver.broadcasting) {
                        if (debug) {
                            Log.v(TAG, "  Filter's target already added");
                        }
                        continue;
                    }

                    int match = receiver.filter.match(action, type, scheme, data,
                            categories, "LocalBroadcastManager");
                    if (match >= 0) {
                        if (debug) Log.v(TAG, "  Filter matched!  match=0x" +
                                Integer.toHexString(match));
                        if (receivers == null) {
                            receivers = new ArrayList<ReceiverRecord>();
                        }
                        receivers.add(receiver);
                        receiver.broadcasting = true;
                    } else {
                        if (debug) {
                            String reason;
                            switch (match) {
                                case IntentFilter.NO_MATCH_ACTION: reason = "action"; break;
                                case IntentFilter.NO_MATCH_CATEGORY: reason = "category"; break;
                                case IntentFilter.NO_MATCH_DATA: reason = "data"; break;
                                case IntentFilter.NO_MATCH_TYPE: reason = "type"; break;
                                default: reason = "unknown reason"; break;
                            }
                            Log.v(TAG, "  Filter did not match: " + reason);
                        }
                    }
                }

                if (receivers != null) {
                    for (int i=0; i<receivers.size(); i++) {
                        receivers.get(i).broadcasting = false;
                    }
                    mPendingBroadcasts.add(new BroadcastRecord(intent, receivers));
                    if (!mHandler.hasMessages(MSG_EXEC_PENDING_BROADCASTS)) {
                        mHandler.sendEmptyMessage(MSG_EXEC_PENDING_BROADCASTS);
                    }
                    return true;
                }
            }
        }
        return false;
    }

閱讀源碼我們可以發(fā)現(xiàn)桶蝎,整個(gè)流程中首先從Intent拿到Action驻仅,然后從mActions中找到key為Action的廣播接收者列表谅畅,接著拿列表中的各個(gè)廣播接收者與intent中攜帶的相關(guān)信息進(jìn)行匹配,匹配成功則添加進(jìn)新的廣播接收者列表receivers 中噪服,最后用receivers生成BroadcastRecord對(duì)象并添加進(jìn)mPendingBroadcasts中毡泻,mHandler發(fā)送消息MSG_EXEC_PENDING_BROADCASTS觸發(fā)主線程執(zhí)行。

    private LocalBroadcastManager(Context context) {
        mAppContext = context;
        mHandler = new Handler(context.getMainLooper()) {

            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case MSG_EXEC_PENDING_BROADCASTS:
                        executePendingBroadcasts();
                        break;
                    default:
                        super.handleMessage(msg);
                }
            }
        };
    }
    private void executePendingBroadcasts() {
        while (true) {
            final BroadcastRecord[] brs;
            synchronized (mReceivers) {
                final int N = mPendingBroadcasts.size();
                if (N <= 0) {
                    return;
                }
                brs = new BroadcastRecord[N];
                mPendingBroadcasts.toArray(brs);
                mPendingBroadcasts.clear();
            }
            for (int i=0; i<brs.length; i++) {
                final BroadcastRecord br = brs[i];
                final int nbr = br.receivers.size();
                for (int j=0; j<nbr; j++) {
                    final ReceiverRecord rec = br.receivers.get(j);
                    if (!rec.dead) {
                        rec.receiver.onReceive(mAppContext, br.intent);
                    }
                }
            }
        }
    }

mHandler 收到消息后最終執(zhí)行到了executePendingBroadcasts方法粘优;然后你會(huì)很驚訝的發(fā)現(xiàn)仇味,這居然用了一個(gè)死循環(huán),當(dāng)然也添加了打斷條件(mPendingBroadcasts.size()為0的時(shí)候)雹顺。在同步鎖的情況下用mPendingBroadcasts生成了一個(gè)BroadcastRecord[]數(shù)組brs邪铲,然后清空了mPendingBroadcasts的數(shù)據(jù),這樣做的是為了避免直接操作mPendingBroadcasts的數(shù)據(jù)進(jìn)而造成數(shù)據(jù)混亂无拗,最后雙重遍歷brs找到receiver并回調(diào)onReceive方法带到。
到此本地廣播的源碼基本上已經(jīng)看完了,你會(huì)發(fā)現(xiàn)LocalBroadcastManager沒有涉及到靜態(tài)廣播的東西英染,所以靜態(tài)注冊(cè)的廣播接收者是接收不到本地廣播的揽惹;同時(shí)mHandler 的使用也證明了onReceive是跑在主線程的弯菊;本地廣播的onReceive是一個(gè)個(gè)串行跑的谊却,不存在并行的情況.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市递览,隨后出現(xiàn)的幾起案子闪金,更是在濱河造成了極大的恐慌疯溺,老刑警劉巖,帶你破解...
    沈念sama閱讀 210,914評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件哎垦,死亡現(xiàn)場(chǎng)離奇詭異囱嫩,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)漏设,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評(píng)論 2 383
  • 文/潘曉璐 我一進(jìn)店門墨闲,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人郑口,你說我怎么就攤上這事鸳碧。” “怎么了犬性?”我有些...
    開封第一講書人閱讀 156,531評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵瞻离,是天一觀的道長。 經(jīng)常有香客問我乒裆,道長套利,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,309評(píng)論 1 282
  • 正文 為了忘掉前任,我火速辦了婚禮日裙,結(jié)果婚禮上吹艇,老公的妹妹穿的比我還像新娘。我一直安慰自己昂拂,他們只是感情好受神,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,381評(píng)論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著格侯,像睡著了一般鼻听。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上联四,一...
    開封第一講書人閱讀 49,730評(píng)論 1 289
  • 那天撑碴,我揣著相機(jī)與錄音,去河邊找鬼朝墩。 笑死醉拓,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的收苏。 我是一名探鬼主播亿卤,決...
    沈念sama閱讀 38,882評(píng)論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼鹿霸!你這毒婦竟也來了排吴?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,643評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤懦鼠,失蹤者是張志新(化名)和其女友劉穎钻哩,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體肛冶,經(jīng)...
    沈念sama閱讀 44,095評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡街氢,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,448評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了淑趾。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片阳仔。...
    茶點(diǎn)故事閱讀 38,566評(píng)論 1 339
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖扣泊,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情嘶摊,我是刑警寧澤延蟹,帶...
    沈念sama閱讀 34,253評(píng)論 4 328
  • 正文 年R本政府宣布,位于F島的核電站叶堆,受9級(jí)特大地震影響阱飘,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,829評(píng)論 3 312
  • 文/蒙蒙 一沥匈、第九天 我趴在偏房一處隱蔽的房頂上張望蔗喂。 院中可真熱鬧,春花似錦高帖、人聲如沸缰儿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽乖阵。三九已至,卻和暖如春预麸,著一層夾襖步出監(jiān)牢的瞬間瞪浸,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評(píng)論 1 264
  • 我被黑心中介騙來泰國打工吏祸, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留对蒲,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,248評(píng)論 2 360
  • 正文 我出身青樓贡翘,卻偏偏與公主長得像齐蔽,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子床估,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,440評(píng)論 2 348