LocalBroadcastManager源碼剖析

Handler系列文章:java

        BroadcastReceiver做爲android四大組件之一經常被人使用。但不管是普通廣播仍是有序廣播,都是系統全局廣播。即發送的廣播能夠被任何應用程序接收到,但同時咱們也能接收來自任何應用程序的廣播。android

        爲了解決廣播安全性的問題,Google引入了一套本地廣播機制。使用本地廣播機制發送的廣播只能在應用中進行傳遞,而廣播接收器也只能接收來自應用內的廣播。數組

LocalBroadcast簡單使用

        本地廣播的使用和普通廣播基本沒區別,主要是使用LocalBroadcastManager進行廣播發送、註冊廣播接收器和註銷廣播接收器。安全

#daqiActivity.java
//本地廣播管理器
private LocalBroadcastManager mBroadcastManager;
//廣播接收器
private daqiBroadcastReceiver mBroadcastReceiver;

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    //.....
    
    //獲取廣播管理類實例
    mBroadcastManager = LocalBroadcastManager.getInstance(daqiActivity.this);
    //初始化intent攔截器
    IntentFilter mIntentFilter = new IntentFilter();
    mIntentFilter.addAction("com.daqi.demo.LOCAL_BROADCAST");
    //初始化廣播接收器
    mBroadcastReceiver = new daqiBroadcastReceiver();
    //註冊廣播接收器
    mBroadcastManager.registerReceiver(mBroadcastReceiver,mIntentFilter);

    mButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Intent intent = new Intent("com.daqi.demo.LOCAL_BROADCAST");
            //發送本地廣播
            mBroadcastManager.sendBroadcast(intent);
        }
    });
}

@Override
protected void onDestroy() {
    super.onDestroy();
    //註銷廣播接收器
    mBroadcastManager.unregisterReceiver(mBroadcastReceiver);
}

private class daqiBroadcastReceiver extends BroadcastReceiver{
    @Override
    public void onReceive(Context context, Intent intent) {
        Toast.makeText(daqiActivity.this,"本地廣播",Toast.LENGTH_SHORT).show();
    }
}
複製代碼

LocalBroadcastManager源碼分析

        從LocalBroadcastManager.getInstance(this)獲取實例能夠看出,LocalBroadcastManager是一個單例管理類。bash

查看LocalBroadcastManager#getInstance()的源碼並沒有異樣,繼續查看構造方法:ide

#LocalBroadcastManager.java
@NonNull
public static LocalBroadcastManager getInstance(@NonNull Context context) {
    synchronized (mLock) {
        if (mInstance == null) {
            //獲取ApplicationContext的context
            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);
            }
        }
    };
}
複製代碼

LocalBroadcastManager的構造函數只作了兩件事:函數

        一、存儲ApplicationContext的Context。oop

        二、初始化一個Handler,並將其綁定在主線程。源碼分析

LocalBroadcastManager竟然初始化了一個Handler,莫非本地廣播也是經過Handler實現的?post

LocalBroadcastManager內部類

        在查看註冊廣播監聽器、註銷廣播監聽器和發送本地廣播的源碼以前,須要先了解LocalBroadcastManager得兩個內部類ReceiverRecord 和 BroadcastRecord。

#LocalBroadcastManager.java
//接收器記錄
private static final class ReceiverRecord {
    final IntentFilter filter;
    final BroadcastReceiver receiver;
    //標記是否正在處於廣播狀態
    boolean broadcasting;
    //標記該接收器記錄是否有效
    //false爲有效,true爲無效。
    boolean dead;

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

    //....
}

//廣播器記錄
private static final class BroadcastRecord {
    //發送廣播時的Intent對象
    final Intent intent;
    //接收器記錄列表
    final ArrayList<ReceiverRecord> receivers;

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

        從名字上ReceiverRecord-接收器記錄,BroadcastRecord-廣播器記錄,能夠很好的理解各種中變量的意義。

        ReceiverRecord主要用於存儲註冊廣播接收器時,存儲註冊廣播時傳遞的參數:IntentFilter 和 BroadcastReceiver。

        BroadcastRecord主要存儲廣播的intent和存儲IntentFilter符合該intent.action的ReceiverRecord對象。

LocalBroadcastManager內部集合

#LocalBroadcastManager.java

//存儲廣播接收器和對應接收器記錄列表(ArrayList<LocalBroadcastManager.ReceiverRecord>)
//在以前的版本這裏value存儲的是ArrayList<IntentFilter>
//LocalBroadcastManager.ReceiverRecord中存有IntentFilter,因此存儲ArrayList<LocalBroadcastManager.ReceiverRecord>和ArrayList<IntentFilter>道理都同樣
private final HashMap<BroadcastReceiver, ArrayList<LocalBroadcastManager.ReceiverRecord>> mReceivers = new HashMap();

//String存儲action,表示一個action對應多個接收該action的接收器記錄(ReceiverRecord)
//(ReceiverRecord內部存儲廣播接收器和過濾規則)
private final HashMap<String, ArrayList<LocalBroadcastManager.ReceiverRecord>> mActions = new HashMap();

//與發送的廣播的Action匹配的ReceiverRecord集合
//即存儲了廣播接收器、廣播過濾規則 和 發送廣播時的Intent
private final ArrayList<LocalBroadcastManager.BroadcastRecord> mPendingBroadcasts = new ArrayList();
複製代碼

LocalBroadcastManager定義了3個集合,簡單的說就是:

  • mReceivers    存儲廣播接收器(BroadcastReceiver)對象 和 其IntentFilter信息。
  • mActions    存儲單個action 和 監聽該action的接收器記錄(ReceiverRecord)列表
  • mPendingBroadcasts    存儲發送廣播時攜帶的intent對象 和 符合該intent.action的IntentFilter。

若是看不懂,暫時放下,配合下面的源碼分析食用。

分別從廣播機制的註冊廣播監聽器、發送廣播、註銷廣播監聽器入手查看源碼。 先從註冊廣播監聽器入手:

registerReceiver()

#LocalBroadcastManager.java

public void registerReceiver(@NonNull BroadcastReceiver receiver,
        @NonNull IntentFilter filter) {
    //加鎖
    synchronized (mReceivers) {
        //傳遞進來的receiver廣播接收器與filter廣播攔截器會被封裝在ReceiverRecord對象中。
        ReceiverRecord entry = new ReceiverRecord(filter, receiver);
        //從mReceivers中獲取有關該receiver廣播接收器的接收器記錄(ReceiverRecord)對象
        ArrayList<ReceiverRecord> filters = mReceivers.get(receiver);
        //若是沒有獲取到對應的記錄(正常是null)
        if (filters == null) {
            //建立一個size爲1的ArrayList,並將信息存儲進去。
            filters = new ArrayList<>(1);
            mReceivers.put(receiver, filters);
        }
        //將存儲註冊廣播信息的內容存儲到filters中
        filters.add(entry);
        //遍歷註冊時傳遞進來的IntentFilter的action列表
        for (int i=0; i<filter.countActions(); i++) {
            //獲取action
            String action = filter.getAction(i);
            //獲取特定action對應的接收器記錄
            ArrayList<ReceiverRecord> entries = mActions.get(action);
            //若是本來沒有這個條action的記錄
            if (entries == null) {
                //則建立一個存進去
                entries = new ArrayList<ReceiverRecord>(1);
                mActions.put(action, entries);
            }
            //將此次註冊廣播監聽器生成的接收器記錄對象存儲到該action對應的接收器記錄(ReceiverRecord)器列表中。
            entries.add(entry);
        }
    }
}
複製代碼

總的來講,LocalBroadcastManager#registerReceiver()作了三件事:

  • 將註冊廣播監聽器的信息:IntentFilter 和 BroadcastReceiver。封裝在ReceiverRecord中。
  • 爲廣播接收器添加IntentFilter,存儲在mReceivers中。
  • 爲註冊時IntentFilter中的action添加接收器記錄(ReceiverRecord),存儲在mActions中。

unregisterReceiver()

再查看註銷廣播接收器的源碼:

#LocalBroadcastManager.java

public void unregisterReceiver(@NonNull BroadcastReceiver receiver) {
    //加鎖
    synchronized (mReceivers) {
        //移除該廣播接收器的IntentFilter規則
        final ArrayList<ReceiverRecord> filters = mReceivers.remove(receiver);
        if (filters == null) {
            return;
        }
        //遍歷其存儲的全部IntentFilter
        for (int i=filters.size()-1; i>=0; i--) {
            //取出其中一個ReceiverRecord對象,實際上是獲取其對應的IntentFilter
            final ReceiverRecord filter = filters.get(i);
            //將該接收器記錄標誌爲失效(死亡)
            filter.dead = true;
            //遍歷這個IntentFilter過濾規則的action
            for (int j=0; j<filter.filter.countActions(); j++) {
                //獲取其中一個action
                final String action = filter.filter.getAction(j);
                //依據該action獲取對應的ReceiverRecord列表,即獲取其對應的廣播接收器(BroadcastReceiver)列表
                final ArrayList<ReceiverRecord> receivers = mActions.get(action);
                //若是該action的接收器記錄(ReceiverRecord)列表不爲空
                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);
                        }
                    }
                    //若是接收器記錄(ReceiverRecord)列表移除後爲空,即該action無對應的廣播接收器,則將該action移除。
                    if (receivers.size() <= 0) {
                        mActions.remove(action);
                    }
                }
            }
        }
    }
}
複製代碼

註銷廣播接收器和註冊廣播接收器作了相反的事情:

  • 在mReceivers中,移除廣播接收器和其IntentFilter。
  • 在mActions中,移除IntentFilter#action對應的接收器記錄(ReceiverRecord),若action無對應的接收器記錄(ReceiverRecord),則將action移除。

sendBroadcast()

#LocalBroadcastManager.java

public boolean sendBroadcast(@NonNull Intent intent) {
    synchronized (mReceivers) {
        //獲取一堆intent的參數
        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();
            
        //根據intent對象的action獲取其對應的接收器記錄(ReceiverRecord)列表
        ArrayList<ReceiverRecord> entries = mActions.get(intent.getAction());
        //若是該action的接收器記錄(ReceiverRecord)列表不爲空
        if (entries != null) {
           //..
           
            //這個列表用來記錄實際用來接收該廣播的廣播接收器(BroadcastReceiver)
            ArrayList<ReceiverRecord> receivers = null;
            for (int i=0; i<entries.size(); i++) {
                //獲取單個接收器記錄,即獲取單個廣播接收器
                ReceiverRecord receiver = entries.get(i);
                //..
                
                //若是當前廣播接收器正在廣播,則先跳過,防止重複註冊
                if (receiver.broadcasting) {
                    //..
                    continue;
                }
                
                //測試此過濾器是否與給定的意圖數據匹配。 只有當Intent中的操做和類別與過濾器匹配時,匹配纔會成功
                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;
                } else {
                   //..
                }
            }
            
            //最後接受廣播的廣播接收器列表不爲空,則將intent對象傳遞進去。
            if (receivers != null) {
                for (int i=0; i<receivers.size(); i++) {
                    //恢復不是正在廣播的狀態
                    receivers.get(i).broadcasting = false;
                }
                //將該廣播的intent對象和廣播接收器列表封裝成接收器記錄,存儲在mPendingBroadcasts中。
                mPendingBroadcasts.add(new BroadcastRecord(intent, receivers));
                if (!mHandler.hasMessages(MSG_EXEC_PENDING_BROADCASTS)) {
                   //發送特定what的Message,交由Handler處理。 mHandler.sendEmptyMessage(MSG_EXEC_PENDING_BROADCASTS);
                }
                return true;
            }
        }
    }
    return false;
}
複製代碼

sendBroadcast()作了4個步驟:

        一、獲取廣播的intent信息,用於後續的匹對。

        二、經過廣播的action在mActions中獲取其對應的接收器記錄(ReceiverRecord)列表。

        三、遍歷接收器記錄(ReceiverRecord)列表,將列表內元素的IntentFilter對象與廣播的intent對象進行匹對,若是匹對成功,則存儲在新的接收器記錄(ReceiverRecord)列表receivers中。

        四、將新的接收器記錄(ReceiverRecord)列表receivers 和 廣播的intent對象存儲在廣播記錄(BroadcastRecord)對象中,並將該廣播記錄(BroadcastRecord)對象存儲在mPendingBroadcasts中。

        五、發送what = MSG_EXEC_PENDING_BROADCASTS的Message,讓Handler傳遞廣播intent對象到對應的接收器中。

注:對列表receivers存儲接收器記錄過程當中,將接收器記錄中的broadcasting標記爲true,能夠防止重複註冊廣播接收器形成的屢次廣播。

重歸Handler的handleMessage方法中:

#LocalBroadcastManager.java
@Override
public void handleMessage(Message msg) {
    switch (msg.what) {
        case MSG_EXEC_PENDING_BROADCASTS:
            executePendingBroadcasts();
            break;
        default:
            super.handleMessage(msg);
    }
}
複製代碼

what = MSG_EXEC_PENDING_BROADCASTS的Message,會調用executePendingBroadcasts進行處理。

#LocalBroadcastManager.java
void executePendingBroadcasts() {
    while (true) {
        final BroadcastRecord[] brs;
        synchronized (mReceivers) {
            //獲取待廣播的廣播記錄對象。
            final int N = mPendingBroadcasts.size();
            if (N <= 0) {
                return;
            }mPendingBroadcasts
            //將廣播記錄BroadcastRecord對象存儲在數組中,並清空mPendingBroadcasts對象。
            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);
                //廣播接收器列表沒有被標記失效,則進行onReceive()回調進行廣播。
                if (!rec.dead) {
                    rec.receiver.onReceive(mAppContext, br.intent);
                }
            }
        }
    }
}
複製代碼

        executePendingBroadcasts()將mPendingBroadcasts轉換爲數組,遍歷廣播記錄數組。對還生效的接收器對象進行onReceive()回調,進行廣播。

注:當廣播接收器移除時,ReceiverRecord#dead會被標記爲true,則標記爲無效。

再回看3個列表對象:

  • mReceivers    存儲廣播接收器(BroadcastReceiver)對象 和 其IntentFilter信息。
    做用:註銷廣播接收器時,經過廣播接收器對象,尋找到其IntentFilter列表信息,方便根據IntentFilter列表中的action信息在mActions中進行移除。
  • mActions    存儲單個action 和 監聽該action的接收器記錄(ReceiverRecord)列表。
    做用:發送廣播時,根據廣播中intent對象的action,能夠快速尋找到其對應接收的接收記錄對象,再根據接收器對象的IntentFilter與廣播中intent對象作匹配,尋找符合該廣播intent對象條件的廣播接收器對象。
  • mPendingBroadcasts    存儲發送廣播時攜帶的intent對象 和 符合該intent.action的IntentFilter。
    做用:短暫存儲須要傳遞的廣播intent對象和接收的該intent對象廣播接收器。

總結:

        本地廣播其實也是使用handler機制,將廣播接收器與action、IntentFilter相關聯。由於Handler的緣由,本地廣播也就只能在該應用內接收。

相關文章
相關標籤/搜索