LocalBroadcastManager原理分析及应用

本文详细介绍了Android中LocalBroadcastManager的工作原理,包括它的优点、注册与反注册过程、异步与同步发送广播的方式,以及如何在实际应用中使用本地广播进行通信。通过源码分析,揭示了LocalBroadcastManager如何在应用内部高效、安全地实现广播机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

引言

Android页面或模块之间通信的方法有很多,如Intent传值、startActivityForResult、EventBus(RxBus)等,大家追求的无非是解耦以及高灵活性;我们自己的应用中使用了基于Android消息机制封装的一套通信体系,且不谈这些,今天的主角是本地广播。

本地广播是系统提供的一种应用内通信方式,它跟全局广播相比,其运行机制是不同的。全局广播是系统级别的,需要跨进程调用,而且作用域是整个设备,而本地广播的作用域只在当前应用之内,因此无需IPC的过程,本应用发送的本地广播,其他应用接收不到,其他应用发送的本地广播,本应用也接收不到。

本地广播的优点

  1. 本地广播作用于App内部,广播数据不会泄露,本应用也不会接收到其他应用的广播,因此安全性较高
  2. 本地广播是在应用内部执行,无需跨进程逻辑,与普通广播相比效率更高
  3. 使用简单,无需静态注册

原理分析

本地广播的原理也很简单,大概流程如下

  1. 首先需要接收广播的页面创建一个BroadcastReceiver(广播声明跟全局广播是一样的)并注册到一个应用内的本地广播接收器列表(注册方式跟全局广播不同,下文会详细说明)
  2. 某个页面利用Intent发送广播(发送方式跟全局广播不同,下文会详细说明)
  3. 本地广播接收器列表根据IntentFilter匹配得到可以接收该广播的BroadcastReceiver,然后匹配的广播接收器会响应广播(注意这里区分广播的异步与同步发送)
  4. 在适当时候反注册BroadcastReceiver,比如Activity的onDestroy()回调中

为了方便大家的使用,Android提供了LocalBroadcastManager类(姑且称之为本地广播管理器),该类帮我们封装了注册、反注册、广播发送等过程,接下来分析一下源码实现。

LocalBroadcastManager定义

首先LocalBroadcastManager定义为一个单例,方便App内全局使用。

private static LocalBroadcastManager mInstance;

public static LocalBroadcastManager getInstance(Context context) {
    synchronized (mLock) {
        if (mInstance == null) {
            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的构造函数中创建了一个Handler实例,并且是运行在主线程的,它响应一种what为MSG_EXEC_PENDING_BROADCASTS的消息,这是用来处理异步广播的,具体如何处理稍后说明。

两个内部类

对于广播接收器的注册过程,并非简单地把BroadcastReceiver进行注册,而是进行了简单的包装,这里涉及到LocalBroadcastManager的两个内部类,如下:

ReceiverRecord
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();
    }
}

顾名思义,ReceiverRecord简单封装了BroadcastReceiver和IntentFilter。

BroadcastRecord
private static class BroadcastRecord {
    final Intent intent;
    final ArrayList<ReceiverRecord> receivers;

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

BroadcastRecord也很简单,封装了广播Intent以及能够匹配该Intent的接收器列表(这里并非直接是BroadcastReceiver,而是包装类ReceiverRecord)。

基于上述两个包装类,LocalBroadcastManager提供了如下两个HashMap用来存储注册的接收器

private final HashMap<BroadcastReceiver, ArrayList<IntentFilter>> mReceivers
        = new HashMap<BroadcastReceiver, ArrayList<IntentFilter>>();
private final HashMap<String, ArrayList<ReceiverRecord>> mActions
        = new HashMap<String, ArrayList<ReceiverRecord>>();

此外还有一个叫mPendingBroadcasts的容器如下

private final ArrayList<BroadcastRecord> mPendingBroadcasts
        = new ArrayList<BroadcastRecord>();

它是用来存储待处理广播的列表,其元素为BroadcastRecord。

注册过程

注册过程源码如下,具体流程分析请看注释

/**
 * Register a receive for any local broadcasts that match the given IntentFilter.
 *
 * @param receiver The BroadcastReceiver to handle the broadcast.
 * @param filter Selects the Intent broadcasts to be received.
 *
 * @see #unregisterReceiver
 */
public void registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
    // 加锁同步
    synchronized (mReceivers) {
        // 将BroadcastReceiver和IntentFilter封装成ReceiverRecord
        ReceiverRecord entry = new ReceiverRecord(filter, receiver);
        // BroadcastReceiver作为HashMap的key,注意一般来说注册时一个BroadcastReceiver对应一个IntentFilter。但有一种情况下同一个页面内使用同一个BroadcastReceiver,配合不同的IntentFilter注册多次;更极端情况是应用内只有一个BroadcastReceiver,使用不同的IntentFilter进行多次注册
        ArrayList<IntentFilter> filters = mReceivers.get(receiver);
        if (filters == null) {
            filters = new ArrayList<IntentFilter>(1);
            mReceivers.put(receiver, filters);
        }
        filters.add(filter);
        // 一个IntentFilter可以对应多个action
        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);
        }
    }
}
反注册过程

对应的反注册过程如下,代码逻辑请看注释

/**
 * Unregister a previously registered BroadcastReceiver.  <em>All</em>
 * filters that have been registered for this BroadcastReceiver will be
 * removed.
 *
 * @param receiver The BroadcastReceiver to unregister.
 *
 * @see #registerReceiver
 */
public void unregisterReceiver(BroadcastReceiver receiver) {
    // 加锁同步
    synchronized (mReceivers) {
        // 同一个BroadcastReceiver可能对应多个IntentFilter,移除以BroadcastReceiver为key的键值对
        ArrayList<IntentFilter> filters = mReceivers.remove(receiver);
        if (filters == null) {
            return;
        }
        for (int i=0; i<filters.size(); i++) {
            IntentFilter filter = filters.get(i);
            // 每个IntentFilter可对应多个action
            for (int j=0; j<filter.countActions(); j++) {
                String action = filter.getAction(j);
                // 以action作为key
                ArrayList<ReceiverRecord> receivers = mActions.get(action);
                if (receivers != null) {
                    for (int k=0; k<receivers.size(); k++) {
                        // 移除包含目标BroadcastReceiver的ReceiverRecord
                        if (receivers.get(k).receiver == receiver) {
                            receivers.remove(k);
                            k--;
                        }
                    }
                    if (receivers.size() <= 0) {
                        mActions.remove(action);
                    }
                }
            }
        }
    }
}
异步发送广播

异步发送广播利用了Android消息机制,发送完成后即可返回,广播会异步响应,通常情况下我们都是使用异步方式,否则会被阻塞。

/**
 * Broadcast the given intent to all interested BroadcastReceivers.  This
 * call is asynchronous; it returns immediately, and you will continue
 * executing while the receivers are run.
 *
 * @param intent The Intent to broadcast; all receivers matching this
 *     Intent will receive the broadcast.
 *
 * @see #registerReceiver
 */
public boolean sendBroadcast(Intent intent) {
    synchronized (mReceivers) {
        // 提取出Intent中的action、type、data、scheme、categories等,稍后用于匹配
        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();
        // debug仅用于打印log,可忽略
        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);

        // 首先根据广播Intent的action进行匹配,匹配不上则直接返回false
        ArrayList<ReceiverRecord> entries = mActions.get(intent.getAction());
        if (entries != null) {
            if (debug) Log.v(TAG, "Action list: " + entries);

            // receivers用来存储匹配上的接收器包装类ReceiverRecord
            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);

                if (receiver.broadcasting) {
                    if (debug) {
                        Log.v(TAG, "  Filter's target already added");
                    }
                    continue;
                }

                // InterFilter的匹配,若匹配成功,返回值match>=0
                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.broadcasting = true;
                } else {
                    // 匹配失败在debug为true的情况下仅输出log
                    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) {
                // 将上述匹配成功时设置的标志重新置为false
                for (int i=0; i<receivers.size(); i++) {
                    receivers.get(i).broadcasting = false;
                }
                // 将匹配成功的接收器封装为BroadcastRecord,加入待处理广播列表
                mPendingBroadcasts.add(new BroadcastRecord(intent, receivers));
                // 利用Handler消息机制,发送消息,异步处理广播
                if (!mHandler.hasMessages(MSG_EXEC_PENDING_BROADCASTS)) {
                    mHandler.sendEmptyMessage(MSG_EXEC_PENDING_BROADCASTS);
                }
                return true;
            }
        }
    }
    return false;
}

可以看到异步广播最终利用Android消息机制切换到主线程执行广播接收过程,具体实现逻辑则是在LocalBroadcastManager的构造器中:

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

最终调用方法为executePendingBroadcasts(),稍后详解。

同步发送广播

同步发送广播比较简单,实现如下

/**
 * Like {@link #sendBroadcast(Intent)}, but if there are any receivers for
 * the Intent this function will block and immediately dispatch them before
 * returning.
 */
public void sendBroadcastSync(Intent intent) {
    if (sendBroadcast(intent)) {
        executePendingBroadcasts();
    }
}

具体的广播执行也是在executePendingBroadcasts()中。

执行广播
private void executePendingBroadcasts() {
    while (true) {
        BroadcastRecord[] brs = null;
        synchronized (mReceivers) {
            final int N = mPendingBroadcasts.size();
            if (N <= 0) {
                return;
            }
            brs = new BroadcastRecord[N];
            mPendingBroadcasts.toArray(brs);
            mPendingBroadcasts.clear();
        }
        for (int i=0; i<brs.length; i++) {
            BroadcastRecord br = brs[i];
            for (int j=0; j<br.receivers.size(); j++) {
                br.receivers.get(j).receiver.onReceive(mAppContext, br.intent);
            }
        }
    }
}

上述代码逻辑比较简单,从mPendingBroadcasts中遍历执行每个BroadcastReceiver的onReceive方法,触发BroadcastReceiver声明页面的回调。在回调中,我们就可以实现自己的广播接收逻辑了。

本地广播的响应逻辑为

@Override
public void onReceive(Context context, Intent intent) {
    // 处理逻辑
}

因此如果要传值的话则要依赖于Intent的能力。

注意事项

  1. 发送广播可以在主线程也可以在子线程
  2. 接收广播在哪个线程要看是异步广播还是同步广播。如果是异步广播,无论发送广播是在主线程还是在子线程,内部都会通过Handler消息机制切换到主线程执行。如果是同步广播,发送广播的线程和接收广播的线程为同一线程,此种方式会导致线程阻塞,使用时需谨慎。
  3. 如果广播接收在主线程的话,注意不要进行耗时操作
  4. 通过广播传值要依赖于Intent的传值方式及要求

下表是一个简单总结

发送本地广播的线程同步 or 异步是否阻塞接收本地广播的线程
主线程异步主线程
主线程同步主线程
子线程异步主线程
子线程同步子线程

应用场景

  1. 单对单通信

    场景描述:一个页面向另一个页面发送消息,可以是相邻页面也可以不相邻

    实现方式:使用特定的action注册BroadcastReceiver,并使用具有该action的Intent发送本地广播

  2. 单对多通信

    场景描述:一个页面发送广播,多个页面可以接收

    实现方式:每个页面都使用相同的action注册BroadcastReceiver即可

  3. 多对单通信

    场景描述:一个页面可以接收到多个不同广播

    实现方式:该页面使用多个不同的action(一个IntentFilter可以有多个action)注册BroadcastReceiver,这样就能接收到具有不同action的广播了

应用示例

以下示例有3个页面,分别为ActivityA、ActivityB、ActivityC,其中ActivityA和ActivityB分别注册了广播接收器,ActivityC发送广播,ActivityA和ActivityB都可以接收到广播,接收到广播之后取出传值显示在UI上。

代码中注释了子线程发送广播、同步发送广播等逻辑,同时还有Log输出,可以查看同步、异步的执行顺序以及发送广播和接收广播所在的线程信息。源代码很简单,如下。

  1. OnReceiveBrodadcastListener接口

    public interface OnReceiveBroadcastListener {
       void onReceive(Context context, Intent intent);
    }
  2. LocalBroadcastReceiver类,继承自BroadcastReceiver

    public class LocalBroadcastReceiver extends BroadcastReceiver {
       private OnReceiveBroadcastListener mListener;
    
       @Override
       public void onReceive(Context context, Intent intent) {
           if (mListener != null) {
               mListener.onReceive(context, intent);
           }
       }
    
       public LocalBroadcastReceiver(OnReceiveBroadcastListener listener) {
           mListener = listener;
       }
    }
  3. ActivityA的实现

    public class ActivityA extends AppCompatActivity {
       private static final String TAG = "LocalBroadcastManager";
       private TextView tv_a;
       private Button btn_a;
       private LocalBroadcastReceiver mReceiver;
    
       @Override
       protected void onCreate(Bundle savedInstanceState) {
           super.onCreate(savedInstanceState);
           setContentView(R.layout.activity_a);
    
           Log.d(TAG, "main thread id:" + Thread.currentThread().getId() + ",name:" + Thread.currentThread().getName());
    
           tv_a = (TextView) findViewById(R.id.tv_a);
           btn_a = (Button) findViewById(R.id.btn_a);
    
           btn_a.setOnClickListener(new View.OnClickListener() {
               @Override
               public void onClick(View v) {
                   Intent intent = new Intent(ActivityA.this, ActivityB.class);
                   startActivity(intent);
               }
           });
    
           mReceiver = new LocalBroadcastReceiver(new OnReceiveBroadcastListener() {
               @Override
               public void onReceive(Context context, Intent intent) {
                   // 注意:异步消息是在主线程处理的,务必避免耗时操作
                   // 如果接收多个广播,可以根据action分别处理
                   String action = intent.getAction();
    
                   Log.d(TAG, "A onReceive=== thread id:" + Thread.currentThread().getId() + ",name:" + Thread.currentThread().getName());
                   Log.d(TAG, "A received broadcast,action=" + action);
    
                   // 取出广播传值
                   String name = intent.getStringExtra("NAME");
                   tv_a.setText(name);
               }
           });
    
           IntentFilter filter = new IntentFilter();
           // 一个IntentFilter可以配置多个action
           filter.addAction("com.aspook.localbroadcast_A");
           filter.addAction("com.aspook.localbroadcast_AA");
           LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver, filter);
    
           // 同一个BroadcastReceiver可以注册多次,极端情况下,全局可以只有一个
    //        IntentFilter filter2 = new IntentFilter();
    //        filter2.addAction("com.aspook.localbroadcast_AAA");
    //        LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver, filter2);
       }
    
       @Override
       protected void onDestroy() {
           super.onDestroy();
           LocalBroadcastManager.getInstance(this).unregisterReceiver(mReceiver);
       }
    }
  4. ActivityB的实现

    public class ActivityB extends AppCompatActivity {
       private static final String TAG = "LocalBroadcastManager";
       private TextView tv_b;
       private Button btn_b;
       private LocalBroadcastReceiver mReceiver;
    
       @Override
       protected void onCreate(Bundle savedInstanceState) {
           super.onCreate(savedInstanceState);
           setContentView(R.layout.activity_b);
    
           tv_b = (TextView) findViewById(R.id.tv_b);
           btn_b = (Button) findViewById(R.id.btn_b);
    
           btn_b.setOnClickListener(new View.OnClickListener() {
               @Override
               public void onClick(View v) {
                   Intent intent = new Intent(ActivityB.this, ActivityC.class);
                   startActivity(intent);
               }
           });
    
           mReceiver = new LocalBroadcastReceiver(new OnReceiveBroadcastListener() {
               @Override
               public void onReceive(Context context, Intent intent) {
                   Log.d(TAG, "B onReceive=== thread id:" + Thread.currentThread().getId() + ",name:" + Thread.currentThread().getName());
                   Log.d(TAG, "B received broadcast,action=" + intent.getAction());
    
                   Bundle bundle = intent.getExtras();
                   int age = bundle.getInt("AGE");
                   tv_b.setText(age + "");
               }
           });
    
           IntentFilter filter = new IntentFilter();
           // 允许定义一个action数组,以接收不同的消息
           filter.addAction("com.aspook.localbroadcast_B");
           filter.addAction("com.aspook.localbroadcast_BB");
           filter.addAction("com.aspook.localbroadcast_AA");
           LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver, filter);
       }
    
       @Override
       protected void onDestroy() {
           super.onDestroy();
           LocalBroadcastManager.getInstance(this).unregisterReceiver(mReceiver);
       }
    }
  5. ActivityC的实现

    public class ActivityC extends AppCompatActivity {
       private static final String TAG = "LocalBroadcastManager";
       private TextView tv_c;
       private Button btn_c;
    
       @Override
       protected void onCreate(Bundle savedInstanceState) {
           super.onCreate(savedInstanceState);
           setContentView(R.layout.activity_c);
    
           tv_c = (TextView) findViewById(R.id.tv_c);
           btn_c = (Button) findViewById(R.id.btn_c);
    
           btn_c.setOnClickListener(new View.OnClickListener() {
               @Override
               public void onClick(View v) {
                   // 在主线程发送本地广播
                   Log.d(TAG, "send broadcast=== thread id:" + Thread.currentThread().getId() + ",name:" + Thread.currentThread().getName());
    
                   Intent intent = new Intent();
                   // Intent传值示例
                   intent.putExtra("NAME", "kevin");
                   Bundle bundle = new Bundle();
                   bundle.putInt("AGE", 22);
                   intent.putExtras(bundle);
    
                   intent.setAction("com.aspook.localbroadcast_AA");
                   // 异步执行
                   LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);
                   // 同步执行
                   //LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcastSync(intent);
                   Log.d(TAG, "do other things after localbroadcast!!!");
    
                   // 在子线程发送本地广播
    //                new Thread(new Runnable() {
    //                    @Override
    //                    public void run() {
    //                        Log.d(TAG, "send broadcast=== thread id:" + Thread.currentThread().getId() + ",name:" + Thread.currentThread().getName());
    //
    //                        Intent intent = new Intent();
    //                        intent.putExtra("NAME", "kevin");
    //                        Bundle bundle = new Bundle();
    //                        bundle.putInt("AGE", 22);
    //                        intent.putExtras(bundle);
    //                        // action should defined as message constant
    //                        intent.setAction("com.aspook.localbroadcast_AAA");
    //                        // 异步执行
    //                        LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);
    //                        // 同步执行
    //                        //LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcastSync(intent);
    //                        Log.d(TAG, "do other things after localbroadcast!!!");
    //                    }
    //                }).start();
               }
           });
       }
    
       @Override
       protected void onDestroy() {
           super.onDestroy();
       }
    }

上面代码只是一个本地广播的应用演示,具体使用时可以再进一步抽象封装,可用作应用内的通信机制。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值