Android JetPack ViewModel源码分析:为什么横竖屏切换数据还可以保存?他是如何保存?为什么activity销毁的时候他是如何跟着销毁?

96 阅读2分钟

一、是什么

ViewModel 是 Android Jetpack 架构组件中的核心类,​​设计目标​​是为 UI 层(Activity/Fragment)提供​​生命周期感知​​的数据管理。它的核心特性是:​​在配置更改(如屏幕旋转)时自动保留数据​​,避免 UI 控制器(Activity/Fragment)因重建导致数据丢失。


二、涉及的类

public class MyViewModel extends ViewModel {
    
}
public class MainActivity extends AppCompatActivity {

    private MyViewModel viewModel;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 初始化 ViewModel
        viewModel = new ViewModelProvider(this).get(MyViewModel.class);
     
    }
}

图片.png


三、源码分析

3.1 创建MyViewModel实例

viewModel = new ViewModelProvider(this).get(MyViewModel.class);

注意到ViewModel不是new,为什么不能new?不能直接实例化?

ViewModel 的核心功能是​​在配置更改(如屏幕旋转)时保留数据​​,并在 Activity/Fragment 完全销毁时自动清理。

直接 new 的问题​​:

  • 若手动实例化 ViewModel(如 MyViewModel viewModel = new MyViewModel()),系统无法将 ViewModel 实例与当前 Activity/Fragment 的生命周期绑定。
  • 当 Activity 因配置更改被销毁重建时,ViewModel 实例会被重新创建,导致数据丢失。

下面我们进入ViewModelProvider,看看他是如何创建出来的。

public constructor(
    owner: ViewModelStoreOwner
) : this(owner.viewModelStore, defaultFactory(owner), defaultCreationExtras(owner))

创建ViewModelProvider。Factory,是一个接口,为什么需要呢?实例化我们的MainViewModel,通过反射完成。 创建ViewModels后并将它们保存在给定ViewModelStoreOwner的存储中。

get方法会将需要反射的viewmodel的class对象给保存起来。

@MainThread
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")
    return get("$DEFAULT_KEY:$canonicalName", modelClass)
}

然后通过factory进行创建,创建以后,在保存到store里面。

@Suppress("UNCHECKED_CAST")
@MainThread
public open operator fun <T : ViewModel> get(key: String, modelClass: Class<T>): T {
    val viewModel = store[key]
    if (modelClass.isInstance(viewModel)) {
        (factory as? OnRequeryFactory)?.onRequery(viewModel)
        return viewModel as T
    } else {
        @Suppress("ControlFlowWithEmptyBody")
        if (viewModel != null) {
            // TODO: log a warning.
        }
    }
    val extras = MutableCreationExtras(defaultCreationExtras)
    extras[VIEW_MODEL_KEY] = key
    // AGP has some desugaring issues associated with compileOnly dependencies so we need to
    // fall back to the other create method to keep from crashing.
    return try {
        factory.create(modelClass, extras)
    } catch (e: AbstractMethodError) {
        factory.create(modelClass)
    }.also { store.put(key, it) }
}

3.2 为什么activity重建,数据不会丢失呢?

ComponentActivityonRetainNonConfigurationInstance() 方法中保留 ViewModelStore。旋转屏幕的时候,会调用onRetainNonConfigurationInstance方法

图片.png

为什么需要 viewModelStore = nc.viewModelStore?​​不是为null?

  1. ​首次创建 Activity​​:

    • Activity 未调用过 getViewModelStore(),因此 mViewModelStorenull
    • 此时若发生配置变更(如第一次旋转屏幕),系统调用 onRetainNonConfigurationInstance()
    • 由于 mViewModelStorenull,代码尝试从 ​​上一个​NonConfigurationInstances(即 nc)中获取 viewModelStore
    • ​若这是首次创建,nc 也为 null​,最终 nci.viewModelStore 仍为 null
  2. ​第二次创建 Activity(第一次配置变更后)​​:

    • 新的 Activity 实例在 onCreate() 中会调用 getViewModelStore() 初始化 mViewModelStore
    • ​此时 mViewModelStore 会被创建​​。
public constructor(
    owner: ViewModelStoreOwner
    //被调用了owner.viewModelStore
) : this(owner.viewModelStore, defaultFactory(owner), defaultCreationExtras(owner))
  1. ​再次发生配置变更(如第二次旋转屏幕)​​:

    • 系统再次调用 onRetainNonConfigurationInstance()
    • 此时 mViewModelStore 已存在,直接保存到新的 nci.viewModelStore 中。
    • ​后续的新 Activity 实例将复用这个 viewModelStore​。

3.3 为什么在页面销毁的时候ViewModel就消失了?

因为结合了lifecycle

file_1745986582550_418.png

OSZAR »