源碼解析Android Jetpack組件之ViewModel的使用
前言
在之前 LiveData 源碼淺析的博客中提到了 ViewModel 組件,當(dāng)時(shí)對(duì) ViewModel 的解釋是 “生命周期比Activity” 更長(zhǎng)的對(duì)象。本文就來(lái)了解下其實(shí)現(xiàn)原理。
依賴版本
// 注意這里的 appcompat、activity-ktx、fragment-ktx // 高版本的自動(dòng)引入了 viewmodel-savedstate 實(shí)戰(zhàn)中很少用到的功能 // 篇幅原因 就不再本文中分析 viewmodel-savedstate 擴(kuò)展組件了 implementation 'androidx.appcompat:appcompat:1.0.0' def fragment_version = "1.1.0" def activity_version = "1.0.0" implementation "androidx.activity:activity-ktx:$activity_version" implementation "androidx.fragment:fragment-ktx:$fragment_version" def lifecycle_version = "2.5.1" implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version" implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version" implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version"
基礎(chǔ)使用
定義
class MainViewModel: ViewModel(){ ... }
// or
class MainViewModel(application: Application): AndroidViewModel(application){
val data: String = ""
fun requestData(){
data = "xxx"
}
}
在 MainVieModel 中可以定義 UI 界面中需要的數(shù)據(jù)(對(duì)象、LiveData、Flow 等等)和方法,在 Activity 真正銷(xiāo)毀前 ViewModel 中的數(shù)據(jù)不會(huì)丟失。
Activity 中獲取
val vm = ViewModelProvider(this).get(MainViewModel::class.java) // or // 引入 activity-ktx 庫(kù)可以這樣初始化 ViewModel val vm by viewModels<MainViewModel>() // 通過(guò) vm 可以調(diào)用其中的方法、獲取其中的數(shù)據(jù) vm.requestData() Log.e(TAG, vm.data)
Fragment 中獲取
val vm = ViewModelProvider(this).get(MainViewModel::class.java) // or // 獲取和 Activity 共享的 ViewModel 也就是同一個(gè) ViewModel 對(duì)象 val vm = ViewModelProvider(requireActivity()).get(MainViewModel::class.java)
引入 fragment-ktx 可以這樣初始化
val vm = viewModels<MainViewModel>() // or 效果同上 val vm = activityViewModels<MainViewModel>()
前置知識(shí)
ViewModel 的使用非常簡(jiǎn)單,也很容易理解,就是一個(gè)生命周期長(zhǎng)于 Activity 的對(duì)象,區(qū)別在于不會(huì)造成內(nèi)存泄漏。ViewModel 不是魔法,站在開(kāi)發(fā)者的角度在 ViewModel 沒(méi)有問(wèn)世之前橫豎屏切換需要保存狀態(tài)數(shù)據(jù)的需求通常都是通過(guò) onSaveInstanceState、onRestoreInstanceState 來(lái)實(shí)現(xiàn)。
onSaveInstanceState、onRestoreInstanceState
關(guān)于這兩個(gè)方法這里就簡(jiǎn)單概述一下:onSaveInstanceState 用于在 Activity 橫豎屏切換(意外銷(xiāo)毀)前保存數(shù)據(jù),而 onRestoreInstanceState 是用于 Activity 橫豎屏切換(重建)后獲取保存的數(shù)據(jù);
onSaveInstanceState 調(diào)用流程
由于是在 Activity 銷(xiāo)毀前觸發(fā),那么直接來(lái) ActivityThread 中找到 performPauseActivity 方法:
ActivityThread.java
private Bundle performPauseActivity(ActivityClientRecord r, boolean finished, String reason, PendingTransactionActions pendingActions) {
// ...
if (shouldSaveState) {
callActivityOnSaveInstanceState(r);
}
// ...
}
private void callActivityOnSaveInstanceState(ActivityClientRecord r) {
// ...
// 這里通過(guò) ActivityClientRecord 獲取到 activity
// state 是 Bundle 對(duì)象,后面要保存的數(shù)據(jù)就放在 state 中
mInstrumentation.callActivityOnSaveInstanceState(r.activity, r.state);
// ...
}
這里有 ActivityThread 調(diào)用到了 Instrumentation 中,繼續(xù)看源碼:
Instrumentation.java
public void callActivityOnSaveInstanceState(@NonNull Activity activity, @NonNull Bundle outState) {
activity.performSaveInstanceState(outState);
}
根據(jù)傳入的 activity 調(diào)用其 performSaveInstanceState 方法:
Activity.java
final void performSaveInstanceState(@NonNull Bundle outState) {
onSaveInstanceState(outState);
}
總結(jié)一下,onSaveInstanceState 中我們將數(shù)據(jù)存儲(chǔ)在 Bundle 對(duì)象中,而這個(gè) Bundle 對(duì)象是存儲(chǔ)在 ActivityClientRecord 中。
onRestoreInstanceState 調(diào)用流程
看完了 onSaveInstanceState 的調(diào)用流程,那么 onRestoreInstanceState 的流程就來(lái)簡(jiǎn)單說(shuō)說(shuō),由于在 onStart 后發(fā)生回調(diào),所以直接去看 ActivityThread 中的源碼:
ActivityThread.java
public void handleStartActivity(ActivityClientRecord r, PendingTransactionActions pendingActions, ActivityOptions activityOptions) {
// ...
mInstrumentation.callActivityOnRestoreInstanceState(activity, r.state);
// ...
}
可以看出這里從 ActivityClientRecord 中取出了 activity 和 state 進(jìn)行傳毒,后面就和 onSaveInstanceState 調(diào)用流程一樣了,源碼比較簡(jiǎn)單就不貼了。
onRetainCustomNonConfigurationInstance、getLastCustomNonConfigurationInstance
除了 onSaveInstanceState 和 onRestoreInstanceState,在 Activity 中還有一組方法可以實(shí)現(xiàn)類(lèi)似的功能,就是 onRetainCustomNonConfigurationInstance 和 getLastCustomNonConfigurationInstance,前者即保存數(shù)據(jù),后者即獲取保存的數(shù)據(jù);
簡(jiǎn)單使用
override fun onRetainCustomNonConfigurationInstance(): Any? {
val data = SaveStateData()
return data
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 獲取保存的數(shù)據(jù)
val data = getLastCustomNonConfigurationInstance() as SaveStateData
}
和 onSaveInstanceState 使用的區(qū)別在于 onSaveInstanceState 只能在其參數(shù)中的 Bundle 對(duì)象中寫(xiě)入數(shù)據(jù),而 onRetainCustomNonConfigurationInstance 返回的類(lèi)型是 Any(Java Object)不限制數(shù)據(jù)類(lèi)型。老樣子看一下這組方法的源碼調(diào)用流程。
onRetainCustomNonConfigurationInstance
onRetainCustomNonConfigurationInstance 是在 ComponentActivity 中定義的,默認(rèn)實(shí)現(xiàn)返回 null,其在 onRetainNonConfigurationInstance 方法中被調(diào)用:
ComponentActivity.java
public Object onRetainCustomNonConfigurationInstance() {
// ComponentActivity 中默認(rèn)返回 null
return null;
}
public final Object onRetainNonConfigurationInstance() {
// 保存在了 custom 變量中
Object custom = onRetainCustomNonConfigurationInstance();
// 這里已經(jīng)出現(xiàn) ViewModel 相關(guān)的源碼了,這里先按下不表
ViewModelStore viewModelStore = mViewModelStore;
if (viewModelStore == null) {
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
viewModelStore = nc.viewModelStore;
}
}
if (viewModelStore == null && custom == null) {
return null;
}
// 新建 NonConfigurationInstances 對(duì)象
NonConfigurationInstances nci = new NonConfigurationInstances();
// custom 賦值給了 NonConfigurationInstances 對(duì)象
nci.custom = custom;
nci.viewModelStore = viewModelStore;
return nci;
}
從 ComponentActivity 的這部分源碼中可以看出保存的數(shù)據(jù)最終放在了 NonConfigurationInstances 對(duì)象的 custom 屬性中;接著找 onRetainNonConfigurationInstance 的定義,在 Activity 中:
Activity.java
public Object onRetainNonConfigurationInstance() {
// 默認(rèn)返回 null
return null;
}
NonConfigurationInstances retainNonConfigurationInstances() {
// ComponentActivity 中返回的 NonConfigurationInstances 對(duì)象
Object activity = onRetainNonConfigurationInstance();
// ...
// 注意 這里有新建另一個(gè) NonConfigurationInstances 對(duì)象
NonConfigurationInstances nci = new NonConfigurationInstances();
// ComponentActivity 中返回的 NonConfigurationInstances 對(duì)象
// 存儲(chǔ)到了新的 NonConfigurationInstances 中的 activity 屬性中
nci.activity = activity;
// ...
return nci;
}
在 Activity 類(lèi)中相當(dāng)于做了一層套娃,又新建了一個(gè) NonConfigurationInstances 對(duì)象,將 ComponentActivity 中返回的 NonConfigurationInstances 對(duì)象存了進(jìn)去;
其實(shí)源碼看到這里就可以了,不過(guò)本著刨根問(wèn)底的原則,我們接著再看一下 NonConfigurationInstances 到底存在了哪里?在 ActivityThread.java 中找到了調(diào)用 retainNonConfigurationInstances 的地方:
ActivityThread.java
void performDestroyActivity(ActivityClientRecord r, boolean finishing, int configChanges, boolean getNonConfigInstance, String reason) {
// ...
// 這個(gè) r 是參數(shù)中的 ActivityClientRecord
r.lastNonConfigurationInstances = r.activity.retainNonConfigurationInstances();
}
和 onSaveInstanceState 一樣存儲(chǔ)在了 ActivityClientRecord 中,只不過(guò)換了一個(gè)屬性罷了。
getLastCustomNonConfigurationInstance
看完了存儲(chǔ)的流程,簡(jiǎn)單來(lái)看看取數(shù)據(jù)的流程。既然存的時(shí)候套娃了一下 NonConfigurationInstances,那取數(shù)據(jù)的時(shí)候肯定也需要套娃:
ComponentActivity.java
public Object getLastCustomNonConfigurationInstance() {
// 通過(guò) getLastNonConfigurationInstance 獲取 NonConfigurationInstances
NonConfigurationInstances nc = (NonConfigurationInstances)
getLastNonConfigurationInstance();
// 返回 custom
return nc != null ? nc.custom : null;
}
那么在 Activity 中肯定還需要取一次 ActivityClientRecord 中的 NonConfigurationInstances:
Activity.java
NonConfigurationInstances mLastNonConfigurationInstances;
public Object getLastNonConfigurationInstance() {
// 返回其 activity 字段
return mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.activity : null;
}
// mLastNonConfigurationInstances 賦值在 attach 方法中
final void attach(Context context, /*參數(shù)太多 省略了*/ NonConfigurationInstances lastNonConfigurationInstances) {
// ...
mLastNonConfigurationInstances = lastNonConfigurationInstances;
// ...
}
可以看出在 Activity attach 方法中就已經(jīng)拿到了套娃后的 NonConfigurationInstances 對(duì)象,我們都知道 Activity attach 方法是在 ActivityThread 的 performLaunchActivity 中調(diào)用,看一下源碼:
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
// ...
// 參數(shù)太多 省略了
// 可以看到是從 ActivityClientRecord 中取出傳入的
activity.attach(appContext, r.lastNonConfigurationInstancesn);
// ...
}
小節(jié)總結(jié)
兩種方式都是將數(shù)據(jù)保存到了 ActivityClientRecord 中,不同的是前者限制了 Bundle 類(lèi)型,后者不限制類(lèi)型(ViewModel 采用的就是后者這組方法實(shí)現(xiàn)),不過(guò)后者已經(jīng)在源碼中被標(biāo)記了刪除,并不影響使用,標(biāo)記刪除是為了讓開(kāi)發(fā)者們利用 ViewModel 來(lái)接管這種需求。下面我們就正式進(jìn)入 ViewModel 源碼。
源碼分析
前置知識(shí)有點(diǎn)長(zhǎng),不過(guò)也幾乎把 ViewModel 的原理說(shuō)透了,ViewModel 的保存、恢復(fù)是利用了系統(tǒng)提供的方法,不過(guò)還有些細(xì)節(jié)還需要在源碼中探索,比如:如何實(shí)現(xiàn) Activity/Fragment 共享 ViewModel?接下來(lái)就來(lái)深入 ViewModel 源碼。
創(chuàng)建
先來(lái)以 Activity 中創(chuàng)建 ViewModel 的這段代碼入手:
val vm by viewModels<MainViewModel>()
查看 viewModels 源碼:
// 這是一個(gè) ComponentActivity 的擴(kuò)展方法
@MainThread // 在主線程中使用
inline fun <reified VM : ViewModel> ComponentActivity.viewModels(
// 從命名也可以看出是一個(gè)工廠模式,默認(rèn)是 null
noinline factoryProducer: (() -> Factory)? = null
): Lazy<VM> {
// 默認(rèn) factoryProducer 為 null
// 返回的是 AndroidViewModelFactory
val factoryPromise = factoryProducer ?: {
val application = application ?: throw IllegalArgumentException(
"ViewModel can be accessed only when Activity is attached"
)
AndroidViewModelFactory.getInstance(application)
}
// 返回了一個(gè) ViewModelLazy 對(duì)象,將 viewModelStore、factoryProducer 傳入
return ViewModelLazy(VM::class, { viewModelStore }, factoryPromise)
}
到這里先暫停看一下 AndroidViewModelFactory 是如何初始化的,以及 viewModelStore 是什么東東:
ViewModelProvider.kt
private var sInstance: AndroidViewModelFactory? = null
@JvmStatic
public fun getInstance(application: Application): AndroidViewModelFactory {
if (sInstance == null) {
sInstance = AndroidViewModelFactory(application)
}
return sInstance!!
}
是一個(gè)單例模式,直接對(duì) AndroidViewModelFactory 進(jìn)行實(shí)例化,再來(lái)看看 mViewModelStore
ComponentActivity.java
// 都是定義在 ComponentActivity 中的變量,默認(rèn) null
private ViewModelStore mViewModelStore;
public ViewModelStore getViewModelStore() {
// ...
if (mViewModelStore == null) { // 第一次啟動(dòng) activity 為 null
// 獲取保存的數(shù)據(jù)
NonConfigurationInstances nc = (NonConfigurationInstances) getLastNonConfigurationInstance();
// 優(yōu)先從保存的數(shù)據(jù)中獲取
if (nc != null) {
mViewModelStore = nc.viewModelStore;
}
// 默認(rèn)返回 ViewModelStore
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
return mViewModelStore;
}
ViewModelStore 內(nèi)部?jī)H僅是管理一個(gè) Map<String, ViewModel>,用于緩存、清理創(chuàng)建的 ViewModel。
回過(guò)頭接著看擴(kuò)展方法 viewModels 返回的 ViewModelLazy:
public class ViewModelLazy<VM : ViewModel> @JvmOverloads constructor(
private val viewModelClass: KClass<VM>, // ViewModel 的 class
private val storeProducer: () -> ViewModelStore, // 默認(rèn)是 ViewModelStore
private val factoryProducer: () -> ViewModelProvider.Factory, // 這里就是 mDefaultFactory
private val extrasProducer: () -> CreationExtras = { CreationExtras.Empty } //
) : Lazy<VM> { // 注意這里返回的 Lazy,延遲初始化
private var cached: VM? = null
override val value: VM
get() { // 由于返回的是 Lazy,也就是當(dāng)使用 ViewModel 時(shí)才會(huì)調(diào)用 get
val viewModel = cached
return if (viewModel == null) { // 第一次調(diào)用是 null,進(jìn)入 if
val factory = factoryProducer() // mDefaultFactory
val store = storeProducer() // ViewModelStore
ViewModelProvider( // 生成 ViewModelProvider 對(duì)象
store,
factory,
extrasProducer()
).get(viewModelClass.java).also { // 調(diào)用其 get 方法獲取 ViewModel
cached = it // 保存到 cached 變量
}
} else {
viewModel
}
}
override fun isInitialized(): Boolean = cached != null
}
這里又出現(xiàn)了一個(gè)陌生的對(duì)象 CreationExtras,其內(nèi)部也是一個(gè) map,可以理解為一個(gè)鍵值對(duì)存儲(chǔ)對(duì)象,只不過(guò)他的 Key 是一個(gè)特殊類(lèi)型。
接著查看 ViewModelProvider 的 get 方法是如何創(chuàng)建 ViewModel 的:
// 存儲(chǔ)ViewModel的key的前綴
internal const val DEFAULT_KEY = "androidx.lifecycle.ViewModelProvider.DefaultKey"
public open operator fun <T : ViewModel> get(modelClass: Class<T>): T {
val canonicalName = modelClass.canonicalName
?: throw IllegalArgumentException("Local and anonymous classes can not be ViewModels")
// 調(diào)用重載方法,拼接 key 傳入
// 當(dāng)前key即為:androidx.lifecycle.ViewModelProvider.DefaultKey$com.xxx.MainViewModel
return get("$DEFAULT_KEY:$canonicalName", modelClass)
}
@MainThread
public open operator fun <T : ViewModel> get(key: String, modelClass: Class<T>): T {
val viewModel = store[key] // 優(yōu)先從 ViewModelStroe 中獲取緩存
if (modelClass.isInstance(viewModel)) { // 如果類(lèi)型相同 直接返回
// 這里我們的 factory 是 AndroidViewModelFactory 所以不會(huì)走這行代碼
(factory as? OnRequeryFactory)?.onRequery(viewModel)
return viewModel as T
}
// ...
// 這里的 defaultCreationExtras 是上一步驟中的 CreationExtras,默認(rèn)值為 CreationExtras.Empty
// MutableCreationExtras 包裝一層就是將 defaultCreationExtras 中所有的鍵值對(duì)都copy一份
val extras = MutableCreationExtras(defaultCreationExtras)
// 將當(dāng)前 ViewModel 的 key 存儲(chǔ)進(jìn)去
extras[VIEW_MODEL_KEY] = key
return try {
// 優(yōu)先調(diào)用雙參數(shù)方法
factory.create(modelClass, extras)
} catch (e: AbstractMethodError) {
// 調(diào)用雙參數(shù)方法發(fā)生異常再調(diào)用單參數(shù)方法
factory.create(modelClass)
}.also {
// 獲取到 ViewModel 后存儲(chǔ)到 viewModelStore 中
// 再提一嘴 viewModelStore 是在 ComponentActivity 中定義
store.put(key, it)
}
}
終于到了創(chuàng)建 ViewModel 的部分了,直接去看 AndroidViewModelFactory 的 create 方法:
ViewModelProvider.kt
override fun <T : ViewModel> create(modelClass: Class<T>, extras: CreationExtras): T {
// application 不為 null 調(diào)用單參數(shù)方法
// 在新建 AndroidViewModelFactory 已經(jīng)傳入了 application,一般情況不為 null
return if (application != null) {
create(modelClass)
} else {
// application 如果為 null,則會(huì)從傳入的 extras 中嘗試獲取
val application = extras[APPLICATION_KEY]
if (application != null) {
// 這個(gè) create 也是雙參數(shù),但不是遞歸,第二個(gè)參數(shù)是 application,源碼貼在下面
create(modelClass, application)
} else {
// 如果 application 仍然為 null,且 ViewModel 類(lèi)型為 AndroidViewModel 則拋異常
if (AndroidViewModel::class.java.isAssignableFrom(modelClass)) {
throw IllegalArgumentException(...)
}
// 類(lèi)型不是 AndroidViewModel 則根據(jù) class 創(chuàng)建
// 注意這里調(diào)用的 super.create 是父類(lèi)方法
// 父類(lèi)方法直接根據(jù) modelClass.newInstance() 創(chuàng)建,就一行就不貼源碼了
super.create(modelClass)
}
}
}
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return if (application == null) { // application 為 null 直接拋異常
throw UnsupportedOperationException(...)
} else {
// 調(diào)用下面的雙參數(shù)方法
create(modelClass, application)
}
}
private fun <T : ViewModel> create(modelClass: Class<T>, app: Application): T {
// 如果是 AndroidViewModel 類(lèi)型則獲取帶 application 的構(gòu)造參數(shù)創(chuàng)建
return if (AndroidViewModel::class.java.isAssignableFrom(modelClass)) {
modelClass.getConstructor(Application::class.java).newInstance(app)
} else {
// 直接調(diào)用父類(lèi) create 方法通過(guò) modelClass.newInstance() 創(chuàng)建
super.create(modelClass)
}
}
至此 Activity 中的 ViewModel 創(chuàng)建過(guò)程源碼就全部分析完了,總結(jié)一下:Activity 中的 ViewModel 創(chuàng)建都是通過(guò)單例工廠 AndroidViewModelFactory 的 create 方法中反射創(chuàng)建,在調(diào)用 create 創(chuàng)建前會(huì)生成字符串 key,創(chuàng)建完成后會(huì)將 key 和 vm 對(duì)象存儲(chǔ)到 ViewModelStore 中,后續(xù)獲取將優(yōu)先從 ViewModelStore 緩存中獲取。
ViewModelStore 是定義在 ComponentActivity 中的,ViewModel 生命周期 “長(zhǎng)于” Activity 的原理跟這個(gè) ViewModelStore 脫不了干系。
恢復(fù)
前面小節(jié)提過(guò),ViewModel 的恢復(fù)利用的是 onRetainNonConfigurationInstance 方法,ViewModelStore 又是定義在 ComponentActivity 中,那么直接去看 ComponentActivity 這部分的源碼:
ComponentActivity.java
public final Object onRetainNonConfigurationInstance() {
// 留給開(kāi)發(fā)者使用的字段
Object custom = onRetainCustomNonConfigurationInstance();
// 獲取當(dāng)前 Activity 的 mViewModelStore
ViewModelStore viewModelStore = mViewModelStore;
if (viewModelStore == null) {
// 如果為 null 則嘗試獲取上一次保存的數(shù)據(jù)
NonConfigurationInstances nc = (NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
// 獲取上一次存儲(chǔ)的 viewModelStore
viewModelStore = nc.viewModelStore;
}
}
// ...
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.custom = custom; // 開(kāi)發(fā)者用的字段
nci.viewModelStore = viewModelStore; // 保存 viewModelStore 的字段
return nci;
}
再來(lái)看一看 ViewModelStore 的獲取方法:
public ViewModelStore getViewModelStore() {
// ...
if (mViewModelStore == null) {
NonConfigurationInstances nc = (NonConfigurationInstances) getLastNonConfigurationInstance();
// 優(yōu)先從保存的數(shù)據(jù)中獲取 viewModelStore
if (nc != null) {
mViewModelStore = nc.viewModelStore;
}
// 獲取不到才會(huì)新建
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
return mViewModelStore;
}
Activity 獲取 mViewModelStore 時(shí)優(yōu)先從 getLastNonConfigurationInstance 獲取到 NonConfigurationInstances 對(duì)象,再?gòu)钠渲蝎@取 viewModelStore,這樣在當(dāng)前 Activity 作用域中創(chuàng)建過(guò)的 ViewModel 都存儲(chǔ)在 ViewModelStore 中,當(dāng)需要再次使用時(shí)走 ViewModel 創(chuàng)建流程會(huì)直接從 ViewModelStore 中返回。
最后
再了解了 onRetainNonConfigurationInstance 這組方法之后再來(lái)探究 ViewModel 的恢復(fù)原理就很簡(jiǎn)單了,onRetainNonConfigurationInstance 也被標(biāo)記為了刪除,google 也希望開(kāi)發(fā)者盡可能的使用 ViewModel 來(lái)保存數(shù)據(jù)(臨時(shí)數(shù)據(jù))。
onRetainNonConfigurationInstance 雖然被標(biāo)記為刪除,但仍然可以正常使用,相比于 onSaveInstanceState 沒(méi)有了數(shù)據(jù)類(lèi)型限制,但并不意味著我們可以隨意存儲(chǔ),比較大的數(shù)據(jù)還是應(yīng)該考慮持久化存儲(chǔ)。
以上就是源碼解析Android Jetpack組件之ViewModel的使用的詳細(xì)內(nèi)容,更多關(guān)于Android Jetpack ViewModel的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android入門(mén)之使用SimpleAdapter實(shí)現(xiàn)復(fù)雜界面布局
這篇文章主要為大家詳細(xì)介紹了Android如何使用SimpleAdapter實(shí)現(xiàn)復(fù)雜的界面布局,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)Android有一定的幫助,需要的可以參考一下2022-11-11
Android 自定義手勢(shì)--輸入法手勢(shì)技術(shù)
這篇文章主要介紹了Android 自定義手勢(shì)--輸入法手勢(shì)技術(shù)的相關(guān)資料,需要的朋友可以參考下2016-10-10
Android開(kāi)發(fā)中的9個(gè)常見(jiàn)錯(cuò)誤和解決方法
這篇文章主要介紹了Android開(kāi)發(fā)中的9個(gè)常見(jiàn)錯(cuò)誤和解決方法,這是Android開(kāi)發(fā)中最常見(jiàn)的9個(gè)錯(cuò)誤,經(jīng)過(guò)各種各樣的整理,以及和熱心網(wǎng)友討論總結(jié)而來(lái),需要的朋友可以參考下2015-01-01
Android開(kāi)發(fā)之開(kāi)發(fā)者頭條(一)啟動(dòng)頁(yè)實(shí)現(xiàn)
這篇文章主要介紹了Android開(kāi)發(fā)之開(kāi)發(fā)者頭條(一)啟動(dòng)頁(yè)實(shí)現(xiàn)的相關(guān)資料,需要的朋友可以參考下2016-04-04
Android編程之客戶端通過(guò)socket與服務(wù)器通信的方法
這篇文章主要介紹了Android編程之客戶端通過(guò)socket與服務(wù)器通信的方法,結(jié)合實(shí)例形式分析了Android基于socket通訊的具體步驟與相關(guān)使用技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-11-11
Android 自動(dòng)化測(cè)試經(jīng)驗(yàn)分享 UiObejct.getFromParent()的使用方法
本篇文章對(duì)Android中UiObejct.getFromParent()的使用進(jìn)行了詳細(xì)的分析介紹。需要的朋友參考下2013-05-05
Android實(shí)現(xiàn)卡片翻轉(zhuǎn)動(dòng)畫(huà)
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)卡片翻轉(zhuǎn)動(dòng)畫(huà),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-01-01

