LocalBroadcastManager詳解

本文最早發佈在CSDN博客,地址:http://blog.csdn.net/hwliu51/article/details/74752250。 歡迎轉載,但請標明原文地址。java

###一 本地廣播與全局廣播區別android

Android文檔上LocalBroadcastManager的說明:設計模式

Helper to register for and send broadcasts of Intents to local objects within your process. This is has a number of advantages over sending global broadcasts with android.content.Context#sendBroadcast: You know that the data you are broadcasting won't leave your app, so don't need to worry about leaking private data. It is not possible for other applications to send these broadcasts to your app, so you don't need to worry about having security holes they can exploit. It is more efficient than sending a global broadcast through the system.安全

本地廣播與全局廣播相比較。優點:不會泄漏數據,不會因廣播而出現安全漏洞,發送和接收更高效。缺點:只能在應用內部使用。app

###二 主要的類和代碼介紹: ####1 LocalBroadcastManager 採用單例模式,一個應用進程只有實例。若是應用爲多進程,在其餘非主進程調用會生成新的對象。異步

//記錄廣播接收者和與之對應的廣播過濾器集合的map
	//一個BroadcastReceiver對應多個IntentFilter
    private final HashMap<BroadcastReceiver, ArrayList<IntentFilter>> mReceivers
            = new HashMap<BroadcastReceiver, ArrayList<IntentFilter>>();
    //廣播消息action和接收該Action的廣播接收者集合的map
    //一個action對應多個ReceiverRecord
    private final HashMap<String, ArrayList<ReceiverRecord>> mActions
            = new HashMap<String, ArrayList<ReceiverRecord>>();

	//記錄待處理的廣播消息和對應接收者的集合
    private final ArrayList<BroadcastRecord> mPendingBroadcasts
            = new ArrayList<BroadcastRecord>();

	//消息code,用於異步處理mPendingBroadcasts的消息
    static final int MSG_EXEC_PENDING_BROADCASTS = 1;
    //用於發送和在主線程處理mPendingBroadcasts
    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) {
            	//使用的是Application的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);
                }
            }
        };
    }

####2 私有的內部類ReceiverRecord 廣播接收者記錄類:記錄廣播過濾器IntentFilter和註冊的BroadcastReceiver廣播接收對象。ide

private static class ReceiverRecord {
		//廣播過濾器
        final IntentFilter filter;
        //廣播接收對象
        final BroadcastReceiver receiver;
        //是否被記錄處處理廣播的標誌,以防止重複記錄。
        boolean broadcasting;

        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);
            builder.append("}");
            return builder.toString();
        }
    }

3 私有的內部類ReceiverRecord

廣播記錄類:記錄廣播消息Intent和接收該廣播消息的ReceiverRecord的集合。oop

private static class BroadcastRecord {
		//帶有廣播消息的Intent
        final Intent intent;
        //接收intent的消息的廣播接收者的集合
        final ArrayList<ReceiverRecord> receivers;

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

###三廣播流程介紹ui

1 註冊本地廣播

經過靜態方法getInstance獲取到LocalBroadcastManager對象,而後調用registerReceiver,傳入BroadcastReceiver和IntentFilter參數註冊廣播。.net

LocalBroadcastManager#registerReceiver代碼:

public void registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
        synchronized (mReceivers) {
        	//建立廣播接收者記錄對象,記錄filter和receiver
            ReceiverRecord entry = new ReceiverRecord(filter, receiver);
            //獲取receiver以前註冊的IntentFilter對象的集合
            ArrayList<IntentFilter> filters = mReceivers.get(receiver);
            if (filters == null) {
            	//filters爲null,說明receiver沒有被註冊過。
            	//建立IntenFilter集合filters,將receiver和filter記錄到mReceiver中
                filters = new ArrayList<IntentFilter>(1);
                mReceivers.put(receiver, filters);
            }
            //記錄filter
            filters.add(filter);
            //遍歷Action,將其與接收該Action的ReceiverRecord關聯
            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);
            }
        }
    }

####2 發送本地廣播 發送有兩種方式,調用sendBroadcast或sendBroadcastSync方法。sendBroadcast方法不會當即處理廣播,而是經過mHandler發送一個MSG_EXEC_PENDING_BROADCASTS的空消,而後在主線程異步處理。而sendBroadcastSync在調用時便處理廣播,即同步處理。所以sendBroadcastSync不能在子線程中調用。

先看看LocalBroadcastManager#sendBroadcast代碼:

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);

			//從mActions中獲取接收該action的廣播接收者記錄集合
            ArrayList<ReceiverRecord> entries = mActions.get(intent.getAction());
            //若是entries不爲null,則有接收該Action的廣播註冊。
            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);
					//broadcasting爲true,則說明該receiver已被記錄到receivers
                    if (receiver.broadcasting) {
                        if (debug) {
                            Log.v(TAG, "  Filter's target already added");
                        }
                        continue;
                    }

					//匹配IntentFilter
                    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
                        receiver.broadcasting = true;//標記爲已記錄
                    } else {
                    	//debug信息
                        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));
                    //若是沒有正在處理mPendingBroadcasts,則放送異步處理消息
                    if (!mHandler.hasMessages(MSG_EXEC_PENDING_BROADCASTS)) {
                        mHandler.sendEmptyMessage(MSG_EXEC_PENDING_BROADCASTS);
                    }
                    //發送成功
                    return true;
                }
            }
        }
        //發送失敗
        return false;
    }

LocalBroadcastManager#sendBroadcastSync代碼:

public void sendBroadcastSync(Intent intent) {
   		//經過sendBroadcast將接收廣播的receiver找出並封裝爲BroadcastRecord記錄到
   		//mPendingBroadcasts,而後調用executePendingBroadcasts當即處理
        if (sendBroadcast(intent)) {
            executePendingBroadcasts();
        }
    }

####3 反註冊本地廣播 調用unregisterReceiver註冊 LocalBroadcastManager#unregisterReceiver代碼:

public void unregisterReceiver(BroadcastReceiver receiver) {
        synchronized (mReceivers) {
        	//將receiver從mReceiver移除,並返回相關的IntentFilter集合
            ArrayList<IntentFilter> filters = mReceivers.remove(receiver);
            if (filters == null) {
                return;
            }
            //遍歷IntentFilter集合
            for (int i=0; i<filters.size(); i++) {
                IntentFilter filter = filters.get(i);
                //遍歷Action
                for (int j=0; j<filter.countActions(); j++) {
                    String action = filter.getAction(j);
                    //獲取action關聯的ReceiverRecord集合
                    ArrayList<ReceiverRecord> receivers = mActions.get(action);
                    if (receivers != null) {
                        for (int k=0; k<receivers.size(); k++) {
                        	//若是相同,則將其刪除
                            if (receivers.get(k).receiver == receiver) {
                                receivers.remove(k);
                                k--;
                            }
                        }
                        //receivers爲空,則說明與action關聯的ReceiverRecord已全被移除
                        //即已無ReceiverRecord與action關聯,則將action從mActions刪除
                        if (receivers.size() <= 0) {
                            mActions.remove(action);
                        }
                    }
                }
            }
        }
    }

從mReceivers中刪除receiver,到遍歷filters和action來刪除mActions中引用,從而完全刪除對receiver的引用。這樣就能夠防止應註冊本地廣播而致使的內存泄漏。

###四 回顧上述分析,再來比較普通廣播與本地廣播 從以上的代碼和流程分析來看本地廣播的優勢: 1.安全:本地廣播所發出的Intent只在應用內部傳播,即便其餘應用註冊了相同的action也沒法接收到廣播消息。而全局廣播則會傳播給全部註冊相同action的應用,從而致使數據泄漏。 2.高效a 本地廣播大概流程 註冊 LocalBroadcastManager#registerReceiver; 反註冊 LocalBroadcastManager#unRegisterReceiver; 發送處理(異步) LocalBroadcastManager#sendBroadcast =》 mHandler#handleMessage =》BroadCastReceiver ; 發送處理(同步) LocalBroadcastManager#sendBroadcastSync =》BroadCastReceiver;

b 普通廣播大概流程 註冊 ContextImpl#registerReceiver =>IBinder => ActivityManagerService#registerReceiver 反註冊 ContextImpl#unRegisterReceiver =>IBinder => ActivityManagerService#unRegisterReceiver 發送處理(只有異步) ContextImpl#sendBroadcast =>IBinder => ActivityManagerService#sendBroadcast=》ApplicationThread#scheduleReceiver =》H#handleMessage =》ActivityThread#handleReceiver =》BroadCastReceiver

本地廣播的數據基本只在LocalBroadcastManager類和BroadcastReceiver內部流通。而普通廣播則複雜的多。

本地廣播的缺點也很明顯,就是不可以跨進程傳播。

###五 本地廣播與EventBus 兩者大概的設計思路比較類似。 LocalBroadcastManager採用了單例設計模式。註冊時,使用集合容器來記錄BroadcastReceiver和action。發送廣播事消息時,從集合容器中查找BroadcastReceiver對象,而後將消息和BroadcastReceiver對象集合封裝到BroadcastRecord對象,並加入處處理的集合中,再異步或同步處理給BroadcastReceiver。能夠將mPendingBroadcasts看做爲一個待處理的消息隊列。

EventBus也使用了單例模式。註冊時,經過反射獲取須要調用的方法和參數消息以及其餘配置信息,而後分別存入到相關的集合容器中。發送消息時,依據發送和配置的策略採起同步或異步處理。異步消息將加入到隊列中,在Handler中遍歷獲取,根據消息的類型遍歷獲取被調用的方法而後傳遞消息。

相關文章
相關標籤/搜索