likes
comments
collection
share

解密ViewModel是如何实现页面状态保存功能?

作者站长头像
站长
· 阅读数 3

前言

  互联网越来越卷,要求越来越高,我们不能仅仅停留在api的使用层面,要深入源码,阅读源码,分析源码,理解源码,最后模仿源码,才能快速提高 " 搬砖 "水平!

一,简介

 ViewModel作为谷歌Jetpack重要组件之一,它不仅可以保存当前页面的状态,还能在页面配置更改后持久化保留相应状态。

解密ViewModel是如何实现页面状态保存功能?

二,使用步骤

1. 普通ViewModel

1.定义MainViewModel类继承ViewModel()

class MainViewModel:ViewModel() {
   val mainLiveData = MutableLiveData<String>() 
}

2.在activity中通过ViewModelProvider获取MainViewModel实例对象。

class MainActivity : AppCompatActivity() {
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val viewModel = ViewModelProvider(this, ViewModelProvider.NewInstanceFactory()).
        get(MainViewModel::class.java)
        viewModel.mainLiveData.observe(this){
            
        }
    }

通过定义一个MainViewModel类继承自ViewModel抽象类,然后再Activity中初始化MainViewModel。

2.提供Content的AndroidViewModel

  1. 如果需要在ManViewModel中使用context,MainViewModel需要继承AndroidViewModel。
class MainViewModel(application: Application):AndroidViewModel(application) {
   val mainLiveData = MutableLiveData<String>()

}

2 .使用ViewModelProvider获取MainViewModel实例对象

 val viewModel = ViewModelProvider(this, ViewModelProvider.AndroidViewModelFactory(application)).get(MainViewModel::class.java)
        viewModel.mainLiveData.observe(this){

        }

三,源码分析

解密ViewModel是如何实现页面状态保存功能?

1. Activity绑定

  1. Activity的父类ComponentActivity实现了ViewModelStoreOwner接口,
public class ComponentActivity extends androidx.core.app.ComponentActivity implements
        ContextAware,
        LifecycleOwner,
        ViewModelStoreOwner,
}
  1. ViewModelStoreOwner中定义了getViewModelStore方法用来提供ViewModelStore
public interface ViewModelStoreOwner {
     //返回一个ViewModelStore
    @NonNull
    ViewModelStore getViewModelStore();
}
  1. ViewModelStore中定义HashMap用来保存ViewModel实例。
public class ViewModelStore {
    //定义HashMap集合存储ViewModel
    private final HashMap<String, ViewModel> mMap = new HashMap<>();
    //存储ViewModel
    final void put(String key, ViewModel viewModel) {
        ViewModel oldViewModel = mMap.put(key, viewModel);
        if (oldViewModel != null) {
            oldViewModel.onCleared();
        }
    }
   //根据key值获取对应的ViewModel
    final ViewModel get(String key) {
        return mMap.get(key);
    }

    Set<String> keys() {
        return new HashSet<>(mMap.keySet());
    }

    /**
     *  回收所有ViewModel并清空HashMap.
     */
    public final void clear() {
        for (ViewModel vm : mMap.values()) {
            vm.clear();
        }
        mMap.clear();
    }
}
  1. 由于ComponentActivity实现了ViewModelStoreOwner接口,在ComponentActivity中调用getViewModelStore可以获取ViewModel的存储类ViewModelStoreViewModelStore中使用HashMap存储ViewModel,如果存储的ViewModel的key值相同,则删除oldViewModel旧的ViewModel,替换成最新的ViewModel。

  2. ComponentActivity中使用Lifecycle对当前activity生命周期进行监听,并在on Destroy生命周期中对保存在ViewModelStore中的ViewModel进行清空释放。

public ComponentActivity() {
        Lifecycle lifecycle = getLifecycle();
        ......................
        .........................
        //使用lifecycle对activity生命周期进行监听
        getLifecycle().addObserver(new LifecycleEventObserver() {
            @Override
            public void onStateChanged(@NonNull LifecycleOwner source,
                    @NonNull Lifecycle.Event event) {
                 //如果生命周期为onDestory
                if (event == Lifecycle.Event.ON_DESTROY) {
                    // 清除context
                    mContextAwareHelper.clearAvailableContext();
                    //如果应用配置没有发生改变
                    if (!isChangingConfigurations()) {
                        //清除ViewModelStore中HashMap存储的ViewModel
                        getViewModelStore().clear();
                    }
                }
            }
        });
     getLifecycle().addObserver(new LifecycleEventObserver() {
            @Override
            public void onStateChanged(@NonNull LifecycleOwner source,
                    @NonNull Lifecycle.Event event) {
                //初始化ViewModelStore
                ensureViewModelStore();
                getLifecycle().removeObserver(this);
            }
        });

ComponentActivity退出销毁时,会调用getViewModelStore().clear(), 清除ViewModelStoreHashMap存储的ViewModel,释放资源。

  1. 调用ensureViewModelStore进行ViewModelStore进行初始化。
void ensureViewModelStore() {
        //如果存储类为null
        if (mViewModelStore == null) {
            //获取上一个保存的NonConfigurationInstances实例对象
            NonConfigurationInstances nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) {
                // 从 NonConfigurationInstances 恢复 ViewModelStore
                mViewModelStore = nc.viewModelStore;
            }
            //否则创建新的ViewModelStore
            if (mViewModelStore == null) {
                mViewModelStore = new ViewModelStore();
            }
        }
    }

  static final class NonConfigurationInstances {
        Object custom;
        //成员变量用来保存ViewModelStore
        ViewModelStore viewModelStore;
    }

NonConfigurationIns tances是一个静态类,它有两个成员变量ObjectViewModelStore ;如果ViewModelstore如果不存在,则将NonConfigurationIns tances的成员变量保存的ViewModelstore赋值给ViewModelStore

    //获取上一次保存的数据
    @Nullable
    public Object getLastNonConfigurationInstance() {
        return mLastNonConfigurationInstances != null
                ? mLastNonConfigurationInstances.activity : null;
    }

mLastNonConfigurationInstances 是上一次保存的数据实例,当Activity被创建的时候,调用attach方法,mLastNonConfigurationInstances = lastNonConfigurationInstances; 当应用配置发生改变时,如屏幕旋转,会导致Activity重建,这个时候会获取上一次保存的mLastNonConfigurationInstances ,并取出其中存储的viewmodestore,最后得到ViewModel,直接从ViewModel中得到数据恢复界面列表。

2. ViewModel保存

  1. 使用ViewModelProvider创建ViewModel
public static ViewModelProvider of(@NonNull FragmentActivity activity) {
     //创建ViewModelProvider对象
    return new ViewModelProvider(activity);
    }
  1. 由于当前activity实现ViewModelStoreOwner接口,所以可以通过getViewModelStore方法获取ViewModelStore对象。
 public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
      //由于当前activity实现ViewModelStoreOwner接口,
      //所以可以通过getViewModelStore方法获取ViewModelStore对象。
     
     this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
                ? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
                : NewInstanceFactory.getInstance());
    }
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
        mFactory = factory;
        mViewModelStore = store;
    }