1. 程式人生 > >Android Broadcast原理分析之LocalBroadcast(三)

Android Broadcast原理分析之LocalBroadcast(三)

目錄

  • LocalBroadcastManager簡介
  • LocalBroadcastManager使用
  • 原始碼解析
  • 總結

1. LocalBroadcastManager簡介

前面的文章介紹了常用的Broadcast原理,這種廣播是經過系統排程的,不論是動態註冊還是靜態註冊,不論是有序廣播還是無序廣播,都要經過ams,好處很顯然,可以實現跨程序之間的通訊,而且有很多廣播本身就是由系統發出的,我們需要在APP的程序中監聽,但是也是有壞處的,因為AMS其實是很繁忙的,如果出現比較特殊的情況導致AMS被阻塞,就可能會影響我們自身收到receiver的時間,甚至收不到,這樣就可能會影響到APP自身的業務。 Android中也是考慮到了這種情況,因此在v4包中引入了LocalBroadcastManager,顧名思義,叫做本地廣播,也就是隻在APP的自身程序中執行,不會被其他情況干擾,當然這個不能用作跨程序通訊的手段。

2. LocalBroadcastManager使用

核心類就是LocalBroadcastManager,這個類的方法並不多,但是也涵蓋了常用的registerReceiver,unregisterReceiver,sendBroadcast等方法。 先來看看其中主要都有那些方法:

單例模式獲取LocalBroadcastManager public static LocalBroadcastManager getInstance(Context context);

廣播註冊 public void registerReceiver(BroadcastReceiver receiver, IntentFilter filter);

取消註冊 public void unregisterReceiver(BroadcastReceiver receiver);

傳送廣播 public boolean sendBroadcast(Intent intent);

同步傳送廣播 public void sendBroadcastSync(Intent intent);

執行廣播派發 private void executePendingBroadcasts();

可以看到,和平時使用的廣播沒有太大區別,不過需要通過getInstance獲取一個單例之後操作,其中有一個方法看起來沒有見過,executePendingBroadcasts

,而且是一個private方法,接下來從原始碼看一下這個LocalBroadcast的實現。

3. 原始碼解析

3.1 getInstance

    private static LocalBroadcastManager mInstance;

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

通過ApplicationContext建立單例。

3.2 registerReceiver

    public void registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
        // mReceivers是一個hashmap
        // key是BroadcastReceiver,value是receiverRecord的list
        synchronized (mReceivers) {
            //  根據filter和receiver建立ReceiverRecord
            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);
                // mActions同樣是一個hashmap
                // key是廣播action,value是ReceiverRecord的list
                ArrayList<ReceiverRecord> entries = mActions.get(action);
                if (entries == null) {
                    entries = new ArrayList<ReceiverRecord>(1);
                    mActions.put(action, entries);
                }
                entries.add(entry);
            }
        }
    }

ReceiverRecord中就只存了broadcastReceiver以及intentFilter 這段邏輯並不複雜,主要實現以下三步:

  1. 根據receiver和filter建立ReceiverRecord
  2. 將上面建立的record放入到mReceivers的value的list中
  3. 對本次廣播的filter中所有action,在mAction的value中分別放入ReceiverRecord物件 經過註冊最終的結果是這樣: mReceivers中存放的是已註冊的receiver以及其對應的ReceiverRecord,同一個receiver如果多次用相同的filter不會生效多次,但是如果filter不同註冊多次,那麼就會在mReceivers儲存多個ReceiverRecord; mAction中存放的是已註冊的所有action,以及這個action對應的ReceiverRecord,如果這個filter中有多個action,那麼就會儲存多個相同的ReceiverRecord。

3.3 unregisterReceiver

    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);
                        }
                    }
                }
            }
        }
    }
  1. 取消註冊的時候,因為mReceivers的key是receiver,直接把value移除即可。
  2. 從移除的value(ReceiverRecord)的list中遍歷,分別拿出其中的filter中的action,前面兩步主要是為了拿到本次取消註冊的所有action,拿到action後,遍歷所有的action,根據action去mActions拿到這個action對應的所有的ReceiverRecord物件列表,把這個列表中的receiver和要移除的receiver相等的ReceiverRecord移除。

3.4 sendBroadcast

    public boolean sendBroadcast(Intent intent) {
        synchronized (mReceivers) {
            final String action = intent.getAction();
            // 根據指定的schema獲取intent的type
            final String type = intent.resolveTypeIfNeeded(
                    mAppContext.getContentResolver());
            // 獲取uri
            final Uri data = intent.getData();
            final String scheme = intent.getScheme();
            final Set<String> categories = intent.getCategories();
            // 根據action,拿到所有註冊了監聽這個action的ReceiverRecord
            ArrayList<ReceiverRecord> entries = mActions.get(intent.getAction());
            if (entries != null) {
                ArrayList<ReceiverRecord> receivers = null;
                for (int i=0; i<entries.size(); i++) {
                    ReceiverRecord receiver = entries.get(i);
                    // 因為不同的action中,可能有相同的ReceiverRecord,需要判斷狀態
                    // 如果正在傳送了就下一個
                    if (receiver.broadcasting) {
                        continue;
                    }
                    // 根據ReceiverRecord中的record匹配
                    // 匹配成功之後,就加入到要派發的列表中
                    int match = receiver.filter.match(action, type, scheme, data,
                            categories, "LocalBroadcastManager");
                    if (match >= 0) {
                        if (receivers == null) {
                            receivers = new ArrayList<ReceiverRecord>();
                        }
                        receivers.add(receiver);
                        receiver.broadcasting = true;
                    }
                }
                // 到這裡就找到了所有的監聽這個action的receiver

                if (receivers != null) {
                    for (int i=0; i<receivers.size(); i++) {
                        receivers.get(i).broadcasting = false;
                    }
                    // 把要派發的廣播加入到mPendingBroadcasts中
                    mPendingBroadcasts.add(new BroadcastRecord(intent, receivers));
                    // 傳送message
                    if (!mHandler.hasMessages(MSG_EXEC_PENDING_BROADCASTS)) {
                        mHandler.sendEmptyMessage(MSG_EXEC_PENDING_BROADCASTS);
                    }
                    return true;
                }
            }
        }
        return false;
    }

收集需要派發的receiver,並post訊息到主執行緒

3.5 handleMessage

    private LocalBroadcastManager(Context context) {
        mAppContext = context;
        // 可以看到這裡用的是主執行緒的handler
        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方法,看來這個方法是派發的核心了。

3.6 executePendingBroadcasts

    private void executePendingBroadcasts() {
        // 死迴圈,直到廣播都派發完畢才return
        while (true) {
            final BroadcastRecord[] brs;
            synchronized (mReceivers) {
                final int N = mPendingBroadcasts.size();
                // 沒有需要派發的廣播那就return了
                if (N <= 0) {
                    return;
                }
                brs = new BroadcastRecord[N];
                mPendingBroadcasts.toArray(brs);
                mPendingBroadcasts.clear();
            }
            // 便利了所有的receiver之後,回調了receiver的onReceive
            // mAppContext正是建立單例時的context
            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);
                    }
                }
            }
        }
    }

果然,真正的廣播派發以及receiver的回撥就在這裡。 前面有提到還有一個方法sendBroadcastSync

3.7 sendBroadcastSync

    public void sendBroadcastSync(Intent intent) {
        if (sendBroadcast(intent)) {
            executePendingBroadcasts();
        }
    }

從名字上看意思應該是同步傳送廣播,事實上正是這樣,在呼叫了sendBroadcast之後,其中是經過了向主執行緒post訊息,然後主執行緒去處理receiver的,而這裡sendBroadcast返回值為true就代表收集到了receiver,直接在當前執行緒開始排程了。因為在executePendingBroadcasts方法中是持鎖派發,而且有對需要派發廣播的數量校驗,因此並不需要擔心廣播會被派發多次。

總結

  1. 依舊是經典的觀察者模式
  2. 註冊的時候通過mReceivers/mActions兩個map從不同的維度儲存了已註冊的receiver
  3. 傳送廣播之後根據action以及filter的匹配,查詢需要派發的receiver
  4. 正常流程是post訊息到主執行緒派發
  5. 可以選擇在當前執行緒同步進行廣播派發