jetpack的简单使用

Jetpack

ViewModel:

什么是ViewModel?

ViewModel 是 Android 架构组件的一部分,用于帮助开发者管理 UI 数据的持久性和生命周期感知。ViewModel 的主要目的是将 UI 数据与界面控制逻辑分离,以便更好地管理数据的生命周期,确保数据在配置更改(如屏幕旋转)或 Activity 生命周期变化时不会丢失。

以下是 ViewModel 的主要特点和用途:

  1. 生命周期感知: ViewModel 是生命周期感知的,它会自动与关联的 Activity 或 Fragment 生命周期同步。这意味着它将在 Activity 启动、暂停、恢复和销毁时进行适当的操作,以确保数据的一致性。
  2. 数据持久性: ViewModel 实例将在配置更改时保持不变,因此您可以将数据存储在 ViewModel 中,而不必担心数据丢失。这对于保存用户输入、加载网络数据或执行其他需要在 Activity 重新创建时保持不变的操作非常有用。
  3. 分离 UI 和数据: ViewModel 的设计鼓励将数据存储和业务逻辑与界面控制逻辑分离开来。这提高了代码的可维护性,使应用程序更易于测试。
  4. 与界面无关: ViewModel 不依赖于特定的界面元素或视图,因此它可以在不同的 UI 层中重复使用,如手机和平板电脑的不同布局。
  5. 支持共享数据: 多个组件(如多个 Fragment 或 Activity)可以共享同一个 ViewModel 实例,以便它们可以访问和更新相同的数据。这对于在应用程序中共享状态信息非常有用。
  6. 避免内存泄漏: 使用 ViewModel 时,您可以更容易地避免由于持有对 Activity 或 Fragment 的引用而导致的内存泄漏问题。

在 Android 应用程序中,通常会创建自定义的 ViewModel 类,该类扩展自 androidx.lifecycle.ViewModel 并包含需要在界面之间共享和保持的数据。然后,通过使用 ViewModelProvider 获取 ViewModel 实例,并在 UI 控制器(如 Activity 或 Fragment)中使用它。

ViewModel 是一项强大的工具,有助于改进 Android 应用程序的可维护性、性能和用户体验。它在处理 UI 数据时提供了一种更有效的方式。

为什么需要ViewModel?

  1. 生命周期管理: Android 应用中的 Activity 和 Fragment 在配置更改(例如屏幕旋转)或其他生命周期事件发生时会销毁和重新创建。这可能导致数据丢失或重新加载,对用户体验造成困扰。ViewModel 可以在这些生命周期事件中保持数据的一致性,确保数据不会丢失。
  2. 分离关注点: 使用 ViewModel 可以帮助开发人员将界面控制逻辑与数据管理和业务逻辑分离开来。这样的分离使代码更易于理解、测试和维护,同时遵循了单一责任原则。
  3. 支持共享数据: 多个 UI 组件(如不同的 Fragment 或 Activity)可以共享同一个 ViewModel 实例,以便它们可以访问和更新相同的数据。这在应用程序中的不同界面之间共享状态信息时非常有用,而无需复杂的数据传递和同步。
  4. 避免内存泄漏: 在传统的 Android 开发中,持有对 Activity 或 Fragment 的引用可能导致内存泄漏。ViewModel 避免了这个问题,因为它不持有对界面控制器的强引用,而是与其生命周期进行适当的管理。
  5. 提高性能: 由于 ViewModel 在内存中保持其数据,不需要在每次配置更改时重新加载数据,可以提高性能和响应时间。这对于加载网络数据或执行复杂计算的操作尤其重要。
  6. UI 状态保持: ViewModel 可以用于保持 UI 状态,如用户输入或滚动位置。这使用户能够在屏幕旋转或其他配置更改后,恢复到他们之前的操作状态。

ViewModel如何使用?

ViewModel最常用的场景:

viewModel的生命周期:

jetpack的简单使用_第1张图片

一:活动内使用的变量放在ViewModel里面:

①创建一个java类,继承ViewModel类:

在里面定义变量,这些变量都是在主活动中需要使用的变量。

②在对应活动里面使用变量:

只能使用ViewModelProvider来获取ViewModel,因为直接在Create方法里面创建的话,会导致在屏幕旋转的时候造成数据丢失。

package com.example.demo.jetpack;

import androidx.lifecycle.ViewModel;

public class MainViewModel extends ViewModel {
    int count;

    public MainViewModel() {

    }

    public MainViewModel(int count) {
        this.count = count;
    }
}

package com.example.demo.jetpack;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.ViewModel;
import androidx.lifecycle.ViewModelProvider;

import android.content.Context;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import com.example.demo.R;

public class ViewModelActivity extends AppCompatActivity {
    private MainViewModel viewModel;
    private TextView tv;

    private Button btn;
    private Button btn_clear;
    private SharedPreferences.Editor edit;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_view_model);
        SharedPreferences config = getSharedPreferences("config", Context.MODE_PRIVATE);
        edit = config.edit();
        int count = config.getInt("count", 0);
        try {
            tv = findViewById(R.id.tv_ViewModel);
            btn = findViewById(R.id.btn_ViewModel);
            viewModel = new ViewModelProvider(this).get(MainViewModel.class);
            btn_clear = findViewById(R.id.btn_clear_ViewModel);
            btn_clear.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    clearCount();
                }
            });
            refresh();
            btn.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    viewModel.count++;
                    refresh();
                    edit.putInt("count", count);
                    edit.commit();
                }
            });
        } catch (Exception e) {
            Log.d("ViewModelActivity_test", e.toString());
        }
    }

    private void clearCount() {
        viewModel.count = 0;
        tv.setText("0");
        edit.putInt("count", 0);
        edit.commit();
    }

    private void refresh() {
        tv.setText(String.valueOf(viewModel.count));
    }
}

注意:

  1. VIewModel的类必须有一个无参构造器,不然无法创建实例
  2. 每一个类对应一个ViewModel
  3. 在其它活动里面也可以引用本活动的ViewModel

二:向ViewModel传递参数:

直接赋值

向ViewModel传递参数是通过ViewModelProvider.Factory类实现

重写Create方法之后,

由于更新之后,只能向Create,会有一个CreationExtras变量,只能向ViewModel传递这个参数,还没想到,怎么ViewModel传递参数。

jetpack的简单使用_第2张图片

在继承ViewModelProvider.Factory的类中,实现Create方法:

创建一个构造器,将你需要的东西传进去,将参数传递给ViewModel的构造函数,这就是上面ViewModel里面的构造函数。 直接将数值传递给ViewModel,注意ViewmodelProvider的使用。

package com.example.demo.jetpack;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.ViewModel;
import androidx.lifecycle.ViewModelProvider;
import androidx.lifecycle.viewmodel.CreationExtras;

public class MainVIewModelFactory implements ViewModelProvider.Factory {
    private final int initialValue; // 要传递的参数

    public MainVIewModelFactory(int initialValue) {
        this.initialValue = initialValue;
    }

    @NonNull
    @Override
    public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
        if (modelClass == MainViewModel.class) {
            return (T) new MainViewModel(initialValue); // 将参数传递给 ViewModel 的构造函数
        }
        throw new IllegalArgumentException("Unknown ViewModel class");
    }
}
package com.example.demo.jetpack;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.lifecycle.ViewModel;
import androidx.lifecycle.ViewModelProvider;

import android.content.Context;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import com.example.demo.R;

public class ViewModelActivity extends AppCompatActivity {
    private MainViewModel viewModel;
    private TextView tv;

    private Button btn;
    private Button btn_clear;
    private SharedPreferences.Editor edit;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_view_model);
        SharedPreferences config = getSharedPreferences("config", Context.MODE_PRIVATE);
        edit = config.edit();
        int count = config.getInt("count", 0);
        try {
            tv = findViewById(R.id.tv_ViewModel);
            btn = findViewById(R.id.btn_ViewModel);
            viewModel = new ViewModelProvider(this, new MainVIewModelFactory(count)).get(MainViewModel.class);
            btn_clear = findViewById(R.id.btn_clear_ViewModel);
            btn_clear.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    clearCount();
                }
            });
            refresh();
            btn.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    viewModel.count++;
                    refresh();
                    edit.putInt("count", viewModel.count);
                    edit.commit();
                }
            });
        } catch (Exception e) {
            Log.d("ViewModelActivity_test", e.toString());
        }
    }

    private void clearCount() {
        viewModel.count = 0;
        tv.setText("0");
        edit.putInt("count", 0);
        edit.commit();
    }

    private void refresh() {
        tv.setText(String.valueOf(viewModel.count));
    }
}

Lifecycles:

Lifecycles 的中文意思就是 生命周期,所以我们可以知道它和生命周期是息息相关的!

什么是Lifecycles?

“LifeCycles”(生命周期)是 Android 架构组件的一部分,旨在帮助开发者更好地管理 Android 应用程序中的组件(如 Activity 和 Fragment)的生命周期。生命周期库为开发者提供了在 Android 生命周期事件发生时执行操作的机制,以便更容易处理与生命周期相关的任务,例如初始化、清理、数据加载和资源释放。

Android 应用程序组件的生命周期是由系统管理的,包括以下关键事件:

  • onCreate(): 组件创建时调用。
  • onStart(): 组件变为可见但尚未与用户交互时调用。
  • onResume(): 组件变为可见并与用户交互时调用。
  • onPause(): 组件不再与用户交互时调用。
  • onStop(): 组件不再可见时调用。
  • onDestroy(): 组件销毁时调用。

生命周期库为每个生命周期事件提供了对应的类,开发者可以使用这些类来执行特定的操作。以下是生命周期库中的主要类:

  1. LifecycleOwner: 它是 ActivityFragment 的常见接口,表示拥有生命周期的对象。
  2. Lifecycle: 代表应用程序组件的生命周期状态,包括 CREATEDSTARTEDRESUMEDDESTROYED 等。
  3. LifecycleObserver: 定义了在生命周期事件发生时要执行的回调方法。
  4. LifecycleRegistry: 实现了 Lifecycle 接口,用于管理组件的生命周期状态。

通过将 LifecycleOwnerLifecycleObserver 结合使用,开发者可以轻松地在组件的生命周期事件中执行操作,从而更好地管理资源和确保应用程序的正确运行。

生命周期库是 Android 架构组件的一部分,可帮助开发者编写更健壮、可维护和易于测试的 Android 应用程序。它特别适用于处理内存管理、数据加载和其他与生命周期相关的任务。

为什么需要Lifecycles?

“LifeCycles”(生命周期)是 Android 应用程序中非常重要的一部分,它解决了以下几个关键问题,使应用程序更稳定、可维护且用户体验更好:

  1. 生命周期管理: Android 应用的组件(如 Activity 和 Fragment)具有多种生命周期状态,而这些状态的管理对应用程序的正确运行至关重要。生命周期库允许您更轻松地管理组件的生命周期,确保它们在正确的时间执行所需的操作。这有助于避免内存泄漏、资源泄漏以及确保资源及时释放。
  2. 配置更改: Android 设备经常经历配置更改,如屏幕旋转。在这些情况下,活动和碎片会被销毁并重新创建。生命周期库帮助您在配置更改后正确地保存和还原数据,以提供平滑的用户体验,无需用户重新输入或重新加载数据。
  3. 资源管理: Android 应用程序必须管理有限的资源,如网络连接、数据库连接、传感器和其他硬件资源。生命周期库使您能够在不需要资源时及时释放它们,以避免资源泄漏和性能问题。
  4. 易于测试: 使用生命周期库,您可以更轻松地编写单元测试和集成测试,以验证应用程序在不同生命周期事件下的行为。这有助于提高代码质量并减少错误。
  5. 分离关注点: 将生命周期感知的逻辑与界面控制逻辑分开是一种良好的编程实践。生命周期库鼓励将生命周期操作封装在 LifecycleObserver 类中,使代码更易于理解和维护。
  6. 性能优化: 通过在生命周期事件中执行适当的操作,您可以提高应用程序的性能,例如延迟加载数据直到界面可见,以减少不必要的开销。

总之,生命周期库是 Android 架构组件的一部分,它旨在简化和增强应用程序开发的生命周期管理,有助于构建更稳定、高性能和易于维护的 Android 应用程序。这使得开发者能够更专注于应用程序的功能和用户体验,而无需过多关注底层生命周期事件的处理。

如何使用Lifecycles?

lifecycle的本质就是在任意一个类里面用监视器,监视活动的状态

很多时候,发现碎片或者活动处于某一个状态的时候,就不需要执行一些操作了:使用场景也比较多,因此,有了Lifecycle

一:创建一个MyObserver类:
主要使用注解,区分检测到什么状态,执行什么方法:

使用@OnLifecycleEvent()注解:里面填的就是活动的某一个状态:

jetpack的简单使用_第3张图片

二:在被监视的活动里面使用getLifecycle()方法

得到实例之后,直接调用addObserver方法():

jetpack的简单使用_第4张图片

现在就可以自动监视活动或者碎片的生命周期了。

LiveData:

什么是LiveData?

LiveData 是 Android 架构组件中的一个类,用于在 Android 应用中以一种生命周期感知的方式管理数据。它具有以下关键特点:

  1. 生命周期感知: LiveData 是生命周期感知的,它可以感知与之关联的组件(如 Activity 或 Fragment)的生命周期状态。这使得它能够自动管理观察者的订阅和取消订阅,以避免潜在的内存泄漏问题。
  2. 实时数据更新: LiveData 用于存储和公开实时更新的数据。它可以通知观察者(通常是 UI 组件)数据发生变化,从而实时更新界面。这对于构建实时性强的应用程序非常有用,如实时聊天、实时通知等。
  3. 数据共享: 多个组件(如不同的 Fragment 或 Activity)可以观察相同的 LiveData 实例,从而共享相同的数据。这有助于确保这些组件都看到相同的数据,而无需复杂的数据同步。
  4. 生命周期对齐的数据更新: LiveData 可以确保数据更新仅在观察者处于活动状态时通知,而在非活动状态时会暂停通知。这有助于避免在不必要的时候触发 UI 更新,提高性能和减少资源消耗。
  5. 简化异步操作: LiveData 可以与其他异步操作(如网络请求或数据库查询)结合使用,以简化异步数据加载和处理。它允许您将异步操作的结果直接提供给观察者,而无需手动处理线程切换。
  6. 支持数据变换: LiveData 支持数据变换,使您可以对数据进行转换和处理,然后将结果提供给观察者。这使得数据转换和映射变得更容易。
  7. 避免内存泄漏: 由于 LiveData 是生命周期感知的,它可以帮助避免由于观察者未正确取消订阅而导致的内存泄漏问题。

为什么需要LiveData?

LiveData 是 Android 架构组件的一部分,被广泛用于 Android 应用程序开发中,它的存在和使用有多个重要原因:

  1. 生命周期感知: LiveData 是生命周期感知的数据持有类,它可以感知与之关联的组件(如 Activity 和 Fragment)的生命周期状态。这意味着 LiveData 可以确保数据仅在组件处于活动状态时进行更新,从而避免了在组件处于非活动状态时产生的 UI 更新错误和潜在的内存泄漏。
  2. 数据通知: LiveData 具备通知机制,当数据发生变化时,它会主动通知已注册的观察者(通常是界面组件),以便实时更新界面。这消除了手动管理数据刷新和界面更新的繁琐工作。
  3. 一致性: LiveData 提供了一种一致的、可预测的数据更新机制。它确保了数据更改的顺序和一致性,避免了多线程操作数据时可能出现的竞态条件和不一致性。
  4. 数据持久性: LiveData 可以与 ViewModel 一起使用,以支持屏幕旋转等配置更改后的数据持久性。这使得在配置更改后能够保持数据的一致性和不丢失数据。
  5. 减少内存泄漏: 使用 LiveData 可以减少因未正确处理生命周期而导致的内存泄漏。LiveData 可以自动取消不再需要的观察者,从而避免了因忘记取消观察而导致的内存泄漏问题。
  6. 简化异步编程: LiveData 可以与协程一起使用,使异步操作更容易管理和协调。它提供了 TransformationsMediatorLiveData 等功能,用于将数据转换和合并,以便更容易实现复杂的数据操作。
  7. 生命周期的作用域: LiveData 可以感知生命周期的作用域,使数据的更新和观察范围受到生命周期的限制。这对于在不同页面之间共享数据和确保数据不被错误地更新至关重要。

LiveData 的设计目标是提供一种简化 Android 应用程序中数据管理界面更新的方法,同时解决了与生命周期和内存泄漏相关的一些常见问题。它是 Android 应用架构中的一个重要组件,有助于创建更稳定、易维护和高性能的应用程序。

如何使用LiveData?

1.包装数据:

包装数据可以使用liveData的子类,使用MutableData类,可以放置任何数据。

用法如下:

  • 创建一个MutableLiveData,将自己需要的数据类型传输进去

完了,直接最简单的用法:

package com.example.demo.jetpack;

import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.ViewModel;

public class MainViewModel extends ViewModel {
    int count;

    private MutableLiveData<Integer> data;//将真实的LiveData隐藏起来

    {
        data = new MutableLiveData<>();
        data.setValue(count);
    }

    public LiveData<Integer> get() {
        return data;
    }

    public MainViewModel() {

    }

    public void plusOne() {
        int temp = data.getValue();
        if (null != data.getValue()) {
            data.setValue(temp + 1);
        }
    }

    public void clearNow() {
        //自动装箱
        data.setValue(0);
    }

    public MainViewModel(int count) {
        this.count = count;
    }
}

在主活动,调用的时候就使用这个livedata类的实例 就好了,通过调用:

  • getValue() 返回数据
  • setValue() 设置数据
  • postValue() 在非主线程的地方使用,得到数据

等方法,实现对数据的操作,

注意:

最好将本身含有数据的LiveData设置为private,这样就不会向非ViewModel类的类暴漏的数据更少了,

就像上面的例子:

返回一个Livedata类,就只能得到数据和行使观察的方法。

jetpack的简单使用_第5张图片

这样只能调用父类的方法,但是执行的还是子类的getValue方法

map()方法的使用

将一个liveData对象映射为另一个livedata对象。

jetpack的简单使用_第6张图片

这里将一个Integer的liveData映射成String的liveData。

switchMap()方法的使用:

这个方法使用的场景比较多:目前我们对viewModel的学习都是livedata在ViewModel中创建的前提下,但是很多情况下liveData是一个方法返回的数据,经过某个方法的操作:比如向服务器申请数据之后,返回了数据,或者在数据库中查找对应的对象,

假设在一个getUser方法定义在ViewModel的子类中,返回一个livedata类,我们要在活动中添加观察,可以这样写吗:

 viewModel.getUser().observe(this, new Observer<User>() {
                @Override
                public void onChanged(User user) {
                    //具体发现观察到数据变化之后的逻辑操作
                }
            });

记住,千万不能这么做,因为每次返回的数据都不一样,但是观察的却是最初的那一个livedata,从而根本无法观察livedata,这种情况下livedata是不可观察的。

switchMap就是应对这种情况的,如果ViewModel的某个livedata是由某个方法返回的,我们就可以用这个方法将这个livedata转化为另一个可观察的livedata对象。

jetpack的简单使用_第7张图片

使用switchMap方法,将现在的livedata转化为重新申请的livedata,对于活动来说,直接就可以使用livedata z

补充:

这种情况下是调用geUser方法的时候有参数,可以确定livedata发生变化,然后当没有任何参数的时候,我们怎么办呢?

  • 创建一个空的liveData
  • 我们可以直接调用返回的livedata的getValue方法,只要我们调用了setValue方法或者postValue方法就一定会发生数据的变化,触发转化函数

这样实现了对livedata数据的更新

总结:

  • map方法用于将一个livedata映射为另一个livedata
  • switchmap方法用于持续更新方法返回的livedata的实例,只需要在活动里面添加结果livedata的观察就好了

Room数据库 库:

什么是Room?

Room 是一个用于 Android 应用的持久性数据库库,它是 Google 推出的一部分 Android 架构组件。Room 的主要目标是简化数据库操作,提供更好的类型安全性,同时提供方便的本地数据库访问方式。Room 在 SQLite 数据库之上构建,因此它继承了 SQLite 的强大功能,同时提供了更高层次的抽象,以便于开发者使用。

Room 提供以下主要组件:

  1. Entity(实体):Entity 是一个与数据库表相对应的 Java/Kotlin 类,它定义了表的结构,每个实体类的字段对应表的列。通过在实体类上使用注解,您可以指定主键、索引和其他数据库相关属性。
  2. DAO(Data Access Object):DAO 是一个包含数据库操作方法的接口或抽象类,它定义了对数据库的操作,如插入、更新、删除和查询。通过在 DAO 方法上使用注解,您可以指定 SQL 查询或操作。
  3. Database(数据库):Database 是一个用于访问数据的抽象类,通常扩展自 RoomDatabase。它负责管理数据库的连接,提供 DAO 的实例,以及处理数据库版本迁移。

为什么需要Room?

  1. 类型安全的数据库操作: Room 在编译时进行类型检查,这意味着您可以在编译期间捕获到许多潜在的数据库操作错误,从而减少运行时错误。
  2. 简化数据库访问: Room 简化了数据库操作的编写,您只需定义实体类和 DAO 接口,而不必编写大量的 SQL 查询语句。这使数据库访问更加直观和容易理解。
  3. 方便的数据库版本管理: Room 提供了用于管理数据库版本迁移的工具,这允许您在应用升级时更改数据库架构,而不会导致数据丢失或应用崩溃。
  4. 集成 LiveData 和 RxJava: Room 可以与 Android 架构组件中的 LiveData 和 RxJava 集成,使数据持久性层与应用界面之间的数据流更加容易管理。
  5. 性能优化: Room 提供了一些性能优化功能,如查询编译、预编译语句和查询缓存,以提高数据库操作的效率。
  6. 良好的文档和社区支持: Room 是由 Google 推出的一部分 Android 架构组件,因此它拥有广泛的文档和社区支持。您可以轻松找到教程、示例代码和问题解答。
  7. 广泛采用: Room 已经广泛采用,成为了许多 Android 应用的首选数据库解决方案,这意味着您可以利用社区经验和工具来解决问题。
  8. 跨平台兼容性: Room 可以在不同的 Android 设备上运行,并且不依赖于特定的硬件或操作系统版本。

Room如何使用?

基本使用:

实现数据库基本操作:增删改查

  • 创建实体类
  • 创建Dao接口
  • 创建Database继承于RoomDatabase

一:创建实体类:

按照需求,创建要在数据库中存储的实体类

  • 在类名上面加上注释@Entity,框架才能认识这是你所要存储的实体类
  • 确定一个主键,在主键上面加上注释@PrimarKey,设置属性autoGenerate为true,主键会自己增长,如果想要设置元素的名字:使用注释:@ColumnInfo 设置属性name为你想要的值,
package com.example.demo.jetpack.room.database;

import androidx.room.ColumnInfo;
import androidx.room.Entity;
import androidx.room.PrimaryKey;

/**
 * @author winiymissl
 */
@Entity
public class User {
    @PrimaryKey(autoGenerate = true)
    long id;
  
    int age;
    String name;
    String sex;

    public User(String name, int age, String sex) {
        this.age = age;
        this.name = name;
        this.sex = sex;
    }

    @Override
    public String toString() {
        return "User{" + "id=" + id + ", age=" + age + ", name='" + name + '\'' + ", sex='" + sex + '\'' + '}';
    }

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }


    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

}

二:创建Dao接口

这是Room框架最重要的地方,在这里里面实现增删改查的操作

在增加前面加上:@Insert

参数处放着实体类就好:这里使用User作为一个实体类

 	/**
     * Desc:插入用户信息
     */
    @Insert
    void inertUser(User user);

删除,修改和查看:

	/**
     * Desc:更新用户信息
     */
    @Update
    void updateUser(User user);

    /**
     * @param user 传入一个User类
     */
    @Delete
    void delUser(User user);

    /**
     * @return 返回User集合
     */
    @Query("select * from User")
    List<User> queryAll();

    /**
     * @param age 删除输入的id对应的数据
     * @return 返回User集合
     */
    @Query("select * from User where age =:age")
    List<User> query(int age);

在接口上面加上注释@Dao

三:创建database类:

在单线程应用中,记得使用单例模式,使用一个数据库;

package com.example.demo.jetpack.room.database;

import android.content.Context;

import androidx.annotation.NonNull;
import androidx.room.Database;
import androidx.room.DatabaseConfiguration;
import androidx.room.InvalidationTracker;
import androidx.room.Room;
import androidx.room.RoomDatabase;
import androidx.sqlite.db.SupportSQLiteOpenHelper;

import kotlin.jvm.Synchronized;

/**
 * @Author winiymissl
 * @Date 2023-10-22 14:37
 * @Version 1.0
 */
@Database(version = 1, entities = User.class)
public abstract class MyDataBase extends RoomDatabase {
    public abstract Dao userDao();

    private static MyDataBase myDataBase = null;
//    private static Context context;

    //    public MyDataBase(Context context) {
//        this.context = context;
//    }
    @Synchronized
    public static MyDataBase getDatabase(Context context) {
        if (myDataBase == null) {
            myDataBase = Room.databaseBuilder(context.getApplicationContext(), MyDataBase.class, "app_database").allowMainThreadQueries().build();
            return myDataBase;
        }
        return myDataBase;
    }

    @Override
    public void clearAllTables() {

    }

    @NonNull
    @Override
    protected InvalidationTracker createInvalidationTracker() {
        return null;
    }

    @NonNull
    @Override
    protected SupportSQLiteOpenHelper createOpenHelper(@NonNull DatabaseConfiguration databaseConfiguration) {
        return null;
    }
}

注意:数据库的操作必须放在子线程,在创建build的时候加上方法allowMainThreadQueries(),就可以了,但是最好只在测试中使用。在发行版中就不能再使用这个方法了。

Room数据库的升级:

data数据库的每一次升级都需要自己编写更新的逻辑

  • 在测试中更新使用**fallbackToDestructiveMigration()**方法直接会销毁之前的数据库。
  • 老老实实创建表

当然为了保留用户的数据,我们就必须使用方法二:

  • 创建一个实体类,要求和最初的操作相同
  • 在database类中更新信息:
  1. 更新类名上面的version版本,以及entity类,加上你刚更新的实体类
  2. 创建migration实例,参数填写版本号,重写migrate方法,在里面,通过database使用sql语句对数据库进行操作,添加返回BookDao的抽象方法
  3. 在生成单例的方法里面使用方法addMigrations(),添加刚才的migration。
  • 重新运行程序
package com.example.demo.jetpack.room.database;

import android.content.Context;

import androidx.annotation.NonNull;
import androidx.room.Database;
import androidx.room.DatabaseConfiguration;
import androidx.room.InvalidationTracker;
import androidx.room.Room;
import androidx.room.RoomDatabase;
import androidx.room.migration.Migration;
import androidx.sqlite.db.SupportSQLiteDatabase;
import androidx.sqlite.db.SupportSQLiteOpenHelper;

import kotlin.jvm.Synchronized;

/**
 * @Author winiymissl
 * @Date 2023-10-22 14:37
 * @Version 1.0
 */
@Database(version = 2, entities = {User.class, Book.class})
public abstract class MyDataBase extends RoomDatabase {
    public abstract UserDao userDao();

    public abstract BookDao bookDao();

    private static MyDataBase myDataBase = null;
    public static final Migration MIGRATION_1_2 = new Migration(1, 2) {
        @Override
        public void migrate(@NonNull SupportSQLiteDatabase database) {
            database.execSQL("create table Book (id integer primary key autoincrement not null, name text not null, pages integer not null)");
        }
    };


    public static MyDataBase getDatabase(Context context) {
        if (myDataBase == null) {
            myDataBase = Room.databaseBuilder(context.getApplicationContext(), MyDataBase.class, "app_database").addMigrations(MIGRATION_1_2).allowMainThreadQueries().build();
            return myDataBase;
//            .addMigrations(MIGRATION_1_2)
        }
        return myDataBase;
    }
	//......
}

workManager:

workManager是一个用于管理任务的高级工具,可以对后台服务,实现更加人性化的操作:

什么是workManager?

WorkManager 是 Android Jetpack 库的一部分,用于帮助您轻松管理后台任务和异步工作。它提供了一个强大的、高度可定制的任务调度框架,用于执行延迟任务、周期性任务和与系统约束(例如充电状态、网络连接、设备空闲等)相关的任务。WorkManager 旨在使后台任务的管理变得更加简单,并确保这些任务在不同版本的 Android 上都能可靠地运行。

  1. 兼容性:WorkManager 兼容 Android 5.0(API 级别 21)及更高版本的 Android 设备,因此无需担心版本兼容性问题。
  2. 约束任务:您可以定义任务,以响应特定约束,例如只在设备空闲时运行、仅在设备充电时运行、仅在有网络连接时运行等。这有助于减少不必要的电池消耗和数据使用。
  3. 延迟任务:WorkManager 允许您安排执行任务的延迟时间,以便在需要时执行任务,而不会立即执行。
  4. 周期性任务:您可以创建周期性任务,例如每天、每周或每小时运行一次任务,以便在后台定期执行操作。
  5. 任务链:WorkManager 支持任务依赖关系,允许您构建任务链,其中一个任务的输出可以成为另一个任务的输入,以便以特定顺序运行任务。
  6. 可观察性:WorkManager 提供了 LiveData 和 RxJava 支持,以便您可以轻松地观察任务的执行状态和结果。
  7. 灵活性:WorkManager 允许您使用 Worker 类定义具体的任务逻辑,并根据需要定制 Worker。您可以将长时间运行的操作、文件下载、数据同步等操作放在 Worker 中。
  8. 后台任务管理:WorkManager可确保即使在应用关闭或设备重启后,任务也能被正确调度和执行。

为什么需要workManager?

  1. 可靠性:WorkManager 通过使用不同的后台任务调度技术(如 JobScheduler、AlarmManager、Firebase JobDispatcher等)来保证任务的可靠性。这意味着即使在应用关闭或设备重启后,WorkManager 也会尽力确保任务会得到执行。
  2. 版本兼容性:WorkManager 兼容 Android 5.0(API 级别 21)及更高版本的设备,使您无需担心版本兼容性问题。
  3. 约束任务:WorkManager 允许您根据约束条件来运行任务,例如只在设备空闲时运行、仅在充电时运行、仅在有网络连接时运行等。这有助于减少不必要的电池消耗和数据使用。
  4. 延迟任务:您可以安排任务在未来的特定时间点或延迟一段时间后执行,而不需要编写复杂的计时逻辑。
  5. 周期性任务:WorkManager 支持定期运行任务,例如每天、每周或每小时执行一次任务,使之适合用于定期数据同步、通知等场景。
  6. 任务链:WorkManager 允许您创建任务链,其中一个任务的输出可以成为另一个任务的输入,以便以特定顺序运行任务,实现任务依赖关系。
  7. 可观察性:WorkManager 提供 LiveData 和 RxJava 支持,允许您轻松地观察任务的执行状态和结果,以便更好地监控任务的进度。
  8. 后台任务管理:WorkManager 管理了任务的生命周期,并确保它们在需要时得到执行,即使应用程序已经关闭。这对于执行定期的、可靠的后台操作非常有用。
  9. 模块化:WorkManager 使用 Worker 类来执行任务,这使任务逻辑分离,易于维护和测试。

总之,WorkManager 是 Android 开发中的一种强大工具,使后台任务的管理和调度变得更加容易,同时考虑了设备约束和可靠性。它有助于开发者更好地管理后台任务,提高应用的性能和用户体验。无论是数据同步、通知、定期任务还是其他后台操作,WorkManager 都是一个强大的解决方案。

workManager如何使用?

基本用法:

  1. 定义一个后台任务,实现任务的逻辑
  2. 配置后台任务的运行条件和约束信息,并构建后台任务请求
  3. 将该后台任务请求传入Workmanager的enqueue方法里面,系统会在适合的时间运行

第一步:定义一个后台任务

每一个后台必须继承worker类,并调用它唯一的构造函数。然后重新父类中的方法doWork(),在这里面编写具体的逻辑,

dowork不会运行在主线程,因此可以执行耗时的逻辑,dowork要求返回一个Result对象,用于表示任务运行的结果,

  • 成功返回 Result.success()
  • 失败返回 Result.failure()
  • 返回Result.retry(),也代表着失败,可以结合WorkRequest.Bulider的setBackoffCriteria()重新执行任务,

第二步:配置任务:

这其实是最复杂的一步,因为可以配置的内容非常多,现在只进行最基本的配置

使用OneTimeWorkRequest类设置运行一次的任务:

OneTimeWorkRequest build = new OneTimeWorkRequest.Builder(SimpleWork.class).build();

得到一个WorkManager实例,然后将Work的扩展类,放入运行队列里面.

主要活动:

package com.example.demo.jetpack.workmanager;

import androidx.appcompat.app.AppCompatActivity;
import androidx.work.OneTimeWorkRequest;
import androidx.work.WorkManager;

import android.os.Bundle;

import com.example.demo.R;

public class SimpleWorkActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_simple_work);
        OneTimeWorkRequest build = new OneTimeWorkRequest.Builder(SimpleWork.class).build();
        WorkManager.getInstance(this).enqueue(build);
    }
}

第三步:传入workManager

WorkManager.getInstance(this).enqueue(build);

传入到队列里面.

复杂用法(实现配置更多功能):

前面创建work类,重写doWork方法都和前面一样。区别在于创建任务实例的时候,配置的信息,使用的方法会更多。

  • 可以添加标签 addTag(String tag);

我们如果需要取消任务, 可以直接通过任务请求的id取消(通过方法 cancelWorkById()),如果通过Tag的话,可以一次删除多个任务请求。(cancelAllWorkByTag())

cancelAllWork()用于取消所有任务请求

在这里插入图片描述

这个就是任务的id,是由系统自动分配的。

  • 当任务执行返回的是Result.retry()方法,进行继续访问结合方法setBackoffCriteria(),
setBackoffCriteria(BackoffPolicy.LINEAR, 10, TimeUnit.SECONDS)

这方法一共有三个参数:

  1. 第一个参数 用于指定如果任务再次执行失败,不断的重新进行执行,
  2. 第二个
  3. 第三个参数和第二个参数一起决定了多久之后,执行新的任务,时间最短不能低于10秒,LINEAR指的是使用线性的方式延迟,EXPONENTIAL是使用指数方式延迟。
  • 返回Result.success()和返回Result.failure()有什么区别

这两个值是用于通知任务运行结果的,可以在活动中进行监听,

jetpack的简单使用_第8张图片

				         WorkManager.getInstance(this).getWorkInfoByIdLiveData(build.getId()).observe(this, new Observer<WorkInfo>() {
            @Override
            public void onChanged(WorkInfo workInfo) {
                if (WorkInfo.State.SUCCEEDED == workInfo.getState()) {

                }
                if (WorkInfo.State.FAILED == workInfo.getState()) {

                }
            }
        });

使用方法getWorkInfoByIdLiveData(),参数就是系统给任务请求分配的id,方法返回LiveData类,添加监听,对返回结果进行判断。

  • 链式任务

定义了多个任务,第一个使用方法beginWith(),剩下的任务只需要接着用**then()**方法就好了而且,必须是前一个方法执行成功才会执行后面的任务。

你可能感兴趣的:(Android,android)