Android 中的 本地广播LocalBroadcastManager

Android 中的 本地广播LocalBroadcastManager

文章目录

  • Android 中的 本地广播LocalBroadcastManager
    • 一、LocalBroadcastManager 的基本作用
    • 二 、LocalBroadcastManager 的基本使用
      • 1、包的导入
        • (1)Android 源码中bp文件的导入:
        • (2)Android Studio导入
      • 2、代码调用使用
        • (1)发送
        • (2)接收
    • 三、LocalBroadcastManager 源码
    • 四、LocalBroadcastManager 总结
      • 1、LocalBroadcastManager注册广播只能通过代码注册的方式。传统的广播可以通过代码和xml两种方式注册。
      • 2、LocalBroadcastManager注册广播后,一定要记得取消监听。这一步可以有效的解决内存泄漏的问题。
      • 3、LocalBroadcastManager采用的是Handler的消息机制来处理的广播,所以可以高效在应用内部通讯。

一、LocalBroadcastManager 的基本作用

对于LocalBroadcastManager在google官方文档中也说得很清楚,比较简短,也很好看懂,可以去看看。

Helper to register for and send broadcasts of Intents to local objects within your process. This has a number of advantages over sending global broadcasts with sendBroadcast(Intent):You know that the data you are broadcasting won’t leave your app, so don’t need to worry about leaking private data.
It is not possible for other applications to send these broadcasts to your app, so you don’t need to worry about having security holes they can exploit.
It is more efficient than sending a global broadcast through the system.

大体介绍就是这些,顾名思义,本地广播(注册),数据安全,其他app也不能给你发广播(接收)。

也比系统广播高效。

一般使用在应用内部不同fragment和Activity的交互,或者界面和service 的交互。

BroadcastReceiver设计的初衷是从全局考虑可以方便应用程序和系统、应用程序之间、应用程序内的通信,所以对单个应用程序而言BroadcastReceiver是存在安全性问题的(恶意程序脚本不断的去发送你所接收的广播)。为了解决这个问题LocalBroadcastManager就应运而生了。

二 、LocalBroadcastManager 的基本使用

1、包的导入

(1)Android 源码中bp文件的导入:
    static_libs: ["androidx.core_core","androidx.legacy_legacy-support-v4", // 包含:LocalBroadcastManager。。。],
(2)Android Studio导入

只要有 androidx 的包就可以,第一次使用有可能有右键根据提示导入一下,或者依赖里面加入:

implementation 'androidx.legacy:legacy-support-v4:1.0.0'

如果是非常就的android.support 项目,自行查一下,或者那直接使用本地保存的Java类吧。

其实 LocalBroadcastManager 就是一个普通的工具类,后面有源码提供,直接保存成java文件就可以只用!如果实在无法导包可以这样操作!

2、代码调用使用

(1)发送
LocalBroadcastManager lcm=LocalBroadcastManager.getInstance(mContext);
lcm.sendBroadcast(new Intent(ACTION_LOCATION));//发送
(2)接收
LocalBroadcastManager mLocalBroadcastManager=LocalBroadcastManager.getInstance(this);
mBoradCast = new MyBroadCast(); //定义广播,广播里面接收处理具体事务
IntentFilter intentFilter = new IntentFilter(); //添加监听的广播ActionintentFilter.addAction(XXX);
//重点在这里,本地注册,本地接收。
mLocalBroadcastManager.registerReceiver(mBoradCast, intentFilter);private BroadcastReceiver mReceiver = new BroadcastReceiver() {@Overridepublic void onReceive(Context context, Intent intent) { //处理具体事务String action = intent.getAction();Log.d(TAG, "onReceive: action = " + action);if (action.equals(XXX)) {}}};

Settings 等很多源码应用就使用到了 LocalBroadcastManager 进行消息通讯。

三、LocalBroadcastManager 源码

源码:


package androidx.localbroadcastmanager.content;//复制到本地使用,修改成自己的包名即可。import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;
import android.os.Handler;
import android.os.Message;
import android.util.Log;import androidx.annotation.NonNull;import java.util.ArrayList;
import java.util.HashMap;
import java.util.Set;/*** Helper to register for and send broadcasts of Intents to local objects* within your process.  This has a number of advantages over sending* global broadcasts with {@link android.content.Context#sendBroadcast}:* <ul>* <li> You know that the data you are broadcasting won't leave your app, so* don't need to worry about leaking private data.* <li> It is not possible for other applications to send these broadcasts to* your app, so you don't need to worry about having security holes they can* exploit.* <li> It is more efficient than sending a global broadcast through the* system.* </ul>*/
public final class LocalBroadcastManager {private static final class ReceiverRecord {final IntentFilter filter;final BroadcastReceiver receiver;boolean broadcasting;boolean dead;ReceiverRecord(IntentFilter _filter, BroadcastReceiver _receiver) {filter = _filter;receiver = _receiver;}@Overridepublic String toString() {StringBuilder builder = new StringBuilder(128);builder.append("Receiver{");builder.append(receiver);builder.append(" filter=");builder.append(filter);if (dead) {builder.append(" DEAD");}builder.append("}");return builder.toString();}}private static final class BroadcastRecord {final Intent intent;final ArrayList<ReceiverRecord> receivers;BroadcastRecord(Intent _intent, ArrayList<ReceiverRecord> _receivers) {intent = _intent;receivers = _receivers;}}private static final String TAG = "LocalBroadcastManager";private static final boolean DEBUG = false;private final Context mAppContext;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<>();static final int MSG_EXEC_PENDING_BROADCASTS = 1;private final Handler mHandler;private static final Object mLock = new Object();private static LocalBroadcastManager mInstance;@NonNullpublic static LocalBroadcastManager getInstance(@NonNull 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()) {@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case MSG_EXEC_PENDING_BROADCASTS:executePendingBroadcasts();break;default:super.handleMessage(msg);}}};}/*** 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(@NonNull BroadcastReceiver receiver,@NonNull 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);}}}/*** 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(@NonNull 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);}}}}}}/*** 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** @return Returns true if the intent has been scheduled for delivery to one or more* broadcast receivers.  (Note tha delivery may not ultimately take place if one of those* receivers is unregistered before it is dispatched.)*/public boolean sendBroadcast(@NonNull 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);ArrayList<ReceiverRecord> entries = mActions.get(intent.getAction());if (entries != null) {if (debug) Log.v(TAG, "Action list: " + entries);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;} 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);}}}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;}/*** 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(@NonNull Intent intent) {if (sendBroadcast(intent)) {executePendingBroadcasts();}}@SuppressWarnings("WeakerAccess") /* synthetic access */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);}}}}}
}

有兴趣可以自行研究一下。

这个工具类主要特点:

1.在获取LocalBroadcastManager对象实例的时候,这里用了单例模式。并且把外部传进来的Context 转化成了ApplicationContext,有效的避免了当前Context的内存泄漏的问题。这一点我们在设计单例模式框架的时候是值得学习的,看源码可以学习到很多东西。2.在LocalBroadcastManager构造函数中创建了一个Handler.可见 LocalBroadcastManager 的本质上是通过Handler机制发送和接收消息的。3.在创建Handler的时候,用了 context.getMainLooper() , 说明这个Handler是在Android 主线程中创建的,广播接收器的接收消息的时候会在Android 主线程,所以我们决不能在广播接收器里面做耗时操作,以免阻塞UI。4、LocalBroadcastManager采用的是Handler的消息机制来处理的广播,而注册到系统中的是通过Binder机制实现的,速度是应用内广播要快很多。不过由于Handler的消息机制是为了同一个进程的多线程间进行通信的,因而跨进程时无法使用应用内广播。

四、LocalBroadcastManager 总结

1、LocalBroadcastManager注册广播只能通过代码注册的方式。传统的广播可以通过代码和xml两种方式注册。

2、LocalBroadcastManager注册广播后,一定要记得取消监听。这一步可以有效的解决内存泄漏的问题。

3、LocalBroadcastManager采用的是Handler的消息机制来处理的广播,所以可以高效在应用内部通讯。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/118690.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2016年亚太杯APMCM数学建模大赛A题基于光学信息数据的温度及关键元素含量预测求解全过程文档及程序

2016年亚太杯APMCM数学建模大赛 A题 基于光学信息数据的温度及关键元素含量预测 原题再现 光含有能量&#xff0c;在一定条件下可以转化为热。燃烧是一种常见的现象&#xff0c;既能发光又能发热。光和热通常是同时存在的&#xff0c;一般来说&#xff0c;光强度越高&#xf…

2023年香水行业数据分析:国人用香需求升级,高端香水高速增长

在人口结构变迁的背景下&#xff0c;“Z世代”作为当下我国的消费主力&#xff0c;正在将“悦己”消费推动成为新潮流。具备经济基础的“Z世代”倡导“高颜值”、“个性化”、“精致主义”&#xff0c;这和香水、香氛为代表的“嗅觉经济”的特性充分契合&#xff0c;因此&#…

mysql中limit和offset的用法详细介绍

有的时候我们在学习或者工作中会使用到SQL语句&#xff0c;那么介绍一下limit和offset的使用方法。 mysql里分页一般用limit来实现&#xff0c;例如&#xff1a; 1、select* from user limit 3 表示直接取前三条数据 2、select * from user limit 1,3; 表示取1后面的第2,3,…

翻页视图ViewPager

ViewPager控件允许页面在水平方向左右滑动&#xff0c;就像翻书、翻报纸&#xff0c;Android提供了已经分装好的控件。对于ViewPager来说&#xff0c;一个页面就是一个项&#xff08;相当于ListView的一个列表项&#xff09;&#xff0c;许多页面组成ViewPager的页面项。 List…

【计算机网络笔记】网络应用对传输服务的需求

系列文章目录 什么是计算机网络&#xff1f; 什么是网络协议&#xff1f; 计算机网络的结构 数据交换之电路交换 数据交换之报文交换和分组交换 分组交换 vs 电路交换 计算机网络性能&#xff08;1&#xff09;——速率、带宽、延迟 计算机网络性能&#xff08;2&#xff09;…

LuatOS-SOC接口文档(air780E)--log - 日志库

常量 常量 类型 解释 log.LOG_SILENT number 无日志模式 log.LOG_DEBUG number debug日志模式 log.LOG_INFO number info日志模式 log.LOG_WARN number warning日志模式 log.LOG_ERROR number error日志模式 log.setLevel(level) 设置日志级别 参数 传入值…

实现打包后暴露可修改接口地址文件

### vue-cli3 实现打包后暴露可修改接口地址文件 在前后端分离的开发模式中,有时候我们开发好打包的文件在部署完之后,需要去修改获取接口的地址,这个时候就需要前端修改完之后重新打包再部署了,但是如果我们只是需要修改接口地址时,反复的重新打包就太麻烦了,接下来就分享一种…

Rust笔记【1】

元组和解构语法 let tup : (i32, f64, u8) (666, 2.0, 1);let tup (666, 2.0, 1); let (x, y, z) tup;let x tup.0; let y tup.1; let z tup.2;数组类型 数组定义是方括号&#xff1a;[ ] 元组定义是小圆括号&#xff1a;( ) 结构体定义是大括号&#xff1a;{ }&#xf…

USB学习(2):USB端点和传输协议(数据包、事物)详解

接着上一篇文章USB学习(1)&#xff1a;USB基础之接口类型、协议标准、引脚分布、架构、时序和数据格式&#xff0c;继续介绍一下USB的相关知识。 文章目录 1 USB端点(Endpoints)1.1 基本知识1.2 四种端点 2 传输协议2.1 数据包类型2.1.1 令牌数据包(Token packets)2.1.2 数据数…

【机器学习合集】激活函数合集 ->(个人学习记录笔记)

文章目录 综述1. S激活函数(sigmoid&Tanh)2. ReLU激活函数3. ReLU激活函数的改进4. 近似ReLU激活函数5. Maxout激活函数6. 自动搜索的激活函数Swish 综述 这些都是神经网络中常用的激活函数&#xff0c;它们在非线性变换方面有不同的特点。以下是这些激活函数的主要区别&am…

学习笔记二十三:Deployment入门到企业实战应用

Deployment入门到企业实战应用 Deployment控制器&#xff1a;概念、原理解读Deployment概述Deployment工作原理&#xff1a;如何管理rs和Pod&#xff1f;什么叫做更新节奏和更新逻辑呢 Deployment使用案例&#xff1a;创建一个web站点,2个副本deploy-demo详细解读 通过k8s实现滚…

分布式:一文搞定Redis/Zookeeper/MySQL实现分布式锁

目录 一、项目准备spring项目数据库 二、传统锁演示超卖现象使用JVM锁解决超卖解决方案JVM失效场景 使用一个SQL解决超卖使用mysql悲观锁解决超卖使用mysql乐观锁解决超卖四种锁比较Redis乐观锁集成Redis超卖现象redis乐观锁解决超卖 三、分布式锁概述四、Redis分布式锁实现方案…

Ubuntu 命令行设置静态IP地址方法

一、先ifconfig查看电脑的网卡信息 找到有线网络或WiFi网络的网卡名称&#xff0c;我这里是eno1 二、输入route -n命令&#xff0c;打印路由表&#xff0c;这里主要是为了查看网关地址 我这里网关地址是192.168.10.1 三、更改配置文件 输入 vim /etc/network/interfaces&am…

Spring Boot拦截器Interceptor

文章目录 Interceptor 作用原理自定义 InterceptorStep 1 实现/继承Step 2 重写方法Step 3 拦截器Interceptor注册例子 Interceptor 作用 日志记录&#xff1a;记录请求信息的日志&#xff0c;以便进行信息监控、信息统计、计算 PV&#xff08;Page View&#xff09;等&#x…

禾匠旧版对接微信小程序发货系统(发货信息管理 接口)

最近小程序如果是商家交易需要再小程序后台点击一下发货&#xff0c;特别麻烦&#xff0c;但是旧版的禾匠又没有这个功能&#xff0c;所以只能手动增加这个功能&#xff0c;但是每一个版本发货逻辑都不一样&#xff0c;大家只能自己手动去兼容一下&#xff0c;下面只是写了一个…

实例详解:Java使用JWT和Redis实现高效单点登录(SSO)

前言 单点登录&#xff08;Single Sign-On&#xff0c;简称SSO&#xff09;是一种身份验证和访问控制机制&#xff0c;允许用户使用一组凭证&#xff08;如登录名和密码&#xff09;登录到多个应用程序中&#xff0c;而无需为每个应用程序单独进行身份验证。用户只需要登录一次…

uniapp 单选框以及多选框样式更改

radio以及checkbox默认样式不符合自身需求时&#xff0c;根据自身需求更改样式&#xff0c;以下是自身的示例&#xff1a; 单选&#xff1a; 多选&#xff1a; 由于uniapp自身包含了一套默认的样式&#xff0c;所以如果不想全局更改只想在某个单据页面使用的话&#xff0c;就…

Mysql,SqlServer,Oracle获取库名 表名 列名

先看下需求背景&#xff1a; 获取某个数据源连接下所有库名&#xff0c;库下所有表名&#xff0c;表中所有字段 1.MySql 先说MySql吧&#xff0c;最简单 1.1获得所有数据库库名 这是一个mysql和sqlserver公用的方法&#xff0c;这里url不用担心数据库问题&#xff0c;他其实…

程序的编译和链接

目录 翻译环境 linux下的测试 ​编辑 预定义符号 执行环境 #define定义宏 #和## # ## 宏参数的副作用 宏和函数对比 优点 缺点 #undef 条件编译 头文件包含 在标准c的任何实现中&#xff0c;存在两种环境——翻译环境和执行环境 翻译环境 翻译环境生成目标文件…

2023年中国磷酸一铵行业产能、产量及发展趋势分析:农业需求稳定增长[图]

磷酸一铵是一种重要的氮磷复合肥料&#xff0c;在农业生产中广泛应用。亚洲地区尤其是中国、印度等国家是磷酸一铵的主要消费市场。近年来&#xff0c;中国磷酸一铵产能呈现下降趋势&#xff0c;2022年开始恢复增长。2022年中国磷酸一铵产能达1945万吨&#xff0c;同比增长5.4%…