Android本地广播机制

214 阅读3分钟

本地广播机制

Android广播机制分为本地广播和全局广播。本地广播仅在自身应用内传播,全局广播则在整个系统所有应用内传播。

本地广播的优点

  1. 传播的数据不会离开自身应用,因此本地广播不需要担心私密数据泄露;
  2. 其他应用发送相同广播不会被自身应用监听到,因此本地广播不需要担心安全漏洞风险;
  3. 发送本地广播比全局广播效率更高;

源码解析

LocalBroadcastManager是系统提供的本地广播管理类,协助进行本地广播的注册与发送。

  1. ReceiverRecord是对Filter和Receiver的封装类;BroadcastRecord是对Intent和ReceiverRecord的封装类。
     private static final class ReceiverRecord {
         final IntentFilter filter;
         final BroadcastReceiver receiver;
         boolean broadcasting;   // 是否正在广播中;
         boolean dead;   // 是否已解注册
     }
     private static final class BroadcastRecord {
         final Intent intent;
         final ArrayList<ReceiverRecord> receivers;
     }
    
  2. 内部持有mReceivers和mActions两个映射表,分别表示监听器对应的多个Record、Action对应的多个Record;还存在mPendingBroadcasts集合存储BroadcastRecord,表示需要执行的广播;
     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<>();
    
  3. registerReceiver()注册时,首先新建ReceiverRecord,添加到mReceivers中;然后遍历Filter的Actions,在对应的mActions中添加。
     public void registerReceiver(@NonNull BroadcastReceiver receiver, @NonNull IntentFilter filter) {
         synchronized (mReceivers) {
             ReceiverRecord entry = new ReceiverRecord(filter, receiver);
             /** 同一个Receiver可以多次注册 */
             ArrayList<ReceiverRecord> filters = mReceivers.get(receiver);
             if (filters == null) {
                 filters = new ArrayList<>(1);
                 mReceivers.put(receiver, filters);
             }
             filters.add(entry);
             /** 遍历filter中的所有Action,获取监听Action的接收器列表,添加新的Record到列表中 */
             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);
             }
         }
     }
    
  4. unregisterReceiver()解注册时,根据Receiver首先查到所有ReceiverRecord,然后遍历获取Actions,然后在mActions中删除对应的Record。
     public void unregisterReceiver(@NonNull BroadcastReceiver receiver) {
         synchronized (mReceivers) {
             /** 移除receiver在集合中对应的Record */
             final ArrayList<ReceiverRecord> filters = mReceivers.remove(receiver);
             if (filters == null) {
                 return;
             }
             /** 遍历Record,通过filter再遍历actions,移除receiver在mActions集合中的记录,设置Record#dead=true */
             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);
                         }
                     }
                 }
             }
         }
     }
    
  5. sendBroadcast()发送广播
    • 首先解构Intent,然后根据Action获取到对应的List<ReceiverRecord>
    • 遍历此集合,判断Receiver是否匹配此Intent,若是则添加到receivers临时集合中,表示要接收此Intent的Receiver;
    • 将receivers临时集合与Intent一起封装为BroadcastRecord对象,发送消息使内部的Handler进行处理;
    • 遍历receivers,对仍处于监听状态的Receiver回调onReceive()方法。
    • 同一时刻只能存在一条MSG_EXEC_PENDING_BROADCASTS信息,而对应的执行方法会将周期内所有广播全部执行。
    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获取Record */
            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);
                    if (receiver.broadcasting) {
                        continue;
                    }
                    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与Receiver注册的Filter不匹配*/
                    }
                }
                /** 将上面得到的,需要响应的Record集合与Intent一起封装为BroadcastRecord,发送同步消息等待后续执行广播回调 */
                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;
    }
    
  6. sendBroadcastSync()是同步阻塞的发送广播,在调用后立即执行广播接收者回调,而不会通过Handler进行消息排队。
  7. 对于deadbroadcasting两个flag的思考
    1. 对于同一个Receiver和同一个IntentFilter,可以多次调用registerReceiver()注册,因此mReceivers和mActions对应的集合中都有可能存在完全相同的ReceiverRecord
    2. 注册解注册、发送广播、执行等方法都使用synchronized进行线程管控,因此可以判断这两个flag没有实际用途。

总结

本地广播不属于跨进程通信范畴,因此没有使用到如Binder等底层IPC机制,而仅仅是依靠全局变量、主线程的Handler以及线程同步机制实现的应用内消息传播。以此而言,效率比全局广播的跨进程通信更高,更适用于应用内跨组件通信的场景。