美文网首页Android开发Android开发经验谈Android技术知识
Android LocalBroadcast 发送广播更加方便快

Android LocalBroadcast 发送广播更加方便快

作者: 魏树鑫 | 来源:发表于2019-06-24 16:29 被阅读5次

LocalBroadcast是一个应用内的局部广播,广播范围只限于应用内部,并且不属于四大组件,相对高效很多,可以用来实现事件总线,相比其他事件总线实现方案,先对简单,高效。
而且,其实现很简单,设计的很好;

优点

  • 广播范围只限于应用内部,不会出现数据泄漏和安全漏洞问题;
  • 相对于系统广播,更加高效便捷;
  • 相对于其他事件总线框架,实现简单,高效,不存在例如EventBus事件类代码区域划分问题;
  • 可以集中管理Action,使用系统Intent传递数据,学习成本低;
  • 虽然Intent传递数据,但是内部没有涉及到序列化,所以不同于四大组件的Intent实体需要实现序列化,并且大小也无限制;
  • 使用IntentFilter进行事件匹配,所以可以是多维度的,是根据Action, Type, Scheme, Uri, Categories进行匹配的;

缺点

  • 无法线程变换,事件处理默认在主线程;
  • 无法实现粘性事件;
  • 如果不定义Action常量,则需要用文档管理维护大量的Action;IntentFilter也相当于事件类,如果是多纬度的,那么就需要维护IntentFilter这个事件类了;
  • 查找订阅事件或者发送事件比较困难,只能全局搜索,如果定义Action常量,那么Action代码也会出现区域划分问题;

注意

  • 可以重复注册,并且重复注册后会收到多次回调;

使用

//1. 创建BroadcastReceiver
val broadcastReceiver = object : BroadcastReceiver() {
    override fun onReceive(context: Context?, intent: Intent?) {
        Log.i("shuxin.wei", "onReceive")
    }
}
//2. 在合适的位置注册监听
LocalBroadcastManager.getInstance(this).registerReceiver(broadcastReceiver, IntentFilter("ACTION_TEST"))
LocalBroadcastManager.getInstance(this).registerReceiver(broadcastReceiver, IntentFilter("ACTION_TEST"))
//3. 发布事件
LocalBroadcastManager.getInstance(this).sendBroadcastSync(Intent("ACTION_TEST"))
Log.i("shuxin.wei", "sendBroadcastSync")
LocalBroadcastManager.getInstance(this).sendBroadcast(Intent("ACTION_TEST"))
Log.i("shuxin.wei", "sendBroadcast")
//4. 在合适的位置取消监听
if (broadcastReceiver != null) {
    LocalBroadcastManager.getInstance(this).unregisterReceiver(broadcastReceiver);
}
//日志输出
//可以看到同步广播是在onReceive执行完,才继续向下执行的;
//广播是可以重复注册的,并会重复收到;
shuxin.wei: onReceive
shuxin.wei: onReceive
shuxin.wei: sendBroadcastSync
shuxin.wei: sendBroadcast
shuxin.wei: onReceive
shuxin.wei: onReceive

源码分析

1. 数据结构

//保存接收者与IntentFilter,并记录广播状态
//这个实体,既可以用作代替BroadcastReceiver,也可以用作代替IntentFilter
//在注册时利用其保存了BroadcastReceiver与多个IntentFilter对应关系
//也利用其保存了Action与多个BroadcastReceiver的对应关系
private static final class ReceiverRecord {
    final IntentFilter filter;
    final BroadcastReceiver receiver;
    boolean broadcasting;
    boolean dead;
    ReceiverRecord(IntentFilter _filter, BroadcastReceiver _receiver) {
        filter = _filter;
        receiver = _receiver;
    }
}
//保存广播和Intent,在分发广播时,将Intent传递到接收者的receiver方法中
//receiver.onReceive(mAppContext, br.intent)
private static final class BroadcastRecord {
    final Intent intent;
    final ArrayList<ReceiverRecord> receivers;
    BroadcastRecord(Intent _intent, ArrayList<ReceiverRecord> _receivers) {
        intent = _intent;
        receivers = _receivers;
    }
}
//记录BroadcastReceiver和注册的所有IntentFilter
//例如在多个位置对相同的BroadcastReceiver注册多个IntentFilter
HashMap<BroadcastReceiver, ArrayList<ReceiverRecord>> mReceivers = new HashMap<>();
//记录Action和对应所有的BroadcastReceiver
//不同的BroadcastReceiver可以注册相同的Action
HashMap<String, ArrayList<ReceiverRecord>> mActions = new HashMap<>();
//记录所有要分发的Intent和对应的所有的BroadcastReceiver
//不同BroadcastReceiver可以对应相同的Action,所以,分发时一个Intent可以对应多个BroadcastReceiver
ArrayList<BroadcastRecord> mPendingBroadcasts = new ArrayList<>();

2. 创建LocalBroadcastManager实例

  1. LocalBroadcastManager是相对线程安全的单例;
  2. 使用ApplicationContext,不会出现内存泄漏;
  3. Handler使用MainLooper,回调只能在主线程;
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);
            }
        }
    };
}

2. 注册监听

  1. 同步方法,线程安全
  2. 用ReceiverRecord保存BroadcastReceiver和IntentFilter
  3. 从HashMap中获取BroadcastReceiver对应的IntentFilter集合
  4. 如果没有注册过BroadcastReceiver,则创建新的filters集合,添加到HashMap中,并将ReceiverRecord保存到filters集合;
  5. 开始处理Action,遍历要注册的IntentFilter的Action;
  6. 根据Action从HashMap中取到对应的BroadcastReceiver集合;
  7. 如果没有注册这个Action,则创建新的BroadcastReceiver集合,添加到HashMap中,并将ReceiverRecord保存到BroadcastReceiver集合;
    总结:同一个BroadcastReceiver可以注册多个相同或不同IntentFilter;
    相同Action可以对应多个相同或不同BroadcastReceiver;
public void registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
    synchronized (mReceivers) {
        ReceiverRecord entry = new ReceiverRecord(filter, receiver);
        ArrayList<ReceiverRecord> filters = mReceivers.get(receiver);
        if (filters == null) {
            filters = new ArrayList<>(1);
            mReceivers.put(receiver, filters);
        }
        filters.add(entry);
        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);
        }
    }
}

3. 取消注册

  1. 同步方法,线程安全;
  2. 根据BroadcastReceiver移除对应的所有的IntentFilter;
  3. 如果IntentFilter集合为空,那么说明已经被unregister过,Action的移除也处理过了,直接结束;
  4. 如果IntentFilter集合不为空,则还需要处理Action,遍历所有的IntentFilter;
  5. 标记ReceiverRecord为dead;继续遍历IntentFilter中的Action;
  6. 根据Action取出对应的ReceiverRecord集合,遍历ReceiverRecord集合,移除匹配的BroadcastReceiver;
  7. 如果Action取出对应的ReceiverRecord集合为空了,那么不再需要这个Action,移除掉,释放内存;
    思考:如果mActions里面用HashMap<String,HashMap<BroadcastReceiver, ArrayList<ReceiverRecord>>>保存Action与ReceiverRecord对应关系,那么移除时,可以直接根据BroadcastReceiver移除ReceiverRecord,理论上效率应该会更高一些,虽然性能差距可能不大
public void unregisterReceiver(BroadcastReceiver receiver) {
    synchronized (mReceivers) {
        final ArrayList<ReceiverRecord> filters = mReceivers.remove(receiver);
        if (filters == null) {
            return;
        }
        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);
                    }
                }
            }
        }
    }
}

4. 发送广播

  1. 相对主线程,可以是异步发送的,加了锁,线程安全;
  2. 解析Intent,根据Action取出所有对应的ReceiverRecord;
  3. 如果ReceiverRecord集合不为空,则进行事件处理,否则,没有对应的事件,直接retrun false;
  4. 遍历ReceiverRecord集合,判断当前事件是否已经添加到等待处理的广播集合中;
    broadcasting的作用是??????在注册时ReceiverRecord都是直接new出来的,所以不会出现重复,那么broadcasting的作用是什么呢?没搞懂!!!
  5. 进行IntentFilter匹配,匹配成功,添加到待分发的ReceiverRecord集合中;
  6. 将Intent和ReceiverRecord集合包装添加到待分发的mPendingBroadcasts中;
  7. 如果mHandler里没有等待执行的任务,则启动一个任务;
  8. 任务启动后会回调到mHandler中,执行executePendingBroadcasts()方法;
  9. 将mPendingBroadcasts复制一份,然后调用receiver.onReceive(mAppContext, br.intent),清空mPendingBroadcasts,完成事件分发;
    分发中的死循环是处理异步任务可能随时见添加新的事件,确保一次执行能将所有任务处理完;
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();
        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 (debug) Log.v(TAG, "Matching against filter " + receiver.filter);
                if (receiver.broadcasting) {
                    if (debug) {
                        Log.v(TAG, "  Filter's target already added");
                    }
                    continue;
                }
                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;
                }
            }
            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;
}

private void executePendingBroadcasts() {
    while (true) {
        final BroadcastRecord[] brs;
        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++) {
            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);
                if (!rec.dead) {
                    rec.receiver.onReceive(mAppContext, br.intent);
                }
            }
        }
    }
}

同步广播

sendBroadcast方法是同步的,一定是执行完了receiver.onReceive(mAppContext, br.intent)回调才会继续向下执行,否则阻塞于while循环中;

public void sendBroadcastSync(Intent intent) {
    if (sendBroadcast(intent)) {
        executePendingBroadcasts();
    }
}

相关文章

网友评论

    本文标题:Android LocalBroadcast 发送广播更加方便快

    本文链接:https://www.haomeiwen.com/subject/vxuoqctx.html