文章目录

  • 简介
  • 导入
  • ViewModel 的使用
    • 新建 MyViewModel 类,继承自 ViewModel
    • 修改 MainActivity
    • 运行效果
    • 对比不使用 ViewModel 的情况
    • ViewModel 的局限性
  • ViewModelSavedState
    • 导入 ViewModelSavedState
    • 修改 MyViewModel
    • 修改 MainActivity
  • AndroidViewModel

简介

Android 的页面在创建与销毁时,会触发不同的生命周期。当 Activity 重建时,页面上的数据会丢失。为了保存页面的数据,我们以前通常的做法是在 onSaveInstanceState 中,将数据保存到 bundle 中,再在 onCreate 中将 bundle 中的数据取出来。
现在有了 ViewModel,我们就无需再用这种方法保存,因为 ViewModel 会自动感知生命周期,处理数据的保存与恢复。引用一张官网的介绍图:

导入

android {    ...    kotlinOptions {        jvmTarget = JavaVersion.VERSION_1_8.toString()    }}dependencies {    ...    implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0"    implementation 'androidx.fragment:fragment-ktx:1.2.4'}

ViewModel 的使用

新建 MyViewModel 类,继承自 ViewModel

class MyViewModel : ViewModel() {    var number = 0}

修改 MainActivity

布局文件中只有一个 id 为 tv 的 TextView 和一个 id 为 btn 的 Button,故省略布局文件。

class MainActivity : AppCompatActivity() {    private val myViewModel by viewModels<MyViewModel>()    override fun onCreate(savedInstanceState: Bundle?) {        super.onCreate(savedInstanceState)        setContentView(R.layout.activity_main)        tv.text = myViewModel.number.toString()        btn.setOnClickListener {            myViewModel.number++            tv.text = myViewModel.number.toString()        }    }}

运行效果

对比不使用 ViewModel 的情况

如果不使用 ViewModel ,只用一个 number 变量保存数据的话,页面重建时数据将会丢失,运行效果如下:

ViewModel 的局限性

当进程在后台被系统杀死后,ViewModel 里的数据还是会丢失。为了模拟这个情景,我们先到开发者选项中将 Don't keep activities 打开:

这个设置打开的作用是,当我们点击 home 键使程序进入后台时,程序会立刻被系统杀掉。
这时我们再次运行以上使用 ViewModel 的程序,效果如下:

可以看到,当进程被系统回收后,ViewModel 中的数据丢失了。为了解决这个问题,我们需要用到 ViewModelSavedState.

ViewModelSavedState

导入 ViewModelSavedState

implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.2.0"

修改 MyViewModel

导入依赖后,我们就多了一个构造方法,允许 ViewModel 传入一个 SavedStateHandle 类。这个类的内部使用了一个 HashMap 保存数据。

const val NUMBER_KEY = "number_key"class MyViewModel(private val state: SavedStateHandle) : ViewModel() {    var number: Int        get() {            return state.get<Int>(NUMBER_KEY) ?: 0        }        set(value) {            state[NUMBER_KEY] = value        }}

可以看到,我们在 number 的 get 方法中,通过 SavedStateHandle 获取 number,如果没有获取到,默认返回 0;在 set 方法中,修改 state 中对应的值。

修改 MainActivity

class MainActivity : AppCompatActivity() {    private val myViewModel by lazy {        ViewModelProvider(this, SavedStateViewModelFactory(application, this)).get(MyViewModel::class.java)    }    override fun onCreate(savedInstanceState: Bundle?) {        super.onCreate(savedInstanceState)        setContentView(R.layout.activity_main)        tv.text = myViewModel.number.toString()        btn.setOnClickListener {            myViewModel.number++            tv.text = myViewModel.number.toString()        }    }}

唯一的修改在于 myViewModel 的初始化,不妨记做固定写法。

再次运行程序,数据就不会丢失了。

需要注意的是,这里的数据也不是永久保存的,当手机重启或者用户手动杀掉进程后,数据仍然会丢失。

如果需要持久化存储,可以使用 SharedPreferences或数据库将其存储起来。
当调用这些存储方法时,往往我们都会用到 Context,所以 Android 给我们提供了一个 AndroidViewModel 类。

AndroidViewModel

AndroidViewModel 类做的事情很简单,就是封装了一个 Application 字段。
源码如下:

public class AndroidViewModel extends ViewModel {    @SuppressLint("StaticFieldLeak")    private Application mApplication;    public AndroidViewModel(@NonNull Application application) {        mApplication = application;    }    @SuppressWarnings({"TypeParameterUnusedInFormals", "unchecked"})    @NonNull    public <T extends Application> T getApplication() {        return (T) mApplication;    }}

使用如下:

class MyViewModel(application: Application) : AndroidViewModel(application) {    fun test() {        Log.d("~~~", getApplication<Application>().resources.getString(R.string.app_name))    }}

只需继承 AndroidViewModel,我们就可以通过 getApplication 来获取到 Application 对象。
Activity 中对 ViewModel 的初始化、使用还是和以前一样,完全不用修改。

更多相关文章

  1. 解决Android(安卓)sdk manager无法访问google服务器更新的问题
  2. Android(安卓)默认AP名字,以及AP名字存储路径
  3. Android数据库开源框架GreenDao分析
  4. 7-framework--详解 8 Android平台开发-WIFI 驱动移植 -- 详细 9
  5. 安卓开发之 在应用中使用数据库
  6. ContentProvider拾遗
  7. 探究Android(安卓)SQLite3多线程
  8. 详解Android四种存储方式
  9. Android(安卓)如何设置默认语言

随机推荐

  1. 4QCY20各大企业存储公司财报汇总
  2. 快递物流官方商家寄件下单API接口案例代
  3. ENAS pygraphviz 的替换之路
  4. ENAS加载自己的数据集之路
  5. 商家寄件运力接口-查询全国快递公司运力
  6. Django实战之增加评论
  7. 快递物流发货单API接口代码及功能说明
  8. 自定义select 三角样式
  9. 物流快递云打印API接口代码和应用场景
  10. ArcGIS中加载无偏移谷歌卫星影像!奥维官方