Source code analysis of Android 's LocalBroadcastManager

1, briefly

In androcchio, broadcasting is usually divided into general broadcasting, orderly broadcasting, sticky broadcasting and local broadcasting. Compared with local broadcasting, local broadcasting has the following characteristics

  • Security: local broadcast can only be sent and received in its APP, and other applications can't get and receive it;
  • High efficiency: high efficiency of local broadcast transmission and reception;
  • Simple: there is no static registration method, and its encapsulated registration, sending and cancellation methods are used.

2. Easy to use

  • Get object: get the local broadcast object through the local broadcast manager;
mLocalBroadcastManager = LocalBroadcastManager.getInstance(this);
  • Register broadcast: add the custom Action to the IntentFilter;
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("com.xiaohan.LocalBroadcast");
mLocalBroadcastManager.registerReceiver(mLocalBroadcastReceiver,intentFilter);
  • Send broadcast: call sendBroadcast encapsulated in local broadcast manager to send broadcast information;
Intent intent = new Intent();
 intent.setAction("com.xiaohan.LocalBroadcast");
intent.putExtra("test",10);
mLocalBroadcastManager.sendBroadcast(intent);
  • Unregister: call unregisterReceiver encapsulated in local broadcast manager to unregister
mLocalBroadcastManager.unregisterReceiver(mLocalBroadcastReceiver);

3. Source code analysis

The idea of source code analysis can be based on the characteristics of safety, efficiency and simplicity mentioned in the first part.
Construction method
Looking at the construction method of the source code, we can see that it is a private decoration, indicating that it adopts the singleton mode, and its internal is maintained by a Handler. When creating the Handler, context.getMainLooper() is added, so the local broadcast runs in the main thread.

private LocalBroadcastManager(Context context) {
        this.mAppContext = context;
        this.mHandler = new Handler(context.getMainLooper()) {
            public void handleMessage(Message msg) {
                switch(msg.what) {
                case 1:
                    LocalBroadcastManager.this.executePendingBroadcasts();
                    break;
                default:
                    super.handleMessage(msg);
                }

            }
        };
    }

Key set

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

The local broadcast manager is maintained through the above three key collections, with the following meanings:

  • mReceivers: the key is the BroadcastReceiver, and the value is the ArrayList < localbroadcastmanager. ReceiverRecord > collection. The construction parameters of ReceiverRecord are IntentFilter and BroadcastReceiver. This is because the same BroadcastReceiver can contain multiple IntentFilter objects and receive multiple actions;
  • Mcactions: its key is action, and its value is ArrayList < localbroadcastmanager. Receiverrecord >. It contains the action collection information of all local broadcasts. It is added during registration, traversed during sending, and deleted upon release;
  • mPendingBroadcasts: it contains the aggregate information of all the local broadcast's broadcastreceivers and actions

Registered broadcasting

 public void registerReceiver(@NonNull BroadcastReceiver receiver, @NonNull IntentFilter filter) {
        HashMap var3 = this.mReceivers;
        synchronized(this.mReceivers) {
        //1. Broadcast add: check whether the set contains Registered broadcast information, such as not included in the mReceivers
            LocalBroadcastManager.ReceiverRecord entry = new LocalBroadcastManager.ReceiverRecord(filter, receiver);
            ArrayList<LocalBroadcastManager.ReceiverRecord> filters = (ArrayList)this.mReceivers.get(receiver);
            if (filters == null) {
                filters = new ArrayList(1);
                this.mReceivers.put(receiver, filters);
            }
            filters.add(entry);
	//2. Action add: find whether the added action is included in the collection and not included in the mpactions
            for(int i = 0; i < filter.countActions(); ++i) {
                String action = filter.getAction(i);
                ArrayList<LocalBroadcastManager.ReceiverRecord> entries = (ArrayList)this.mActions.get(action);
                if (entries == null) {
                    entries = new ArrayList(1);
                    this.mActions.put(action, entries);
                }

                entries.add(entry);
            }
        }
    }

Cancellation of registration

public void unregisterReceiver(@NonNull BroadcastReceiver receiver) {
        HashMap var2 = this.mReceivers;
        synchronized(this.mReceivers) {
        //1. Remove broadcast information
            ArrayList<LocalBroadcastManager.ReceiverRecord> filters = (ArrayList)this.mReceivers.remove(receiver);
          //2. If the IntentFilter is not empty, remove the Action information corresponding to the broadcast and change its dead property to true
            if (filters != null) {
                for(int i = filters.size() - 1; i >= 0; --i) {
                    LocalBroadcastManager.ReceiverRecord filter = (LocalBroadcastManager.ReceiverRecord)filters.get(i);
                    filter.dead = true;

                    for(int j = 0; j < filter.filter.countActions(); ++j) {
                        String action = filter.filter.getAction(j);
                        ArrayList<LocalBroadcastManager.ReceiverRecord> receivers = (ArrayList)this.mActions.get(action);
                        if (receivers != null) {
                            for(int k = receivers.size() - 1; k >= 0; --k) {
                                LocalBroadcastManager.ReceiverRecord rec = (LocalBroadcastManager.ReceiverRecord)receivers.get(k);
                                if (rec.receiver == receiver) {
                                    rec.dead = true;
                                    receivers.remove(k);
                                }
                            }

                            if (receivers.size() <= 0) {
                                this.mActions.remove(action);
                            }
                 ...
    }

Transmit broadcast

 public boolean sendBroadcast(@NonNull Intent intent) {
        HashMap var2 = this.mReceivers;
        synchronized(this.mReceivers) {
        String action = intent.getAction()
        ...
        //1. Find broadcast information according to action
        ArrayList<LocalBroadcastManager.ReceiverRecord> entries = (ArrayList)this.mActions.get(intent.getAction());
        //2. Traverse all the mcactions collection to get the corresponding broadcast
        for(i = 0; i < entries.size(); ++i) {
				LocalBroadcastManager.ReceiverRecord receiver = (LocalBroadcastManager.ReceiverRecord)entries.get(i);
		//3. Check whether the broadcast has been unregistered, otherwise add it to the broadcast receiver to be sent
				 if (receiver.broadcasting) {
                        if (debug) {
                            Log.v("LocalBroadcastManager", "  Filter's target already added");
                        }
                    } else {
					int match = receiver.filter.match(action, type, scheme, data, categories, "LocalBroadcastManager");
						if (match >= 0) {
						if (receivers == null) {
                                receivers = new ArrayList();
                            }

                            receivers.add(receiver);
                            receiver.broadcasting = true;
						}
						...
				//4. Change the status of the broadcast
					 if (receivers != null) {
                    for(i = 0; i < receivers.size(); ++i) {
                        ((LocalBroadcastManager.ReceiverRecord)receivers.get(i)).broadcasting = false;
                    }
			//	5. Sent to executePendingBroadcasts() by Handler for processing
                    this.mPendingBroadcasts.add(new LocalBroadcastManager.BroadcastRecord(intent, receivers));
                    if (!this.mHandler.hasMessages(1)) {
                        this.mHandler.sendEmptyMessage(1);
                    }
					...
                }
					}
		}
}
void executePendingBroadcasts() {
//1. Constantly check whether there is a broadcast to be sent
 while(true) {
synchronized(this.mReceivers) {
				//2. Get all broadcast objects
                int N = this.mPendingBroadcasts.size();
                if (N <= 0) {
                    return;
                }

                brs = new LocalBroadcastManager.BroadcastRecord[N];
                this.mPendingBroadcasts.toArray(brs);
                this.mPendingBroadcasts.clear();
            }
		//3. Traverse all broadcast objects to see if the. dead of the broadcast state is true. Otherwise, the broadcast information will be sent to all broadcast receivers, and the broadcast information will be obtained from the rewritten onReceive.
            for(int i = 0; i < brs.length; ++i) {
                LocalBroadcastManager.BroadcastRecord br = brs[i];
                int nbr = br.receivers.size();

                for(int j = 0; j < nbr; ++j) {
                    LocalBroadcastManager.ReceiverRecord rec = (LocalBroadcastManager.ReceiverRecord)br.receivers.get(j);
                    if (!rec.dead) {
                        rec.receiver.onReceive(this.mAppContext, br.intent);
                    }
                }
            }
	}

}

At this point, we have finished the analysis of source code. For security, efficiency and simplicity, the analysis is as follows:
Security: it uses a self defined broadcast mechanism, which is maintained through collection, so the broadcast can only run in its application;
Efficient: the common broadcast is maintained by Binder and AMS (Activity Manager Service), while the local broadcast is maintained by Handler, with high efficiency;
Simple: common broadcast includes static and dynamic mode. Local broadcast is maintained by local broadcast manager. Its registration, sending and cancellation are all in this class, which is easy to call and view the source code.

86 original articles published, 46 praised, 50000 visitors+
Private letter follow

Posted on Sat, 01 Feb 2020 06:48:33 -0500 by alcedema