Jetpack业务架构—四件套(Lifecycle、ViewModel、LiveData、DataBinding)

news2024/12/26 9:20:23

Jetpack 是一个由多个库组成的套件,可帮助开发者遵循最佳做法、减少样板代码并编写可在各种 Android 版本和设备中一致运行的代码,让开发者可将精力集中于真正重要的编码工作。

Android Jetpack组件的优势:

Jetpack推出的主要目的是为了能够让开发者更加快速、方便以及高质量的完成产品开发

  • 轻松管理应用程序的生命周期,后台任务的管理,导航的处理等
  • 利用Jetpack组件进行开发可以有效减少内存溢出、崩溃的概率,提升应用开发的质量

Jetpack中包含的库包括:

  1. ViewModel:帮助管理UI组件的生命周期并存储和管理UI相关的数据。
  2. LiveData:提供了响应式编程的功能,可以让数据在数据源发生变化时自动更新UI。
  3. Room:提供了一个抽象层,可以让开发者方便地访问和管理SQLite数据库。
  4. Navigation:提供了一种简单、一致的方式来处理应用程序的导航。
  5. WorkManager:提供了一种简单、可靠的方式来管理后台任务。 除此之外,Jetpack还包括了诸如Paging、Data Binding、Preferences、Security等库,这些库都旨在简化开发过程并提高应用程序的性能和可靠性。

Jetpack四件套介绍

Lifecycle

Life生命,cycle周期,顾名思义:Lifecycle是生命周期的意思。它是Jetpack中的一个 生命周期感知型组件 ,可执行操作来感知响应另一个组件(如 Activity 和 Fragment)的生命周期状态的变化。

使用LifeCycle有什么好处?

LifeCycle可以帮助开发者创建可感知生命周期的组件。这样,组件便能够在其内部管理自己的生命周期,从而降低模块间的耦合度,并降低内存泄漏发生的可能性

举个例子,我们经常需要在页面的onCreate()方法中对组件进行初始化,在onPause()方法中停止组件,而在页面的onDestroy()方法中对组件进行资源回收工作。这样的工作非常烦琐,会让页面与组件之间的耦合度变高。但这些工作又不得不做,因为这可能会引发内存泄漏。

正确使用LifeCycle

使用思路:

  1. 构建一个 Lifecycle 对象(通过一个实现了 LifecycleOwner 接口的对象的 getLifecycle()方法返回),这个对象就是一个被观察者,具有生命周期感知能力
  2. 构建一个 LifecycleObserver 对象,它对指定的 Lifecycle 对象进行监听
  3. 通过将 Lifecycle 对象的 addObserver(…) 方法,将 Lifecycle 对象和 LifecycleObserver 对象进行绑定

①导入依赖(这里直接去官网看最新的版本导入就可以了) 这里给出一些,按需选择,并不是全部都需要的

    dependencies {
        def lifecycle_version = "2.2.0"
        def arch_version = "2.1.0"
    
        // ViewModel
        implementation "androidx.lifecycle:lifecycle-viewmodel:$lifecycle_version"
        // LiveData
        implementation "androidx.lifecycle:lifecycle-livedata:$lifecycle_version"
        // 只有Lifecycles (不带 ViewModel or LiveData)
        implementation "androidx.lifecycle:lifecycle-runtime:$lifecycle_version"
    
        // Saved state module for ViewModel
        implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:$lifecycle_version"
    
        // lifecycle注解处理器
        annotationProcessor "androidx.lifecycle:lifecycle-compiler:$lifecycle_version"
        // 替换 - 如果使用Java8,就用这个替换上面的lifecycle-compiler
        implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"
    
        //以下按需引入
        // 可选 - 帮助实现Service的LifecycleOwner
        implementation "androidx.lifecycle:lifecycle-service:$lifecycle_version"
        // 可选 - ProcessLifecycleOwner给整个 app进程 提供一个lifecycle
        implementation "androidx.lifecycle:lifecycle-process:$lifecycle_version"
        // 可选 - ReactiveStreams support for LiveData
        implementation "androidx.lifecycle:lifecycle-reactivestreams:$lifecycle_version"
        // 可选 - Test helpers for LiveData
        testImplementation "androidx.arch.core:core-testing:$arch_version"
    }
​

②创建一个类实现LifecycleOwner:

public class MyLocationListener  implements LifecycleObserver {
    public MyLocationListener(Activity context, OnLocationChangeListener listener) {
​
        //初始化操作
        iniLocationManager();
    }
    
    private void iniLocationManager() {
    }
    
    //当Activity执行onResume()方法时,该方法会被自动调用
    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    private  void startGetLocation(){
        Log.e("true","onResume"+"被调用了");
    }
    
    //当Activity执行onPause()方法时,该方法会被自动调用
    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    private  void stopGetLocation(){
        Log.e("true","onPause"+"被调用了");
    }
    
    //当Activity执行onDestroy()方法时,该方法会被自动调用
    @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    private  void delGetLocation(){
        Log.e("true","onDestroy"+"被调用了");
    }
    //当地理位置发送改变时,通过该接口通知调用者
    public  interface  OnLocationChangeListener{
        void  onChanged(double latitude,double longitude);
    }
}
​

按照需求通过注解的方式来实现方法的调用。

③在需要被观察的activity或者fragment中添加Lifecycle对象,并且添加observer。 主MainActivity 在MainActivity中,只需要引用MyLocationListener即可,不用再关心Activity生命周期变化对该组件所带来的影响。生命周期的管理完全交给MyLocationListener内部自行处理。在Activity中要做的只是通过getLifecycle().addObserver()方法,将观察者与被观察者绑定起来,代码如下:

public class MainActivity extends AppCompatActivity {
​
    private  MyLocationListener myLocationListener;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    
        myLocationListener = new MyLocationListener(this, new MyLocationListener.OnLocationChangeListener() {
            @Override
            public void onChanged(double latitude, double longitude) {
                //展示收到的位置信息
            }
        });
    
        //将观察者与被观察者绑定
        getLifecycle().addObserver(myLocationListener);
    }
}

ViewModel

ViewModel是Jetpack AAC的重要组件,同时也有一个同名抽象类。 ViewModel,意为 视图模型,即为界面准备数据的模型。简单理解就是,ViewModel为UI层提供数据。

ViewModel使用:

①思路:

  • 导入依赖
  • 继承ViewModel自定义MyViewModel
  • 在MyViewModel中编写获取UI数据的逻辑
  • 使用LiveData将获取到的UI数据抛出
  • 在Activity/Fragment中使用ViewModelProvider获取MyViewModel实例
  • 观察MyViewModel中的LiveData数据,进行对应的UI更新。

举个例子,如果您需要在Activity中显示用户信息,那么需要将获取用户信息的操作分放到ViewModel中,代码如下:

public class UserViewModel extends ViewModel {
​
    private MutableLiveData<String> userLiveData ;
    private MutableLiveData<Boolean> loadingLiveData;
    
    public UserViewModel() {
        userLiveData = new MutableLiveData<>();
        loadingLiveData = new MutableLiveData<>();
    }
    
    //获取用户信息,假装网络请求 2s后 返回用户信息
    public void getUserInfo() {
        
        loadingLiveData.setValue(true);
    
        new AsyncTask<Void, Void, String>() {
            @Override
            protected void onPostExecute(String s) {
                loadingLiveData.setValue(false);
                userLiveData.setValue(s);//抛出用户信息
            }
            @Override
            protected String doInBackground(Void... voids) {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                String userName = "AAAAAABBBBBBBBBBBBCCCCCCCCC";
                return userName;
            }
        }.execute();
    }
    
    public LiveData<String> getUserLiveData() {
        return userLiveData;
    }
    public LiveData<Boolean> getLoadingLiveData() {
        return loadingLiveData;
    }
}
​

UserViewModel继承ViewModel,然后逻辑很简单:假装网络请求 2s后 返回用户信息,其中userLiveData用于抛出用户信息,loadingLiveData用于控制进度条显示。

再看UI层:

public class UserActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ...
        Log.i(TAG, "onCreate: ");
​
        TextView tvUserName = findViewById(R.id.textView);
        ProgressBar pbLoading = findViewById(R.id.pb_loading);
    //获取ViewModel实例
        ViewModelProvider viewModelProvider = new ViewModelProvider(this);
        UserViewModel userViewModel = viewModelProvider.get(UserViewModel.class);
        //观察 用户信息
        userViewModel.getUserLiveData().observe(this, new Observer<String>() {
            @Override
            public void onChanged(String s) {
                // update ui.
                tvUserName.setText(s);
            }
        });
    
        userViewModel.getLoadingLiveData().observe(this, new Observer<Boolean>() {
            @Override
            public void onChanged(Boolean aBoolean) {
                pbLoading.setVisibility(aBoolean?View.VISIBLE:View.GONE);
            }
        });
        //点击按钮获取用户信息
        findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                userViewModel.getUserInfo();
            }
        });
    }
    
    @Override
    protected void onStop() {
        super.onStop();
        Log.i(TAG, "onStop: ");
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.i(TAG, "onDestroy: ");
    }
}
​
​

页面有个按钮用于点击获取用户信息,有个TextView展示用户信息。 在onCreate()中先 创建ViewModelProvider实例,传入的参数是ViewModelStoreOwner,Activity和Fragment都是其实现。然后通过ViewModelProvider的get方法 获取ViewModel实例,然后就是 观察ViewModel中的LiveData。

②总结:

  • ViewModel的使用很简单,作用和原来的Presenter一致。只是要结合LiveData,UI层观察即可。
  • ViewModel的创建必须通过ViewModelProvider。
  • 注意到ViewModel中没有持有任何UI相关的引用。
  • 旋转手机重建Activity后,数据确实恢复了。

LiveData

LiveData是Jetpack提供的一种响应式编程组件,它可以包含任何类型的数据,并在数据发生变化的时候通知给观察者,适合与ViewModel结合在一起使用,就可以让ViewModel将数据的变化主动通知给Activity。

LiveData的使用

在开发中我们比较常用的就是 MutableLiveData ,他给我们暴露好了修改对应值的方法,LiveData的作者认为我们有这个功能就够了。

public class MutableLiveData<T> extends LiveData<T> {
    public MutableLiveData(T value) {
        super(value);
    }
    public MutableLiveData() {
        super();
    }
    // 在子线程 中 更改数据 就需要用到postValue方法
    @Override
    public void postValue(T value) {
        super.postValue(value);
    }
    @Override
    public void setValue(T value) {
        super.setValue(value);
    }
}

一个简单的 使用MutableLiveData的实例

class LiveDataTestActivity : AppCompatActivity() {
    val TAG = "LiveDataTestActivity"
    var index = 1
    val liveData:MutableLiveData<String> by lazy {
        MutableLiveData<String>().also {
            it.value = "周杰伦"
        }
    }
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        var binding = ActivityLiveDataTestBinding.inflate(layoutInflater)
        setContentView(binding.root)
​
        liveData.observe(this){
            Log.e(TAG,it)
        }
    }
     
    fun changeLivaData(view: View) {
        liveData.value = "周杰伦${index++}"
    }
}

首先调用liveData.observe注册一个观察者,观察者注册商后,用户点击xml上的按钮,liveData 会调用setValue方法(因为是kotlin省略了set) 然后在observe的回调中会打印对应的值,那么我们看看这个最终是怎么通知观察者 回调的呢。liveData调用了setValue方法,最后是调到Super了,也就是LiveData中的setValue方法。

DataBinding

DataBinding 是 Google 在 Jetpack 中推出的一款数据绑定的支持库,他的目的是一个帮助我们实现数据和UI绑定,并可以进行双向绑定。

优势

  1. 项目更加简介,代码可读性更高。
  2. 不再需要findViewById()。
  3. 布局文件可以包含简单的业务逻辑。

DataBinding使用

实例化布局文件了,我们删除掉传统的setContentView(),通过 DataBindingUtil.setContentView()来实例化文件。实例化返回的布局文件对象,名字和布局文字名字一致,遵循大驼峰命名规则,后面加上Binding。然后通过binding对象得到控件,控件命名遵循小驼峰规则。

ActivityMainBinding binding=DataBindingUtil.setContentView(this,R.layout.activity_main);
binding.textHome.setText("hello databinding!");

数据绑定

如何将数据传递到布局文件中呢?首先,在布局文件中定义布局变量,指定对象的名字和类型,当然数据的操作在标签里。data标签里用于放在布局文件中各个UI控件所需要的数据,这些数据类型可以是自定义类型,也可以是基本类型。

<data>
​
        <variable
            name="book"
            type="com.yhj.jetpackstudy.Book" />
        <variable
            name="number"
            type="Integer" />
​
</data>
public class Book {
​
    private int id;
    private String title;
    private String author;
​
}

有时我们需要在布局文件中引入一些Java工具类或静态类,处理一些简单的逻辑在布局中,我们可以使用标签导入。使用alias,当类名有冲突时,其中一个类可使用别名重命名。默认导入java.lang.*

<data>    
      <import type="com.yhj.jetpackstudy.ui.home.Constants"alias="reName"/>
</data>

布局中的数据绑定使用“@{}”语法写入属性中,通过布局表达式的形式设置TextView的text。

<TextView android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:text="@{book.title}" />
          
<TextView android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:text="@{Constants.APP_ID}" />

DataBinding为了方便使用,对布局变量提供了Setter类,因此,在Activity中,通过setBook(),将Book对象传递给布局变量。

Book book = new Book(0, "android", "yhj");
//BR类似于Android中的R类,由DataBinding自动生成,用于存放所有布局变量的id。
//DataBinding为了方便使用提供了Setter类,直接使用setXxx()
//binding.setVariable(BR.book,book);
binding.setBook(book);

绑定后,就不需要再Activity中设置内容了,实现了布局与页面的解耦。 DataBinding具有Null校验,如果绑定值为null,则分配默认值null,如果类型为int,默认值为0。

本文主要讲了在Android jetpack中的重要组件,更多的jetpack技术请参考《jetpack技术与Android架构精讲》点击可查看详细类目。

最后

在我们工作中,可维护、可扩展、可测试和可重用的业务架构对于提高应用程序的质量和效率意义非凡,而JetPack是帮助开发者快速地组织和管理应用程序的代码的工具包。希望这篇文章能够让大家了解到jetpack的重要性

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

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

相关文章

【LeetCode75】第四十四题 省份数量

目录 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 代码&#xff1a; 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 给我们一个二维数组&#xff0c;表示城市之间的连通情况&#xff0c;连在一起的城市为一个省份&#xff0c;问我们一共有多少个省份。 这…

Python实现多子图绘制系统

文章目录 修改DrawTypeDrawType的调用逻辑绘图逻辑源代码 Python绘图系统&#xff1a; &#x1f4c8;从0开始的3D绘图系统&#x1f4c9;一个3D坐标系&#xff0c;多个函数图表类型和风格&#xff1a;&#x1f4c9;极坐标绘图&#x1f4ca;散点图和条形图&#x1f4ca;混合类型…

推进数据要素化,数据云为何是“加速器”?

数据要素化&#xff0c;一个世界性难题。 相比于传统生产要素&#xff0c;数据要素具有获得非竞争性、使用非排他性等独有特征&#xff0c;在流通、产权、安全和使用等方面需要法规制度与基础设施的双重保障。 我国无疑是最早探索数据要素化的国家之一。从早期成立的各种大数…

win10下的CLion控制台中文乱码终极解决方案

win10下的CLion控制台中文乱码终极解决方案 如果你也是&#xff0c;用Clion时候&#xff0c;CPP文件中有中文&#xff0c;然后终端运行会有乱码&#xff0c;改了设置发现&#xff0c;项目中的中文乱码了&#xff0c;但是终端又不乱码了&#xff0c;&#xff0c;&#xff0c;&am…

使用java代码给Excel加水印,保真,新鲜出炉

首先&#xff0c;往表格里贴透明图片&#xff0c;这个很智障&#xff0c;会严重干扰正常阅读和操作 设置文件背景图&#xff1b; 其次&#xff0c;其实就是给excel加一个背景图&#xff0c;但是问题就麻烦在java中基本没有这么干过的&#xff0c;可用方案很少&#xff0c;有spi…

基于java+springboot+vue的置换网站-lw

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql Webstorm Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; ssm mybatis Maven mysql5.7或8.0等等组成&#xff0c;B/S模式 Maven管理等等。 环境需要 1.…

Java-HashMap中put()方法是如何实现的,内含详细流程图

文章目录 Java中的HashMap什么是HashMap&#xff1f;对比其他Map中put()方法HashMap中put()方法使用示例 HashMap中put()源码解析手绘流程图实现原理源码探究&#xff08;JDK 1.8&#xff09; 设计put()的意义总结 Java中的HashMap 什么是HashMap&#xff1f; HashMap是Java中…

ARP欺骗

ARP欺骗定义 ARP欺骗&#xff08;英语&#xff1a;ARP spoofing&#xff09;&#xff0c;又称ARP毒化&#xff08;ARP poisoning&#xff0c;网络上多译为ARP病毒&#xff09;或ARP攻击&#xff0c;是针对以太网地址解析协议&#xff08;ARP&#xff09;的一种攻击技术&#x…

Qt —UDP通信QUdpSocket 简介 +案例

1. UDP通信概述 UDP是无连接、不可靠、面向数据报&#xff08;datagram&#xff09;的协议&#xff0c;可以应用于对可靠性要求不高的场合。与TCP通信不同&#xff0c;UDP通信无需预先建立持久的socket连接&#xff0c;UDP每次发送数据报都需要指定目标地址和端口。 QUdpSocket…

SpringCloudAlibaba Gateway(一)简单集成

SpringCloudAlibaba Gateway(一)简单集成 随着服务模块的增加&#xff0c;一定会产生多个接口地址&#xff0c;那么客户端调用多个接口只能使用多个地址&#xff0c;维护多个地址是很不方便的&#xff0c;这个时候就需要统一服务地址。同时也可以进行统一认证鉴权的需求。那么服…

vcs仿真教程(查看断言)

VCS是在linux下面用来进行仿真看波形的工具&#xff0c;类似于windows下面的modelsim以及questasim等工具&#xff0c;以及quartus、vivado仿真的操作。 1.vcs的基本指令 vcs的常见指令后缀 sim常见指令 2.使用vcs的实例 &#xff08;1&#xff09;新建文件夹&#xff1a; …

linux 开设端口

1&#xff0c;查看端口情况 firewall-cmd --list-all2&#xff0c;开设端口 firewall-cmd --zonepublic --add-port6379/tcp --permanent–zonepublic 表示作用域为公共的 –add-port6379/tcp 添加 tcp 协议的端口端口号为 6379 –permanent 永久生效&#xff0c;如果没有此参…

每日一题 1372二叉树中的最长交错路径

题目 给你一棵以 root 为根的二叉树&#xff0c;二叉树中的交错路径定义如下&#xff1a; 选择二叉树中 任意 节点和一个方向&#xff08;左或者右&#xff09;。如果前进方向为右&#xff0c;那么移动到当前节点的的右子节点&#xff0c;否则移动到它的左子节点。改变前进方…

枚举的简单介绍

目录 概念&#xff1a; 枚举的声明&#xff1a; 枚举的使用&#xff1a; 枚举的取值&#xff1a; 枚举的优点&#xff1a; #define的功能&#xff1a; 而与#define对比&#xff0c;枚举的优点有&#xff1a; 概念&#xff1a; 枚举顾名思义就是⼀⼀列举。 把可能的取值…

WireShark流量抓包详解

目录 Wireshark软件安装Wireshark 开始抓包示例Wireshakr抓包界面介绍WireShark 主要界面 wireshark过滤器表达式的规则 Wireshark软件安装 软件下载路径&#xff1a;wireshark官网。按照系统版本选择下载&#xff0c;下载完成后&#xff0c;按照软件提示一路Next安装。 Wire…

CUDA小白 - NPP(2) - Arithmetic and Logical Operations(2)

cuda小白 原始API链接 NPP GPU架构近些年也有不少的变化&#xff0c;具体的可以参考别的博主的介绍&#xff0c;都比较详细。还有一些cuda中的专有名词的含义&#xff0c;可以参考《详解CUDA的Context、Stream、Warp、SM、SP、Kernel、Block、Grid》 常见的NppStatus&#xf…

【100天精通python】Day50:python web编程_Django框架从安装到使用

目录 1 安装Django Web框架 2 创建一个Django 项目 3 数据模型 3.1 在应用程序的 models.py 文件中定义数据模 3.2 创建模型的迁移文件并应用 3.2.1 查询模型对象&#xff1a; 3.2.2 创建新模型对象&#xff1a; 3.2.3 更新模型对象&#xff1a; 3.2.4 删除模型对象&a…

JUC并发编程---Lock锁

文章目录 什么是Locksynchronized加锁和Lock加锁代码示例synchronized使用Lock加锁 公平锁和非公平锁公平锁&#xff1a;非公平锁&#xff1a;Lock和Synchronized的区别 synchronized 版的生产者和消费者Lock 版的生产者和消费者生产者和消费者出现的问题Condition精准通知和唤…

机器视觉工程师,人学习最大的能力是理解与善于运用,而不是记住能力

谁记得以前记住的元素周期表&#xff0c;谁能记得住乘法口诀。 如果我们去看一眼&#xff0c;就会迅速记起来。再加上我们小学机械般的练习题。再到我们在现实生活中经常用到。 其实我们机器视觉工程师&#xff0c;一定要去看&#xff0c;还要去练习​。实操软件&#xff0c;多…

深度学习-4-二维目标检测-YOLOv5源码测试与训练

本文采用的YOLOv5源码是ultralytics发行版3.1 YOLOv5源码测试与训练 1.Anaconda环境配置 1.1安装Anaconda Anaconda 是一个用于科学计算的 Python 发行版&#xff0c;支持 Linux, Mac, Windows, 包含了众多流行的科学计算、数据分析的 Python 包。 官方网址下载安装包&…