文章目錄
簡介
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>
來獲取到 Application 對象。
Activity 中對 ViewModel 的初始化、使用還是和以前一樣,完全不用修改。