val userAgeLiveData: LiveData
get() = _userAgeLiveData
fun loadUserName(userId: String){
val accountRepository = AccountRepository()
Log.i(“ViewModel=====”, "loadUserName: ")
viewModelScope.launch {
//suspend 方法,2s 后获取用户信息
val result = accountRepository.requestUserInfo(userId)
// 赋值
_userAgeLiveData.value = result
}
}
override fun onCleared() {
super.onCleared()
Log.i(“ViewModel=====”, "onCleared: ")
}
}
Activity 代码如下:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
txtUserName = findViewById(R.id.txtUserName)
Log.i(TAG, "onCreate: ")
val accountViewModel = ViewModelProvider(this).get(AccountViewModel::class.java)
findViewById(R.id.btnGetUserInfo).setOnClickListener {
accountViewModel.loadUserName(“jackie”)
}
accountViewModel.userAgeLiveData.observe(this, Observer {
//通知UI,UI 进行操作
Log.i(TAG, “onCreate: observe change=”)
txtUserName.text = it
})
}
点击按钮后获取用户信息,执行了 ViewModel 中的方法loadUserName
jetpackdemo I/ViewModel=====: loadUserName:
jetpackdemo I/AccountActivity=====: onCreate: observe change=
配置变化后(屏幕旋转后),日志打印如下:
jetpackdemo I/AccountActivity=====: onPause:
jetpackdemo I/AccountActivity=====: onDestroy:
jetpackdemo I/AccountActivity=====: onCreate:
jetpackdemo I/AccountActivity=====: onStart:
jetpackdemo I/AccountActivity=====: onCreate: observe change=
可以看到 Activity 重新创建了,我们并没有点击按钮执行loadUserName
方法,但是却回调了Observer 的 onChange 方法;而且 ViewModel 的 onClear方法并没有执行,说明 ViewModel 并没有销毁重建。
注意
ViewModel 的创建方式使用的是
val accountViewModel = ViewModelProvider(this).get(AccountViewModel::class.java)
而不是
val accountViewModel = AccountFactory().create(AccountViewModel::class.java)
class AccountFactory: ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class): T {
if (modelClass.isAssignableFrom(AccountViewModel::class.java)){
return AccountViewModel() as T
}
throw IllegalArgumentException(“Unknown ViewModel class”)
}
}
Fragment 间数据共享
Activity 中的两个或更多 Fragment 互相通信是一种很常见的需求。假设你有一个 Fragment,在该 Fragment 中,用户从列表中选择一项,还有另一个 Fragment,用于显示选定项的内容。这种情况不太容易处理,因为这两个 Fragment 都需要定义某种接口描述,并且所有者 Activity 必须将两者绑定在一起。此外,这两个 Fragment 都必须处理另一个 Fragment 尚未创建或不可见的情况。
可以使用 ViewModel
对象解决这个常见的难点。这两个 Fragment 可以使用其 Activity 范围共享 ViewModel
来处理此类通信,代码如下:
class SharedViewModel : ViewModel() {
val selected = MutableLiveData()
fun select(item: Item) {
selected.value = item
}
}
class MasterFragment : Fragment() {
private lateinit var itemSelector: Selector
// Use the ‘by activityViewModels()’ Kotlin property delegate
// from the fragment-ktx artifact
private val model: SharedViewModel by activityViewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
itemSelector.setOnClickListener { item ->
// Update the UI
}
}
}
class DetailFragment : Fragment() {
// Use the ‘by activityViewModels()’ Kotlin property delegate
// from the fragment-ktx artifact
private val model: SharedViewModel by activityViewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
model.selected.observe(viewLifecycleOwner, Observer { item ->
// Update the UI
})
}
}
这两个 Fragment 都会检索包含它们的 Activity。这样,当这两个 Fragment 各种获取 ViewModelProvider
时,他们会收到相同的SharedViewModel
实例(其范围限定为该 Activity)。
此方法具有以下优势:
-
Activity 不需要执行任何操作,也不需要对此通信有任何了解。
-
除了
SharedViewModel
约定之外,Fragment 不需要相互了解。如果其中一个 Fragment 消失,另一个 Fragment 将继续照常工作。 -
每个 Fragment 都有自己的生命周期,而不受另一个 Fragment 的生命周期的影响。如果一个 Fragment 替换另一个 Fragment,界面将继续工作而没有任何问题。
ViewModel 的生命周期
ViewModel
对象存在的时间范围是获取ViewModel
时传递给 VIewModelProvider 的 Lifecycle。也就是上面我们调用的代码:
val accountViewModel = ViewModelProvider(this).get(AccountViewModel::class.java)
这里的this
是Activity
实例对象,因为我们的Activity
实现了 Lifecycle
的关联。ViewModel
将一直留在内存中,直到限定其存在时间范围的 Lifecycle
永久消失:对于 Activity,是在 Activity 完成时;而对于 Fragment,是在 Fragment 分离时。
下图是 Activity 在屏幕旋转而后结束时所处的各种生命周期状态。该图还在关联 Activity 生命周期的旁边显示了 ViewModel
的生命周期。这些基本状态同样适用于 Fragment 的生命周期。
通常是在系统首次调用 Activity 对象的 onCreate()
时请求ViewModel
。ViewModel
存在的时间范围是从首次请求ViewModel
直到 Activity 完成并销毁。
源码分析
在分析源码前,我先提出两个问题,ViewModel 是如何做到让数据可在发生屏幕旋转等配置更改后继续留存(也就是说ViewModel 实例依然存在)?Fragment 之间是如何通过 ViewModel 共享数据的?
先来看看ViewModel
类
public abstract class ViewModel {
···
private volatile boolean mCleared = false;
/**
-
This method will be called when this ViewModel is no longer used and will be destroyed.
-
-
It is useful when ViewModel observes some data and you need to clear this subscription to
-
prevent a leak of this ViewModel.
*/
//该方法将会在 ViewModel 被清除时调用,可以在这个方法里做一些取消注册,防止内存泄漏
@SuppressWarnings(“WeakerAccess”)
protected void onCleared() {
}
@MainThread
final void clear() {
mCleared = true;
···
onCleared();
}
···
}
ViewModel 只是一个抽象类,clear()
方法会在 ViewModel 被清除时调用有,用户可以通过重写 onCleared()
方法来处理一些额外的操作。
再从调用处开始分析
val accountViewModel = ViewModelProvider(this).get(AccountViewModel::class.java)
我们再来看看ViewModelProvider
这个类:
public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
-
? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
- NewInstanceFactory.getInstance());
}
public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) {
this(owner.getViewModelStore(), factory);
}
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
mFactory = factory;
mViewModelStore = store;
}
最终调用到的构造器都是第三个构造器,再来看看ViewModelStore
和Factory
是什么,因为我们传入的是 Activity(是ViewModelStoreOwner
和HasDefaultViewModelProviderFactory
的实现者),所以才会有owner.getViewModelStore()
,第二个参数是getDefaultViewModelProviderFactory()
。
ViewModelStoreOwner
可以理解为 ViewModelStore(ViewModel 存储器)的拥有者,也就是说我们的 Activity/Fragment 是 ViewModel 存储器的拥有者。
然后我们来看看看ViewModelStore
是什么?
// 存储 ViewModel
public class ViewModelStore {
private final HashMap<String, ViewModel> mMap = new HashMap<>();
final void put(String key, ViewModel viewModel) {
ViewModel oldViewModel = mMap.put(key, viewModel);
if (oldViewModel != null) {
oldViewModel.onCleared();
}
}
final ViewModel get(String key) {
return mMap.get(key);
}
Set keys() {
return new HashSet<>(mMap.keySet());
}
/**
- Clears internal storage and notifies ViewModels that they are no longer used.
*/
public final void clear() {
for (ViewModel vm : mMap.values()) {
vm.clear();
}
mMap.clear();
}
}
可以看到ViewModelStore
的代码很简单,就是用一个 HashMap 存储了key(String)
和value(ViewModel)
,这里的 key 的名字为
// key 的名字
DEFAULT_KEY + “:” + canonicalName
private static final String DEFAULT_KEY =
“androidx.lifecycle.ViewModelProvider.DefaultKey”;
String canonicalName = modelClass.getCanonicalName();
因为第二个参数是通过getDefaultViewModelProviderFactory()
获取到的,前面说过Activity
是 HasDefaultViewModelProviderFactory
的实现类,我们再来看看Activity
中的getDefaultViewModelProviderFactory()
方法
@NonNull
@Override
public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {
if (getApplication() == null) {
throw new IllegalStateException("Your activity is not yet attached to the "
- “Application instance. You can’t request ViewModel before onCreate call.”);
}
if (mDefaultFactory == null) {
mDefaultFactory = new SavedStateViewModelFactory(
getApplication(),
this,
getIntent() != null ? getIntent().getExtras() : null);
}
return mDefaultFactory;
}
可以看到是通过一个SavedStateViewModelFactory
来获取ViewModelProvider.Factory
,命名也很清晰直观,就是保存状态的 ViewModel 工厂。
下面我们再来看看get(AccountViewModel::class.java)
方法
@NonNull
@MainThread
public T get(@NonNull Class modelClass) {
String canonicalName = modelClass.getCanonicalName();
if (canonicalName == null) {
throw new IllegalArgumentException(“Local and anonymous classes can not be ViewModels”);
}
return get(DEFAULT_KEY + “:” + canonicalName, modelClass); //key的名字
}
@SuppressWarnings(“unchecked”)
@NonNull
@MainThread
public T get(@NonNull String key, @NonNull Class modelClass) {
ViewModel viewModel = mViewModelStore.get(key); //从 hashmap 中获取 viewmodel 实例
if (modelClass.isInstance(viewModel)) {
if (mFactory instanceof OnRequeryFactory) {
((OnRequeryFactory) mFactory).onRequery(viewModel);
}
return (T) viewModel;
} else {
//noinspection StatementWithEmptyBody
if (viewModel != null) {
// TODO: log a warning.
}
}
//使用 Factory 创建
if (mFactory instanceof KeyedFactory) {
viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
} else {
viewModel = (mFactory).create(modelClass);
}
//存入 viewModelStore
mViewModelStore.put(key, viewModel);
return (T) viewModel;
}
简单来说就是从mViewModelStore
获取 ViewModel,如果没有获取到,就使用 Factory 创建,然后存入mViewModelStore
。
这样逻辑就清楚了,ViewModelProvider(this).get(AccountViewModel::class.java)
会把ViewModel
存入ViewModelStore
中。
因为 Activity 实现了ViewModelStoreOwner
接口,可以理解为 ViewModelStore(ViewModel 存储器)的拥有者,也就是说我们的 Activity/Fragment 是 ViewModel 存储器的拥有者。
public interface ViewModelStoreOwner {
/**
-
Returns owned {@link ViewModelStore}
-
@return a {@code ViewModelStore}
*/
@NonNull
ViewModelStore getViewModelStore();
}
然后我们来看看该方法在 Activity 中的实现
@NonNull
@Override
public ViewModelStore getViewModelStore() {
if (getApplication() == null) {
throw new IllegalStateException("Your activity is not yet attached to the "
- “Application instance. You can’t request ViewModel before onCreate call.”);
}
if (mViewModelStore == null) {
//从getLastNonConfigurationInstance 获取
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
//恢复viewmodelstore
// Restore the ViewModelStore from NonConfigurationInstances
mViewModelStore = nc.viewModelStore;
}
//如果还是获取不到,就新建一个
if (mViewModelStore == null) {
mViewModelStore = new ViewModelStore();
}
}
return mViewModelStore;
}
从该方法中可以看到,Activity(ViewModelStoreOwner)
内部最终会创建一个ViewModelStore
,用来存储ViewModel
,接下来我们来看getLastNonConfigurationInstance
方法
//ComponentActivity.java
NonConfigurationInstances mLastNonConfigurationInstances;
@Nullable
public Object getLastNonConfigurationInstance() {
return mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.activity : null;
}
static final class NonConfigurationInstances {
Object custom;
ViewModelStore viewModelStore;
}
为什么使用 getLastNonConfigurationInstance 方法呢,我们先来看看 Activity 状态保存和恢复:
onSaveInstanceState和
onRetainNonConfigurationInstance 的使用场景区别
我们知道,在屏幕旋转的时候会执行保存状态和恢复状态的方法
@Override
protected void onSaveInstanceState(Bundle outBundle) { //保存
super.onSaveInstanceState(outBundle);
}
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) { //恢复
super.onRestoreInstanceState(savedInstanceState);
}
//注意,如果你是继承 AppcompactActiviy,该方法已经在它的父类 ComponentActivity 定义为 final,子类无法重写
//继承 Activity 就可以
public Object onRetainNonConfigurationInstance() {
// TODO Auto-generated method stub
// 在这里设置需要保存的内容,在切换时不是bundle了,我们可以直接通过object来代替。
return super.onRetainNonConfigurationInstance();
}
@Nullable
@Override
public Object getLastNonConfigurationInstance() {
return super.getLastNonConfigurationInstance();
}
在 Android 10,他们的执行顺序都在onStop
和onDestory
之间,而且onSaveInstanceState
比onRetainNonConfigurationInstance
先执行,一般情况我们保存的数据不是太大,适合放在 Bundle 中,这个时候使用onSaveInstanceState
比较合适,如果要保存的数据不适合放在 Bundle 中(比如: 一个socket)或是数据比较大(比如 Bitmap),那么这个时间我们就应该使用onRetainNonConfigurationInstance()
,而且我们使用onRetainNonConfigurationInstance()
可以保存任何类型的对象,像AsyncTask
和SQLiteDatabse
,我们都可以进行保存。这些类型的数据可能会被一个新的Activity
重新使用。
也就是说 Bundle 中只能放一些特定的类型,比如基本数据类型,数组,Serialable 对象,而onRetainNonConfigurationInstance
中只要是个 Object 对象就可以了。
同时当某个activity
变得“容易”被系统销毁时,该activity
的onSaveInstanceState
就会被执行,而onRetainNonConfigurationInstance
更多的是时候是在配置改变时操作的,这个时候保存一些不会因为配置改变而发生改变的东西,而且onSaveInstanceState
数据是序列化保存到磁盘中。而onRetainNonConfigurationInstance
保存的数据是存在内存中。
所以这里我们的 ViewModel 肯定是放在onRetainNonConfigurationInstance
方法中,再来看看
//ComponentActivity.java
@Override
@Nullable
public final Object onRetainNonConfigurationInstance() {
Object custom = onRetainCustomNonConfigurationInstance();
ViewModelStore viewModelStore = mViewModelStore;
if (viewModelStore == null) {
// No one called getViewModelStore(), so see if there was an existing
// ViewModelStore from our last NonConfigurationInstance
NonConfigurationInstances nc =
(NonConfigurationInstances) getLastNonConfigurationInstance();
if (nc != null) {
viewModelStore = nc.viewModelStore;
}
}
if (viewModelStore == null && custom == null) {
return null;
}
//新建 NonConfigurationInstances,将 viewModelStore 保存
NonConfigurationInstances nci = new NonConfigurationInstances();
nci.custom = custom;
nci.viewModelStore = viewModelStore;
return nci;
}
可以看到在该方法中保存了viewModelStore
,保存在了NonConfigurationInstances
。而该getLastNonConfigurationInstance
的真正实现是在 Activity.java 类中
//Activity.java
//静态内部类
static final class NonConfigurationInstances {
Object activity;
HashMap<String, Object> children;
FragmentManagerNonConfig fragments;
ArrayMap<String, LoaderManager> loaders;
VoiceInteractor voiceInteractor;
}
NonConfigurationInstances mLastNonConfigurationInstances;
@Nullable
public Object getLastNonConfigurationInstance() {
return mLastNonConfigurationInstances != null
? mLastNonConfigurationInstances.activity : null;
}
而mLastNonConfigurationInstances
的赋值是在attach
方法中的mLastNonConfigurationInstances = lastNonConfigurationInstances;
,
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor voiceInteractor,
Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken) {
attachBaseContext(context);
···
mLastNonConfigurationInstances = lastNonConfigurationInstances;
···
为什么ViewModel 在配置变化后依旧存在?
最后
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。
因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
figurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor voiceInteractor,
Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken) {
attachBaseContext(context);
···
mLastNonConfigurationInstances = lastNonConfigurationInstances;
···
为什么ViewModel 在配置变化后依旧存在?
最后
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。
因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-kryNVVWQ-1715796404994)]
[外链图片转存中…(img-TS4WIJYP-1715796404995)]
[外链图片转存中…(img-fUIW4ezN-1715796404997)]
[外链图片转存中…(img-r2tpg8ld-1715796404998)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!