Android(安卓)Jetpack 组件之 ViewModel(Kotlin)
文章目录
- 简介
- 导入
- 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 的初始化、使用还是和以前一样,完全不用修改。
更多相关文章
- 解决Android(安卓)sdk manager无法访问google服务器更新的问题
- Android(安卓)默认AP名字,以及AP名字存储路径
- Android数据库开源框架GreenDao分析
- 7-framework--详解 8 Android平台开发-WIFI 驱动移植 -- 详细 9
- 安卓开发之 在应用中使用数据库
- ContentProvider拾遗
- 探究Android(安卓)SQLite3多线程
- 详解Android四种存储方式
- Android(安卓)如何设置默认语言