【Android】程序开发组件—探究Jetpack

news/2024/9/17 8:30:01/ 标签: android, java

引言

Jetpack是一个开发组件工具集,它的主要目的是帮助我们编写出更加简洁的代码,并简化我们的开发过程,在这么多的组件当中,最需要我们关注的其实还是架构组件,接下来就对Jetpack的主要架构组件进行学习!

ViewModel

ViewModel的诞生

  1. 瞬时数据的丢失:在之前应该已经写过一些程序了,当我们开启屏幕旋转的时候会发现之间的数据丢失,这是因为在屏幕旋转的时候相当于新创建了一个当前活动
  2. 异步调用时的内存泄漏:UI 控制器(Activity 或 Fragment)有自己的生命周期,它们可能会在不需要时被销毁(例如,用户按下 Home 键,或者屏幕旋转)。然而,异步任务一旦开始,通常会在后台线程中运行,直到任务完成,不管 UI 控制器的状态如何,如果在异步任务中持有 UI 控制器的强引用(如直接引用 Activity),那么即使 UI 控制器已经被销毁,这个引用仍然存在。这意味着垃圾回收器(GC)不能回收 UI 控制器的实例,因为它仍然被异步任务引用,从而导致内存泄漏
  3. 类膨胀提高维护难度和测试难度:类似于活动当中的代码量过多

ViewModel的作用

它是介于View(视图)和Model(数据模型)之间的桥梁,使数据和视图能够分离,也能保持通信
在这里插入图片描述

实践:

新建一个活动放置一个TextView,和一个按钮,当每次按下这个按钮就会使TextView部分的数字加一,这个逻辑大家想必都能很快的写出来,找到按钮与文本控件,为按钮注册点击事件,每次使用getText()setText()方法修改文本部分的内容,但当我们的屏幕进行旋转的时候,数据就会丢失变为0,因此使用ViewModel来解决这个问题,我们为按钮注册点击事件,创建一个MyViewModel继承于ViewModel,将文本内容的数字设置在这里

java">public class MyViewModel extends ViewModel {public int number;
}

主活动的代码:

java">public class MainActivity extends AppCompatActivity {private TextView textView;private MyViewModel viewModel;@Overrideprotected void onCreate(Bundle savedInstanceState) {......textView = findViewById(R.id.Textview);viewModel = new ViewModelProvider(this, new ViewModelProvider.AndroidViewModelFactory(getApplication())).get(MyViewModel.class);textView.setText(String.valueOf(viewModel.number));}//为按钮注册的点击事件public void plus(View view) {textView.setText(String.valueOf(++viewModel.number));}
}

大部分的代码之间经常看到,分析一下中间的代码:

  • ViewModelProvider是Android架构组件库中的一个类,用于获取ViewModel实例。
  • this参数传递的是当前的Activity实例。
  • new ViewModelProvider.AndroidViewModelFactory(getApplication())创建了一个AndroidViewModelFactory实例,它是一个工厂类,用于创建AndroidViewModel类型的ViewModel实例。getApplication()方法用于获取当前应用程序的上下文。
  • .get(MyViewModel.class)调用ViewModelProviderget方法,传入MyViewModel.class作为参数,以获取MyViewModel的实例。

此时无论我们如何旋转屏幕数据都不会丢失!

ViewModel的生命周期

ViewModel 的生命周期比 ActivityFragment 的生命周期更长,它不会因为配置更改(如屏幕旋转)而重建。

ViewModel 的生命周期

  1. 创建 (onCreate): 当 ViewModel 首次被请求时,它会被创建。这通常发生在 ActivityFragmentonCreateonCreateView 方法中,通过 ViewModelProvider 获取 ViewModel 实例时。
  2. 准备 (onCleared): ViewModel 提供了一个 onCleared() 方法,这是一个生命周期回调,当 ViewModel 被清除并且即将销毁时会被调用。你可以在这个回调中执行清理工作,比如取消网络请求、注销观察者等。
  3. 活跃: 在 ViewModel 被创建后,它会保持活跃状态,直到与它关联的 ActivityFragment 被销毁。即使 Activity 因为配置更改(如屏幕旋转)而重建,ViewModel 也不会被销毁,它会保持原有的状态。
  4. 销毁 (onCleared): 当 ActivityFragment 被销毁,并且没有任何其他组件与 ViewModel 关联时,ViewModel 会被销毁。onCleared() 方法会在销毁时被调用。

ViewModel 与 Activity/Fragment 生命周期的关系

  • Activity/Fragment 创建: 当 ActivityFragment 创建时,你可以通过 ViewModelProvider 获取 ViewModel 实例。如果 ViewModel 已经存在(比如在屏幕旋转后重建 Activity 时),则直接使用现有的实例。
  • Activity/Fragment 重建: 如果 Activity 因为配置更改而重建,ViewModel 会保持不变,这意味着 ViewModel 中的数据不会丢失。
  • Activity/Fragment 销毁: 当 ActivityFragment 被销毁时,ViewModel 不会被销毁,除非没有任何 ActivityFragment 与它关联。
  • ViewModel 销毁: 当最后一个与 ViewModel 相关联的 ActivityFragment 被销毁,并且 ViewModel 没有被其他组件引用时,它会被销毁。

LiveData

和ViewModel之间的关系

当ViewModel中的数据发生变化的时候,LiveData告诉View

在这里插入图片描述

使用

简单应用

在活动当中设置一个TextView来显示数据,同样的我们创建一个MyViewModel来防止数据的丢失

java">public class MyViewModel extends ViewModel {private MutableLiveData<Integer> current;public MutableLiveData<Integer> getCurrent() {if (current == null) {current = new MutableLiveData<>();current.setValue(0);}return current;}
}
java">public class MainActivity extends AppCompatActivity {TextView textView;MyViewModel viewModel;@Overrideprotected void onCreate(Bundle savedInstanceState) {......textView = findViewById(R.id.text);viewModel = new ViewModelProvider(this, new ViewModelProvider.AndroidViewModelFactory(getApplication())).get(MyViewModel.class);textView.setText(String.valueOf(viewModel.getCurrent().getValue()));viewModel.getCurrent().observe(this, new Observer<Integer>() {@Overridepublic void onChanged(Integer i) {textView.setText(String.valueOf(i));}});startTimer();}private void startTimer() {new Timer().schedule(new TimerTask() {@Overridepublic void run() {//非UI线程postValue()//UI线程setValue()viewModel.getCurrent().postValue(viewModel.getCurrent().getValue() + 1);}}, 1000, 1000);}
}

此时运行程序:

ViewModel与LiveData实现Fragment之间的数据传递

我们创建一个活动,在这个活动当中放置两个碎片,碎片当中放置各自放置一个滑动条,此时活动的布局如下:

当我们拖动碎片当中的一条滑动条,另一个是不动的,因为我们没有将两个碎片联系起来,若我们想将两个滑动条进行关联,使滑动一条滑动条另一个也跟着动就会想到通过Fragment之间的数据传递部分进行联系,就会使主活动的代码量非常大并且 Fragment 之间高度耦合,这使得代码难以维护和扩展。此时我们就可以使用 ViewModel + LiveData去实现。

  1. 新建一个MyViewModel继承于ViewModel
java">public class MyViewModel extends ViewModel {private MutableLiveData<Integer> progress;public MutableLiveData<Integer> getProgress() {if (progress == null) {progress.setValue(0);}return progress;}
}
  1. 对Fragment部分代码进行修改,另一个碎片的代码也是一样的
java">@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {View root = inflater.inflate(R.layout.fragment_blank, container, false);SeekBar seekBar = root.findViewById(R.id.progress_bar1);MyViewModel myViewModel = new ViewModelProvider(getActivity(), new ViewModelProvider.AndroidViewModelFactory(getActivity().getApplication())).get(MyViewModel.class);//第一处myViewModel.getProgress().observe(getActivity(), new Observer<Integer>() {@Overridepublic void onChanged(Integer i) {seekBar.setProgress(i);}});//第二处seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {//第三处@Overridepublic void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {myViewModel.getProgress().setValue(progress);}@Overridepublic void onStartTrackingTouch(SeekBar seekBar) {//当用户开始触摸 SeekBar 时,这个方法会被调用}@Overridepublic void onStopTrackingTouch(SeekBar seekBar) {//当用户停止触摸 SeekBar 时,这个方法会被调用}});return root;
}
  • 第一处:这行代码注册了一个观察者(Observer)来监听 myViewModel.getProgress() LiveData 中的数据变化。当 LiveData 中的值发生变化时,onChanged(Integer i) 方法会被调用,并且新的进度值 i 会被传递到这个方法中。
  • 第二处:为 SeekBar 设置了一个监听器,用于监听用户与 SeekBar 的交互(如拖动进度条),当进度条发生变化的时候就会执行里面的方法
  • 第三处:当用户拖动 SeekBar 或 SeekBar 的进度发生变化时,这个方法会被调用,progress 参数表示 SeekBar 当前的进度值。fromUser 参数是一个布尔值,表示进度变化是否由用户操作引起(如果是程序调用 setProgress 则为 false)。

我们并没有对主活动进行任何的代码添加与修改,接下来运行程序:

LiveData的优势

  1. 生命周期感知LiveData 遵循观察者的生命周期。它确保在观察者(如 ActivityFragment)处于活跃状态时才发送数据更新,从而避免在组件已经销毁后更新 UI 导致的内存泄漏。
  2. 避免内存泄漏: 由于 LiveData 与观察者的生命周期绑定,它不会向已经销毁的观察者发送更新,这减少了因 UI 控制器生命周期管理不当而导致的内存泄漏风险。
  3. 数据变化的响应式更新: 当数据发生变化时,LiveData 会自动通知活跃的观察者。这种响应式编程模式使得 UI 能够自动响应数据的变化,而无需手动轮询数据。
  4. 可观察的数据存储LiveData 可以存储数据的最新状态,当观察者开始观察 LiveData 时,它会立即接收到当前的数据状态,确保 UI 显示的数据是最新的。
  5. 支持转换操作LiveData 可以与 TransformationsMediatorLiveData 等工具结合使用,支持复杂的数据转换和组合操作,使得数据的处理更加灵活和强大。
  6. 线程安全LiveData 的更新操作是线程安全的,你可以在后台线程中更新 LiveData 的数据,而无需担心线程同步问题。
  7. 简化数据共享LiveData 可以简化不同组件间的数据共享,特别是当多个组件需要观察同一份数据时,LiveData 提供了一种简洁的观察和更新机制。
  8. 易于测试: 由于 LiveData 的数据更新是可观察的,你可以更容易地编写测试用例来验证数据更新是否按预期触发了 UI 的变化。
  9. 减少 boilerplate 代码LiveData 减少了在 Activity 或 Fragment 中处理数据更新和 UI 响应的样板代码,使得代码更加简洁和易于维护。
  10. 支持数据恢复: 在配置更改(如屏幕旋转)后,LiveData 可以帮助恢复数据状态,因为它保持了数据的最新值,直到观察者重新观察它。

DataBinding

意义:让布局文件承担了部分原本属于页面的工作,使页面与布局耦合度进一步降低

应用

基本应用

根据一个例子来理解吧!

  1. 新建一个活动,活动当中放置一个图片和两个TextView控件,我们之前为TextView设置内容都是在主活动当中先获取到控件,在使用setText()方法设置,现在我们试试不在活动当中,此时的布局文件大家都会写,在这里就不展示了
  2. 打开build.gradle文件,添加以下内容,注意不要忘记sync now

在这里插入图片描述

  1. 鼠标位于布局文件的最前端点击alt+回车,就会出现以下内容,我们需要添加红色边框部分内容

在这里插入图片描述

  • <data>:这个元素定义了布局文件中可以使用的数据源。
  • <variable:这个元素定义了一个变量,它可以在布局文件中被引用。
    • name="idol":这是变量的名称,在布局文件中可以通过这个名称来引用变量。
    • type="com.example.databinding.Idol":这是变量的类型,它指定了变量的数据类型。在这个例子中,Idol是一个Java类,它可能定义了一些属性,名字、等级,这些属性可以在布局文件中被绑定到UI组件上。
  1. 对主活动进行修改
java">ActivityMainBinding activityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
Idol idol = new Idol("熊大", "五星");
activityMainBinding.setIdol(idol);
  • activityMainBinding:这是一个变量,它存储了绑定类的实例,可以通过这个变量访问布局文件中定义的视图和变量。
  • DataBindingUtil.setContentView:这是一个静态方法,用于将布局文件绑定到当前的Activity。它返回一个绑定类(在这个例子中是ActivityMainBinding)的实例,这个绑定类是由Data Binding库根据布局文件自动生成的。
  • setIdol:这是在ActivityMainBinding类中定义的一个方法,它用于将Idol对象绑定到布局文件中定义的变量idol上。
  1. 对布局文件进行修改

在这里插入图片描述

接下来运行程序:

在这里插入图片描述

Important标签和事件绑定

  1. Important标签

若在我们创建的时候有一个值为整型怎么办,例如在Idol当中将star设置为整型,此时我们就需要将整形转化为字符串类型,创建一个类

java">public class StarUtils {public static String getStar(int star) {switch (star) {case 1:return "一星";case 2:return "二星";case 3:return "三星";case 4:return "四星";}return "";}
}

修改xml文件

<data><variablename="idol"type="com.example.databinding.Idol" /><import type="com.example.databinding.StarUtils"/>
</data>
<TextViewandroid:id="@+id/dengji"android:layout_gravity="center"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="@{StarUtils.getStar(idol.star)}"android:textSize="25sp" />

此时在创建对象的时候:

java">Idol idol = new Idol("熊大", 1);

运行程序,也会得到相应的结果。

  • <import>:这是Data Binding布局文件中的一个XML标签,用于导入Java类。
  • type:这是<import>标签的一个属性,用于指定要导入的类或包的全限定名。
  1. 与事件绑定

设置一个按钮,为点击按钮注册事件

java">public class EventHander {private Context context;public EventHander(Context context) {this.context = context;}public void buttonOnclick(View view) {Toast.makeText(context, "喜欢", Toast.LENGTH_SHORT).show();}
}

将这个类添加到绑定资源当中

<data><variablename="idol"type="com.example.databinding.Idol" /><variablename="eventbutton"type="com.example.databinding.EventHander" /><import type="com.example.databinding.StarUtils"/>
</data>

按钮控件进行添加绑定

android:onClick="@{eventbutton.buttonOnclick}"

此时运行程序,当按下按钮

在这里插入图片描述

二级页面的绑定

标签引用二级页面

在这里插入图片描述

我们将上面布局的按钮进行删除,将两个TextView控件放在一个xml文件之下,使主活动的xml文件引用这个xml文件,此时我们就不需要在一级页面将star进行转换,将Import标签进行删除,并对引用布局部分进行标签传递修改

<include layout="@layout/sub"app:idol="@{idol}"/>

在二级页面当中TextView的代码与之前的一级页面一样,注意不要忘记添加资源部分。此时运行程序的结果与之前一样。

文章到这里就结束了!


http://www.ppmy.cn/news/1521948.html

相关文章

Jmeter终极线程组“Ultimate Thread Group“如何使用?

1、安装,点击"选项"&#xff0c;再点击"Plugins Manager"&#xff0c;下载"Custom Thread Groups" 2、添加"jpgc - Ultimate Thread Group" 3、"jpgc - Ultimate Thread Group"使用

从C到Py:面向对象程序设计

面向对象思想 我们先来介绍一下两大编程思想&#xff0c;其一是面向过程&#xff0c;它强调功能上的封装&#xff0c;运用的是简单的线性思维&#xff0c;二是面向对象&#xff0c;它主要是对属性和行为上的封装&#xff0c;处理复杂的事物。 面向过程的典型语言是C语言&…

西门子PLC MODBUS TCP通信

1. PLC要读设备的数据&#xff0c;PLC作为CLIENT&#xff08;客户端&#xff09;&#xff0c;第三方设备作为SERVER(服务端)。https://zhuanlan.zhihu.com/p/705926399 2.PLC作为服务端 3.1 Modbus TCP 通信S7-1200Modbus TCP通信 Modbus TCP 服务器指令 MB_SERVER V5.0 新功…

android 15升级适配无法安装运行,并且提示应用未安装

最近升级项目到android15&#xff0c;一开始遇到这个报错&#xff0c;以为是广播的问题&#xff0c;因为当时在Log中发现这个的报错 One of RECEIVER_EXPORTED or RECEIVER_NOT_EXPORTED should be 这条报错的解决方案依据 google开发者android14 关于这条的 原文&#xff1a; …

Git撤销add

git要提交版本第一步是add&#xff0c;就算是文件本身已经存在只是修改&#xff0c;也需要添加&#xff0c;即添加到暂存区。其中最偷懒和也保险的命令是&#xff1a; git add . 即添加了本地&#xff08;多称工作目录&#xff09;所有文件。 撤销add有以下文章&#xff1a; …

了解一下HTTP 与 HTTPS 的区别

介绍&#xff1a; HTTP是超文本传输协议。规定了客户端&#xff08;通常是浏览器&#xff09;和服务器之间如何传输超文本&#xff0c;也就是包含链接的文本。通常使用TCP【1】/IP协议来传输数据&#xff0c;默认端口为80。 HTTPS是超文本传输安全协议&#xff0c;具有CA证书。…

【2024数模国赛赛题思路公开】国赛D题思路丨附可运行代码丨无偿自提

2024年国赛D题解题思路 问题一 【题目】 投射一枚深弹&#xff0c;潜艇中心位置的深度定位没有误差&#xff0c;两个水平坐标定位均服从正态分布。分析投弹最大命中概率与投弹落点平面坐标及定深引信引爆深度之间的关系&#xff0c;并给出使得投弹命中概率最大的投弹方案&…

【时时三省】(C语言基础)指针进阶 例题3

山不在高&#xff0c;有仙则名。水不在深&#xff0c;有龙则灵。 ----CSDN 时时三省 例题&#xff1a; 这个arr里面是放了&#xff3b;a b c d e f \0&#xff3d; 第一个arr 这个sizeof是计算这个数组的大小 这个里面加上\0一共是7个元素 所以打印7 第二个arr0 这个指数组…

SQL进阶技巧:每年在校人数统计 | 区间重叠问题

目录 0 问题分析 1 数据准备 2 问题分析 3 小结 区间重叠问题 0 问题分析 有一个录取学生人数表 in_school_stu,记录的是每年录取学生的人数及录取学生的学制,计算每年在校学生人数。 1 数据准备 create table in_school_stu as ( select stack(5,1,2001,2,1200,2,2000…

CSS-动态计算高度

要在 CSS 中动态计算元素的高度&#xff0c;你可以使用几种方法&#xff0c;主要取决于你需要的具体效果和布局。以下是一些常见的方法&#xff1a; 1. 使用 calc() 函数 calc() 允许你在 CSS 中进行动态计算。例如&#xff0c;设置一个元素的高度为视口高度减去一个固定的像…

Unity(2022.3.41LTS) - UI简介,了解

目录 零.简介 一、主要组件 二、布局组件 四、创建和编辑 UI 五、代码控制 UI 六、优化和注意事项 零.简介 Unity UI&#xff08;用户界面&#xff09;是 Unity 引擎中用于创建游戏界面和交互元素的强大工具集。 一、主要组件 Canvas&#xff08;画布&#xff09;&…

Iceberg与Spark整合环境配置

版本对应关系 参考官网 核心依赖jar包 从官网下载对应版本的iceberg-spark-runtime-xxx.jar包。 利用spark配置参数–package或–jars引入 –packages 参数允许你指定 Maven 仓库中的包&#xff0c;这些包将会被自动下载并添加到 Spark 的类路径中。这对于使用 Maven 协调…

【华为OD】2024D卷——剩余银饰的重量

题目描述&#xff1a; 有N块二手市场收集的银饰&#xff0c;每块银饰的重量都是正整数&#xff0c;收集到的银饰会被熔化用于打造新的饰品。 每一回合&#xff0c;从中选出三块最重的银饰&#xff0c;然后一起熔掉。 假设银饰的重量分别为x、y和z&#xff0c;且x<y<z。那…

如何防止IP地址被篡改

1. 强化网络安全基础设施 使用防火墙和入侵检测系统&#xff08;IDS&#xff09;&#xff1a;部署防火墙可以过滤掉未授权的网络流量&#xff0c;而IDS 则能够实时监控网络活动&#xff0c;检测并阻止任何异常或可疑行为&#xff0c;包括IP地址的篡改尝试。 配置安全路由器和…

oracle 清空表数据、

在 Oracle 数据库中&#xff0c;清空表数据可以通过以下几种方式实现&#xff1a; 1. 使用 TRUNCATE 语句 TRUNCATE 语句是最快的方式来删除表中的所有数据。它会删除所有行&#xff0c;并且不记录每行删除的详细信息&#xff0c;因此速度比 DELETE 语句快。它还会释放表的空…

相机常见名词详解

本文主要参考超人视觉课程做的笔记&#xff0c;有讲解不太懂的&#xff0c;又做了详细的解释 1、物距&#xff1a;物体到镜片的距离&#xff1b; 2、像距&#xff1a;像到镜片的距离&#xff1b; 3、焦距&#xff1a;镜片到焦点的距离&#xff1b; (1)二倍焦距以外&#xff…

CosyVoice:开源强大的 AI 语音合成工具

在当今科技飞速发展的时代&#xff0c;AI 语音合成技术正逐渐改变着我们的生活。今天&#xff0c;就为大家介绍一款卓越的语音合成工具——CosyVoice。 一、安装步骤 克隆和安装&#xff1a; 克隆仓库&#xff1a;git clone --recursive https://github.com/FunAudioLLM/Cos…

滑块验证是否人机

效果图&#xff1a; 原理&#xff1a; 使用阿里第三方验证插件js生成滑块&#xff0c;默认获取验证码按钮为不可点击属性 .getyzm{pointer-events: none;cursor: default;} 再添置一个可点击属性的类 .getyzmok{color: #000000 !important;pointer-events: visible;} 当滑块滑动…

Elasticsearch检索原理

Elasticsearch 的检索原理主要基于其内部使用的倒排索引结构&#xff0c;以及诸如BM25等相关性评分算法。 查询解析 当用户提交查询时&#xff0c;Elasticsearch 接收和解析该请求&#xff0c;包括确定查询类型&#xff08;如Match、Bool、Term等&#xff09;和相关字段。解析…

vsstudio2019,windows平台,使用DeviceIOControl向大容量存储设备发起SCSI通信,读写其扇区,绕过文件系统的排查;

源码&#xff1a; 电脑插入U盘&#xff0c;为物理驱动器3 如下使用DeviceIOControl发送MSC类规定的SCSI通信指令中 读指令&#xff08;0x28&#xff09; 指定读0扇区&#xff0c;读1长度的扇区&#xff0c;一共长度为512字节 #include <windows.h> #include <std…