【Android】程序开发组件—探究Jetpack
引言
Jetpack是一个开发组件工具集,它的主要目的是帮助我们编写出更加简洁的代码,并简化我们的开发过程,在这么多的组件当中,最需要我们关注的其实还是架构组件,接下来就对Jetpack的主要架构组件进行学习!
ViewModel
ViewModel的诞生
- 瞬时数据的丢失:在之前应该已经写过一些程序了,当我们开启屏幕旋转的时候会发现之间的数据丢失,这是因为在屏幕旋转的时候相当于新创建了一个当前活动
- 异步调用时的内存泄漏:UI 控制器(Activity 或 Fragment)有自己的生命周期,它们可能会在不需要时被销毁(例如,用户按下 Home 键,或者屏幕旋转)。然而,异步任务一旦开始,通常会在后台线程中运行,直到任务完成,不管 UI 控制器的状态如何,如果在异步任务中持有 UI 控制器的强引用(如直接引用 Activity),那么即使 UI 控制器已经被销毁,这个引用仍然存在。这意味着垃圾回收器(GC)不能回收 UI 控制器的实例,因为它仍然被异步任务引用,从而导致内存泄漏
- 类膨胀提高维护难度和测试难度:类似于活动当中的代码量过多
ViewModel的作用
它是介于View(视图)和Model(数据模型)之间的桥梁,使数据和视图能够分离,也能保持通信
实践:
新建一个活动放置一个TextView,和一个按钮,当每次按下这个按钮就会使TextView部分的数字加一,这个逻辑大家想必都能很快的写出来,找到按钮与文本控件,为按钮注册点击事件,每次使用getText()
与setText()
方法修改文本部分的内容,但当我们的屏幕进行旋转的时候,数据就会丢失变为0,因此使用ViewModel来解决这个问题,我们为按钮注册点击事件,创建一个MyViewModel继承于ViewModel,将文本内容的数字设置在这里
public class MyViewModel extends ViewModel {public int number;
}
主活动的代码:
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)
调用ViewModelProvider
的get
方法,传入MyViewModel.class
作为参数,以获取MyViewModel
的实例。
此时无论我们如何旋转屏幕数据都不会丢失!
ViewModel的生命周期
ViewModel
的生命周期比 Activity
或 Fragment
的生命周期更长,它不会因为配置更改(如屏幕旋转)而重建。
ViewModel 的生命周期
- 创建 (
onCreate
): 当ViewModel
首次被请求时,它会被创建。这通常发生在Activity
或Fragment
的onCreate
或onCreateView
方法中,通过ViewModelProvider
获取ViewModel
实例时。 - 准备 (
onCleared
):ViewModel
提供了一个onCleared()
方法,这是一个生命周期回调,当ViewModel
被清除并且即将销毁时会被调用。你可以在这个回调中执行清理工作,比如取消网络请求、注销观察者等。 - 活跃: 在
ViewModel
被创建后,它会保持活跃状态,直到与它关联的Activity
或Fragment
被销毁。即使Activity
因为配置更改(如屏幕旋转)而重建,ViewModel
也不会被销毁,它会保持原有的状态。 - 销毁 (
onCleared
): 当Activity
或Fragment
被销毁,并且没有任何其他组件与ViewModel
关联时,ViewModel
会被销毁。onCleared()
方法会在销毁时被调用。
ViewModel 与 Activity/Fragment 生命周期的关系
- Activity/Fragment 创建: 当
Activity
或Fragment
创建时,你可以通过ViewModelProvider
获取ViewModel
实例。如果ViewModel
已经存在(比如在屏幕旋转后重建Activity
时),则直接使用现有的实例。 - Activity/Fragment 重建: 如果
Activity
因为配置更改而重建,ViewModel
会保持不变,这意味着ViewModel
中的数据不会丢失。 - Activity/Fragment 销毁: 当
Activity
或Fragment
被销毁时,ViewModel
不会被销毁,除非没有任何Activity
或Fragment
与它关联。 - ViewModel 销毁: 当最后一个与
ViewModel
相关联的Activity
或Fragment
被销毁,并且ViewModel
没有被其他组件引用时,它会被销毁。
LiveData
和ViewModel之间的关系
当ViewModel中的数据发生变化的时候,LiveData告诉View
使用
简单应用
在活动当中设置一个TextView来显示数据,同样的我们创建一个MyViewModel来防止数据的丢失
public class MyViewModel extends ViewModel {private MutableLiveData<Integer> current;public MutableLiveData<Integer> getCurrent() {if (current == null) {current = new MutableLiveData<>();current.setValue(0);}return current;}
}
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
去实现。
- 新建一个
MyViewModel
继承于ViewModel
public class MyViewModel extends ViewModel {private MutableLiveData<Integer> progress;public MutableLiveData<Integer> getProgress() {if (progress == null) {progress.setValue(0);}return progress;}
}
- 对Fragment部分代码进行修改,另一个碎片的代码也是一样的
@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的优势
- 生命周期感知:
LiveData
遵循观察者的生命周期。它确保在观察者(如Activity
或Fragment
)处于活跃状态时才发送数据更新,从而避免在组件已经销毁后更新 UI 导致的内存泄漏。 - 避免内存泄漏: 由于
LiveData
与观察者的生命周期绑定,它不会向已经销毁的观察者发送更新,这减少了因 UI 控制器生命周期管理不当而导致的内存泄漏风险。 - 数据变化的响应式更新: 当数据发生变化时,
LiveData
会自动通知活跃的观察者。这种响应式编程模式使得 UI 能够自动响应数据的变化,而无需手动轮询数据。 - 可观察的数据存储:
LiveData
可以存储数据的最新状态,当观察者开始观察LiveData
时,它会立即接收到当前的数据状态,确保 UI 显示的数据是最新的。 - 支持转换操作:
LiveData
可以与Transformations
和MediatorLiveData
等工具结合使用,支持复杂的数据转换和组合操作,使得数据的处理更加灵活和强大。 - 线程安全:
LiveData
的更新操作是线程安全的,你可以在后台线程中更新LiveData
的数据,而无需担心线程同步问题。 - 简化数据共享:
LiveData
可以简化不同组件间的数据共享,特别是当多个组件需要观察同一份数据时,LiveData
提供了一种简洁的观察和更新机制。 - 易于测试: 由于
LiveData
的数据更新是可观察的,你可以更容易地编写测试用例来验证数据更新是否按预期触发了 UI 的变化。 - 减少 boilerplate 代码:
LiveData
减少了在 Activity 或 Fragment 中处理数据更新和 UI 响应的样板代码,使得代码更加简洁和易于维护。 - 支持数据恢复: 在配置更改(如屏幕旋转)后,
LiveData
可以帮助恢复数据状态,因为它保持了数据的最新值,直到观察者重新观察它。
DataBinding
意义:让布局文件承担了部分原本属于页面的工作,使页面与布局耦合度进一步降低
应用
基本应用
根据一个例子来理解吧!
- 新建一个活动,活动当中放置一个图片和两个
TextView
控件,我们之前为TextView
设置内容都是在主活动当中先获取到控件,在使用setText()
方法设置,现在我们试试不在活动当中,此时的布局文件大家都会写,在这里就不展示了 - 打开build.gradle文件,添加以下内容,注意不要忘记
sync now
- 鼠标位于布局文件的最前端点击alt+回车,就会出现以下内容,我们需要添加红色边框部分内容
<data>
:这个元素定义了布局文件中可以使用的数据源。<variable
:这个元素定义了一个变量,它可以在布局文件中被引用。name="idol"
:这是变量的名称,在布局文件中可以通过这个名称来引用变量。type="com.example.databinding.Idol"
:这是变量的类型,它指定了变量的数据类型。在这个例子中,Idol
是一个Java类,它可能定义了一些属性,名字、等级,这些属性可以在布局文件中被绑定到UI组件上。
- 对主活动进行修改
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
上。
- 对布局文件进行修改
接下来运行程序:
Important标签和事件绑定
- Important标签
若在我们创建的时候有一个值为整型怎么办,例如在Idol当中将star设置为整型,此时我们就需要将整形转化为字符串类型,创建一个类
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" />
此时在创建对象的时候:
Idol idol = new Idol("熊大", 1);
运行程序,也会得到相应的结果。
<import>
:这是Data Binding布局文件中的一个XML标签,用于导入Java类。type
:这是<import>
标签的一个属性,用于指定要导入的类或包的全限定名。
- 与事件绑定
设置一个按钮,为点击按钮注册事件
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的代码与之前的一级页面一样,注意不要忘记添加资源部分。此时运行程序的结果与之前一样。
文章到这里就结束了!
相关文章:

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

pytorch torch.norm函数介绍
torch.norm 函数用于计算张量的范数(norm),可以理解为张量的“长度”或“大小”。根据范数的不同类型,它可以衡量不同的张量性质。该函数可以计算 向量 和 矩阵 的多种范数,如 L1范数、L2范数、无穷范数 等。 1. 函数…...

【lc_hot100】刷题心得
链表 二叉树 二叉树相关的题目基本都有个基本的框架,基本框架就是二叉树的四种遍历方法:前序遍历、中序遍历、后序遍历、层序遍历 往往常用的是前序遍历和中序遍历 图 图跟二叉树一样都有自己的的基本框架,基本框架就是图的两种遍历方法&am…...

FANUC 数控 A06B-6058-H227 伺服放大器
发那科伺服放大器是一种控制电机的电子装置,属于电机控制系统的一部分,用于将输入信号放大并转换成电动机可以理解的信号,从而实现运动控制和定位。 发那科伺服放大器的主要作用包括: 实现运动控制:通过控制…...

Python将表格文件中某些列的数据整体向上移动一行
本文介绍基于Python语言,针对一个文件夹下大量的Excel表格文件,对其中的每一个文件加以操作——将其中指定的若干列的数据部分都向上移动一行,并将所有操作完毕的Excel表格文件中的数据加以合并,生成一个新的Excel文件的方法。 首…...

基于YOLOv8的PCB缺陷检测算法,加入一种基于内容引导注意力(CGA)的混合融合方案(一)
💡💡💡本文内容:针对基于YOLOv8的PCB缺陷检测算法进行性能提升,加入各个创新点做验证性试验。 1)提出了一种基于内容引导注意力(CGA)的混合融合方案,mAP0.5由原始的0.966提升至0.975 1.PCB缺陷…...

如何在红米手机中恢复已删除的照片?(6 种方式可供选择)
凭借出色的相机和实惠的价格,小米红米系列已成为全球知名品牌。但是,最近有些人抱怨他们在 红米设备上丢失了许多珍贵的图片或视频,并希望弄清楚如何从小米手机恢复已删除的照片。好吧,在小米设备上恢复已删除的视频/照片并不难。…...

嵌入式实时操作系统(RTOS):原理、应用与发展
摘要:本文围绕嵌入式实时操作系统(RTOS)展开。首先介绍嵌入式系统与实时操作系统的概念,阐述嵌入式 RTOS 的体系结构。接着分析其关键特性,包含任务管理(如任务的创建与删除、调度、同步与通信)…...

C#里使用位图容器BitArray
由于经常需要操作一些位表示的数据结构,那么就需要采用位图的管理方式。 在C#里就是使用BitArray来管理位图数据结构,这样就比较方便处理。 这个类可以有多种构造函数,可以满足绝大部分的要求。 比如从网络协议里传送过来的字节流,就可以直接写入到里面,就可以直接获取…...

如何在 Kali Linux 上安装 pip3
如何在 Kali Linux 上安装 pip3 在 Kali Linux 上安装 pip3 的过程非常简单。按照以下步骤,你可以轻松完成安装并开始使用 pip3 管理 Python 软件包。 步骤 1:打开终端 首先,打开你的 Kali Linux 终端。 步骤 2:更新软件包列表…...

5.2 排列与代数余子式
一、求行列式的方法 计算机是利用主元计算行列式的。本节介绍其它两种计算行列式的方法。一是 “大公式”(big formula),它使用了全部 n ! n! n! 个排列计算;二是 “代数余子式公式”(cofactor formula)&…...

java框架第五课(终极版本)SpringBoot
一.关于SpringBoot (1)回忆Spring 传统的Spring由Spring 框架(ioc,aop)加mybatis加Springweb组成,虽然相比原生的java程序Spring框架帮我们大大减少了代码量,减少了冗余,提高了开发效率但是由于Spring框架下的配置和相关的jar包依赖过多&am…...

聚类案例——汽车是否值得购买
对汽车是否值得购买,进行聚类分析: 1、数据指标解释: buying, 购买费用 maint, 维修费用 doors, 车门数量 person, 乘坐人数 lug_boot, 行李箱容量 safety, 安全性 2、对数据进行转换 将字符串转换映射量化为数字 数据加载:…...

网络编程9.10
使用数据库完成工人管理系统: ubuntuubuntu:DB$ ubuntuubuntu:DB$ cat 2.c #include <myhead.h> #include <sqlite3.h> #include <string.h>typedef struct {int id;char name[20];double salary; } Worker;int do_insert(sqlite3 *ppDb) {Worker work;pri…...

如何在SQL Server中恢复多个数据库?
一次性恢复多个 SQL数据库吗可以吗? "是的,可以一次性恢复多个 SQL 数据库。通常情况下,只要备份文件的名称与相应的数据库匹配,且没有附加的日期或时间信息,就可以通过有效的 T-SQL 脚本来完成恢复。如果你希望…...

炸裂!新版 SD WebUI Forge 出图速度更快!支持最新Flux 模型!(保姆级安装教程)
大家是不是经常为SD WebUI卡顿、爆显存而苦恼?一启动SD 电脑就开始发烫, 尤其低显存用户屡屡"中招",不得不一遍遍重启。作为AI绘画的必备工具,WebUI却还有这么多"坑",着实让人不爽!😠 好消息是,…...

laserOdometry.cpp源码注释
本博客用于a-loam源码学习,用于和slam初学者一起学习。 #include <cmath>#include <nav_msgs/Odometry.h>#include <nav_msgs/Path.h> //这两行代码是C中包含头文件的指令,它们用于在ROS(Robot Operating System…...

STM32时钟配置图详解
一图概述: 左侧输入时钟源 Input Frequency (LSE/LSI/HSI/HSE) LSE (Low-Speed External):外部32.768 kHz晶体振荡器,通常用于RTC(实时时钟)。LSI (Low-Speed Internal):内部低速时钟,频率为…...

Vscode——调试时,修改变量值
第一步:点击变量,鼠标右键 第二步:点击 设置值...

1. 初识LLM API:环境配置与多轮对话演示
其实AI应用并不是一个什么很高大上的东西,你可以将它当作一个文字的“调库”行为,“调库”只需要知道库名就行了,这里实际也是如此。甚至你只需要知道你想问什么,将你的消息作为输入,就能从大模型得到输出。而这个“库…...

【AI编程助手】VsCode插件--通义灵码
目录 摘要 一、插件安装 二、“通义灵码” 使用 三、官网教程 四、总结 五、参考信息 摘要 通义灵码是一款强大的 AI 编程助手。它能够理解编程相关的复杂逻辑,为开发者提供高效、准确的代码生成与优化建议。在编程过程中,它可以辅助处理各种任务&…...

9月10号的学习
//界面1 头文件 signals://界面1的自定义信号void my_signal(); private slots:void on_pushButton_2_clicked();void on_pushButton_clicked(); //界面1 .cpp文件 void Widget::on_pushButton_2_clicked() {QMessageBox msg(QMessageBox::Warning,"警告","是否…...

QtC++截图支持窗口获取
介绍 在截图工具中你会发现,接触到窗口后会自动圈出目标窗口,个别强大一点的还能进行元素识别可以自动圈出元素,那么今天简单分析一下QTc如何获取窗口并圈出当前鼠标下的窗口。 介绍1.如何获取所有窗口2.比较函数3.实现窗口判断 结尾 1.如何获取所有窗口…...

料品档案没有配置主供应商信息
这个问题经常会出现在普通用户的面前。没有合适的工程人员去打理料品档案。信息是缺漏的。用友给出来的提示,也让人摸不着头脑。只能是记下来备用吧。...

springboot属性加载优先级和常见命令行属性
属性加载优先级: 1.SpringApplication:启动的main方法里注入的属性 2.PropertySource:通过注解 加载的数据配置文件 3.Config data file:application.yml/.properties 4.OS environment variable:环境变量 5.Command l…...

Math Reference Notes: 因式定理
文章目录 1. 因式定理的定义2. 因式定理的数学表达:3. 因式定理的推导4. 因式定理的含义5. 因式定理的应用6. 因式定理与余式定理的关系7. 因式定理的应用领域8.因式定理的局限性 因式定理是多项式代数中的一个重要工具,帮助我们通过多项式的根来因式分解…...

Kubernetes------Service
目录 一、属性说明 二、定义和基本配置 1、定义 2、创建Service 2.1、typeClusterIP 2.2、typeNodePort 2.3、固定IP访问 三、Service、EndPoint、Pod之间的关系 四、服务发现 1、基于Service中IP访问外部服务 2、基于Service中域名访问外部服务 五、Ingress的安装和使…...

C#的LINQ语句
在 C# 中,LINQ(Language Integrated Query)是一种强大的查询技术,它允许你使用熟悉的 C# 语法来查询数据集合。LINQ 可以用于查询各种数据源,包括数组、列表、数据集、SQL数据库等。 以下是一些基本的 LINQ 语句示例&…...

项目实战系列三: 家居购项目 第三部分
文章目录 🍃后台分页🍅后台分页导航 🍃首页分页🍅首页分页导航🍅首页搜索🍅两个奇怪的问题🍅会员显示登录名🍅注销登录🍅验证码 🍃后台分页 程序框架图 1.…...

【WPF】Border的使用
在 WPF 中,Border 控件是一个非常实用的容器控件,它可以用来为其他控件添加边框、背景颜色、边距等样式。Border 本身没有内置的行为,但是它可以包含一个子元素,并且可以通过各种属性来自定义外观。 Border基本属性 Child: 表示…...