Android LocalBroadcastReceiver本地广播
来源:互联网 发布:淘宝卖家怎么设置定制 编辑:程序博客网 时间:2024/06/05 16:45
由于之前的广播都是全局的,所有应用程序都可以接收到,这样就很容易会引起安全性的问题,比如说我们发送一些携带关键性数据的广播有可能被其他的应用程序截获,或者其他的程序不停地向我们的广播接收器里发送各种垃圾广播。
为了能够简单地解决广播的安全性问题,Android引入了一套本地广播机制,使用这个机制发出的广播只能够在应用程序的内部进行传递,并且广播接收器也只能接收来自应用程序发出的广播,这样所有的安全性问题就都不存在了。
核心用法
使用LocalBroadcastManager来管理广播:
- 调用LocalBroadcastManager.getInstance(this)来获得实例
- 调用xx.registerReceiver(BroadcastReceiver receiver,IntentFilter filter)来注册广播;(receiver本地广播接收器)
- 调用xx.sendBroadcast()发送广播
- 调用xx.unregisterReceiver()取消注册
注意事项
- 本地广播无法通过静态注册来接收,相比起系统全局广播更加高效
- 在广播中启动activity的话,需要为intent加入FLAG_ACTIVITY_NEW_TASK的标记,不然会报错,因为需要一个栈来存放新打开的activity。
- 广播中弹出AlertDialog的话,需要设置对话框的类型为:TYPE_SYSTEM_ALERT不然是无法弹出的。
1:本地广播实现Activity传递数据:
源码分析:
1:getInstance(this);用了标准的单利模式;
2:构造中初始化了一个Handle对象,用于对广播的分发;
3:广播的注册、反注册、发送流程
如果让我们来自己来实现广播的注册、反注册、发送我们会怎么搞呢?
首先,注册的时候需要提供BroadcastReceiver和对应的IntentFilter,我们可以对这种数据结构进行封装,放到一个类中ReceiverRecord。
然后维护一个ReceiverRecord对象列表,用于记录当前注册了哪些BroadcastReceiver。可以简单使用ArrayList<ReceiverRecord>.
在unRegister的时候根据提供的BroadcastReceiver对象,遍历List找出对应的receiver进行移除。
这样每来一个unRegister我们都需要对Receiver列表做一次遍历,开销有点大,在查操作比较多的时候我们可以使用MAP。
HashMap<BroadCastReceiver, ReceiverRecord>
ReceiverRecord中已经包含BroadcastReceiver对象了,所以value直接使用IntentFilte就行了,简化数据结构。
那如果一个Receiver注册了多个IntentFilter呢?比如说一个receiver对象注册两次传入不同的IntentFilter.所以Value需要改造为ArrayList<IntentFilter>。 最终用于维护当前Reciver对象列表的数据结构是这样事儿的:
HashMap<BroadcastReceiver, ArrayList<IntentFilter>> mReceivers.
当删除时可以通过receiver对象为key在map中快速查找并移除。
发送广播的时候呢?我们知道sendBroadcast时只传入了Intent对象,Intent携带了Action用于和已经注册的receiver匹配。在查找receiver时,需要对HashMap<BroadcastReceiver, ArrayList<IntentFilter>> mReceivers 的Value进行遍历,每一个Value ArrayList<IntentFilter> 又需要遍历一次。这个查找的开销实在太大了。
看来我们为了实现Action和receiver的快速匹配需要再维护一个数据结构了。同样是频繁查找的需求使用HashMap.
将Action作为Key,value肯定是与之匹配的receiver了。因为一个Action可能会对应多个receiver,receiver注册的时候可以使用相同的Action.所以value需要使用ArrayList<BroadcastReceiver>. 当发送广播时可以快速根据Action找到对应的receiver。对了,不仅仅要使用Action匹配,filter中还有其他信息匹配成功之后才能确认是真正的receiver.所以需要使用ReceiverRecord作为value,因为不仅包含了receiver对象,同时包含了IntentFilter.所以最终的数据结构是HashMap<String,ArrayList<ReceiverRecord>>.
我们来看Google是怎么实现的呢?
先来看两个内部类:
//内部类ReceiverRecord Receiver记录:用于记录reciver,对应的IntentFilter和是否在broadcast状态
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(); } } //BroadcastRecord 广播记录:用于记录广播的intent以及有哪些对应的ReceiverRecord private static class BroadcastRecord { final Intent intent; final ArrayList<ReceiverRecord> receivers; BroadcastRecord(Intent _intent, ArrayList<ReceiverRecord> _receivers) { intent = _intent; receivers = _receivers; } }
再来看几个成员变量:
//维护一个mReceivers Map 记录一个所有的receiver,每一个对应哪些Intentfilters。主要用于记录当前有哪些receiver需要维护接受广播。 //方便广播的反注册,反注册时可以快速找到filter从而找到Action,从而操作mAction.试想如果没有mReceivers,只能全部遍历mAction找出所有BroadcastRecord,从而找到filter和Action,性能很差。 private final HashMap<BroadcastReceiver, ArrayList<IntentFilter>> mReceivers = new HashMap<BroadcastReceiver, ArrayList<IntentFilter>>(); //维护一个mActions Map 记录所有的Action,每一个对应哪些ReceiverRecord private final HashMap<String, ArrayList<ReceiverRecord>> mActions = new HashMap<String, ArrayList<ReceiverRecord>>(); //维护一个List,记录当前正处在等待状态的广播BroadcastRecord,通过BroadcastRecord可以找到intent对应的receivers private final ArrayList<BroadcastRecord> mPendingBroadcasts = new ArrayList<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) { ReceiverRecord entry = new ReceiverRecord(filter, receiver); //查找receiver有没有在mReceiver记录中,如果不在需要添加进去。 ArrayList<IntentFilter> filters = mReceivers.get(receiver); if (filters == null) { filters = new ArrayList<IntentFilter>(1); mReceivers.put(receiver, filters); } //将IntentFilter加入receiver对应的匹配规则中。filters为mReceivers map的value,类型是IntentFilter的ArrayList。 //同一个receiver可能有多个IntentFilter。mReceivers就记录了所有的receiver,并且指明每一个receiver所能匹配到的IntentFilter. filters.add(filter); //开始遍历IntentFilter中的Action.检查Action是否在mActions,如果不在不要添加进去。 //mActions是Action为Key , ArrayList<ReceiverRecord>为value的MAP。 //记录了当前有那些Action,并且每个Action对应的Receiver(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); } //将ReceiverRcoder对象加入entries:entries是该Action对应的ReceiverRecord列表。 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) { //在Receiver列表中移除要注销的receiver,返回对应的filters. ArrayList<IntentFilter> filters = mReceivers.remove(receiver); if (filters == null) { return; } //在Action Map中移除对应的receiver for (int i=0; i<filters.size(); i++) { IntentFilter filter = filters.get(i); for (int j=0; j<filter.countActions(); j++) { String action = filter.getAction(j); //根据Action获取receiver列表,移除要删除的receiver. 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--; } } //如果发现Action对应的receiver都没有删除掉了,这时候就需要在Action列表中清空。 //因为没有receiver来处理这个Action了。 if (receivers.size() <= 0) { mActions.remove(action); } } } } } }广播的发送过程: /** * 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) { 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); //根据Action从mActions MAP 中取对应的receivers. ArrayList<ReceiverRecord> entries = mActions.get(intent.getAction()); if (entries != null) { if (debug) Log.v(TAG, "Action list: " + entries); ArrayList<ReceiverRecord> receivers = null; //遍历receivers找出符合IntentFilter条件的 for (int i=0; i<entries.size(); i++) { ReceiverRecord receiver = entries.get(i); if (debug) Log.v(TAG, "Matching against filter " + receiver.filter); //如果receiver已经在分发中,不做处理。 if (receiver.broadcasting) { if (debug) { Log.v(TAG, " Filter's target already added"); } continue; } //检查receiver是否和发送广播时传入的Intent匹配,并加入符合条件的列表receivers 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 是否在分发中置位true receiver.broadcasting = true; } else { 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); } } } //将broadcasting置位false 加入mPendingBroadcasts等待队列中. if (receivers != null) { for (int i=0; i<receivers.size(); i++) { receivers.get(i).broadcasting = false; } mPendingBroadcasts.add(new BroadcastRecord(intent, receivers)); //发送消息MSG_EXEC_PENDING_BROADCASTS if (!mHandler.hasMessages(MSG_EXEC_PENDING_BROADCASTS)) {//通过Handler发送消息来处理 mHandler.sendEmptyMessage(MSG_EXEC_PENDING_BROADCASTS); } return true; } } } return false; }可以看到最终把要发送的广播加入队列mPendingBroadcasts,然后使用Handler发送消息给主线程处理的,调用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); } } } }}LocalBroadcast也支持使用同步的方式进行分发: /** * 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(); } }
总结
1.LocalBroadcast是APP内部维护的一套广播机制,有很高的安全性和高效性。
所以如果有APP内部发送、接收广播的需要应该使用LocalBroadcast。
2.Receiver只允许动态注册,不允许在Manifest中注册。
3.LocalBroadcastManager所发送的广播action,只能与注册到LocalBroadcastManager中BroadcastReceiver产生互动。
阅读全文
0 0
- Android LocalBroadcastReceiver本地广播
- 使用本地广播 LocalBroadcastReceiver
- LocalBroadcastReceiver (本地广播)2017-05-26
- LocalBroadcastReceiver
- Android本地广播机制
- android本地广播
- Android使用本地广播
- Android发送本地广播
- android 本地广播
- Android 使用本地广播
- Android之广播(本地广播)
- Android之广播机制—本地广播
- Android-本地广播提高安全性
- Android自定义发送本地广播
- Android-本地广播提高安全性
- android本地广播的使用
- Android广播之发送自定义广播+本地广播(二)
- Android广播机制---发送本地广播_android全局信息处理本地广播安全广播
- java Console 控制台为null问题 Console con = System.console()
- Day018
- 线段树--带 * + 符号
- Kotlin从入门到放弃(三)——协程
- Day 018
- Android LocalBroadcastReceiver本地广播
- .Net mvc 登录认证成功后无法跳转到指定的页面,求指教
- python知识点(4)——基本的购物车功能实现
- 自我学习之es6(3)
- 狄利克雷卷积
- java数组复制
- 题目:3:月度开销
- 购物车的实现案例
- RecyclerView 使用 点击事件和长按