“我报名参与金石计划1期挑战——分割10万奖池,这是我的第6篇文章,点击检查活动详情”

前语

学习ViewModel之前首要咱们得简略了解下MVP和MVVM,由于ViewModel是MVVM中的一个元素

MVP MVVM

在MVP中View想要调用Model数据层,需求经过中间层Presenter, 这样就实现了View和Model的解耦,这也是MVP和MVC的不同

可是假如一个Activity中有太多交互,那么咱们的View接口数量就会很庞大到达十几个也不足为奇,并且在View层调用了Presenter之后,会反过来调用View层,这样显得繁琐。而MVVM的出现就处理了这个问题。 提到MVVM的话,咱们放上Google的架构图

MVVM中的VM指的便是ViewModel,从上图为没看到,由于ViewModel中持有了LiveData,而LiveData是一个可调查的数据类型,在LiveData原理篇中,咱们做了详细的剖析

在View层中,将被调查的数据LiveData订阅,并供给了一个调查者Observer,当数据产生变化的时分,就会回调Observer中的onChanged()办法,从而更新UI, 这个过程是体系源码帮咱们处理的,所以就没有上面Presenter中调用View的那一步了

ViewModel概述

运用的某个 Activity 中或许包括用户列表。因装备更改而从头创立 Activity 后,新 Activity 有必要从头提取用户列表

关于简略的数据,Activity 能够运用onSaveInstanceState() 办法从 onCreate() 中的绑缚包恢复其数据,但此办法仅适合能够序列化再反序列化的少量数据,而不适合数量或许较大的数据,如用户列表或位图,运用ViewModel能够处理这个问题

别的,界面控制器常常需求进行异步调用,这些调用或许需求一些时间才能返回成果。界面控制器需求管理这些调用,并保证体系在其销毁后清理这些调用以防止潜在的内存泄露

此项管理需求很多的保护作业,并且在因装备更改而从头创立目标的情况下,会形成资源的糟蹋,由于目标或许需求从头宣布现已宣布过的调用,运用ViewModel能够处理这个问题

比如 Activity 和 Fragment 之类的界面控制器首要用于显现界面数据、对用户操作做出呼应或处理操作体系通讯(如权限恳求)

假如要求界面控制器也负责从数据库或网络加载数据,那么会使类越发膨胀。为界面控制器分配过多的职责或许会导致单个类测验自己处理运用的一切作业,而不是将作业托付给其他类。以这种办法为界面控制器分配过多的职责也会大大增加测试的难度

ViewModel 类旨在以重视生命周期的办法存储和管理界面相关的数据。ViewModel 类让数据可在产生屏幕旋转等装备更改后继续存在

ViewModel运用

ViewModel的运用比较简略,咱们想要运用运用的话直接承继ViewModel或许承继AndroidViewModel即可,AndroidViewModel源码如下,他们俩的差异,是AndroidViewModel中多了一个Application的成员变量以及以Application为参数的构造办法,假如你需求Application的话,就直接承继AndroidViewModel即可

public class AndroidViewModel extends ViewModel {
    @SuppressLint("StaticFieldLeak")
    private Application mApplication;
    public AndroidViewModel(@NonNull Application application) {
        mApplication = application;
    }
    /**
     * Return the application.
     */
    @SuppressWarnings({"TypeParameterUnusedInFormals", "unchecked"})
    @NonNull
    public <T extends Application> T getApplication() {
        return (T) mApplication;
    }
}

这儿以我写的一个Demo为例,这个Demo能够在Github上找到,链接如下JetPack Demo,这个Demo用到了一切Jetpack的组件,是学习Jetpack的辅助资料,需求的小伙伴能够下载和star。咱们自定义一个AppsViewModel如下:

class AppsViewModel(appsRepository: AppsRepository) : ViewModel() {
 val apps: LiveData<List<AppEntity>> = appsRepository.loadApps()
}

由于咱们这儿传入了一个参数,所以需求定义一个Factory,代码如下:

	class AppsViewModelFactory(private val repository: AppsRepository) : ViewModelProvider.Factory {
    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
        return AppsViewModel(repository) as T
    }
}

接下来便是运用了:

class AppsListFragment : Fragment() {
    private lateinit var viewModel: AppsViewModel
    //-----1-----
    private val viewModel: AppsViewModel by viewModels {
    	FactoryProvider.providerAppsFactory(requireContext())
	}
	override fun onActivityCreated(savedInstanceState: Bundle?) {
		  //-----2-----
  		  viewModel = ViewModelProviders.of(this,FactoryProvider.providerAppsFactory(requireContext()))
		        .get(AppsViewModel::class.java)
		  //-----3-----
		  viewModel.apps.observe(viewLifecycleOwner, Observer {
		  		//Update UI
		  })
	}
}

咱们先声明晰一个变量viewModel,咱们能够通过注释1处的 by viewModels供给一个自定义的Factory,可是需求添加一个依靠:implementation “androidx.fragment:fragment-ktx:1.2.2;或许采用注释2的办法直接运用ViewModelProviders.of(fragment,factory).get(class)的方式获取实例

然后就直接运用了,在注释3处运用viewmodel.apps.observe将其参加生命周期调查中,假如数据产生变化就会调用Observer的回调,从而更新UI

ViewModel源码

  • 上面咱们采用ViewModelProviders.of(...).get(class)办法获取ViewModel,咱们就从这儿开端源码开端剖析,咱们先看下这个类的源码:

    @Deprecated
    public class ViewModelProviders {
    @Deprecated
    public ViewModelProviders() {
    }

    @Deprecated
    @NonNull
    @MainThread
    public static ViewModelProvider of(@NonNull Fragment fragment) {
        return new ViewModelProvider(fragment);
    }
    @Deprecated
    @NonNull
    @MainThread
    public static ViewModelProvider of(@NonNull FragmentActivity activity) {
        return new ViewModelProvider(activity);
    }
    @Deprecated
    @NonNull
    @MainThread
    public static ViewModelProvider of(@NonNull Fragment fragment, @Nullable Factory factory) {
        if (factory == null) {
            factory = fragment.getDefaultViewModelProviderFactory();
        }
        return new ViewModelProvider(fragment.getViewModelStore(), factory);
    }
    @Deprecated
    @NonNull
    @MainThread
    public static ViewModelProvider of(@NonNull FragmentActivity activity,
            @Nullable Factory factory) {
        if (factory == null) {
            factory = activity.getDefaultViewModelProviderFactory();
        }
        return new ViewModelProvider(activity.getViewModelStore(), factory);
    }
    @SuppressWarnings("WeakerAccess")
    @Deprecated
    public static class DefaultFactory extends ViewModelProvider.AndroidViewModelFactory {
        @Deprecated
        public DefaultFactory(@NonNull Application application) {
            super(application);
        }
    }
    

    }

咱们看到此类中供给了四个办法,其实着四个都以相同,咱们以第四个为例剖析;第一个参数是Activity,第二个参数是一个Factory,默认情况下咱们是不需求传入Factory这个参数的

假如不传入的话,咱们跟进构造办法就能看到默认是用NewInstanceFactory来作为Factory的,看到内部的create办法通过反射生成了一个ViewModel,实例源码如下:

public static class NewInstanceFactory implements Factory {
    private static NewInstanceFactory sInstance;
    /**
     * Retrieve a singleton instance of NewInstanceFactory.
     *
     * @return A valid {@link NewInstanceFactory}
     */
    @NonNull
    static NewInstanceFactory getInstance() {
        if (sInstance == null) {
            sInstance = new NewInstanceFactory();
        }
        return sInstance;
    }
    @SuppressWarnings("ClassNewInstance")
    @NonNull
    @Override
    public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
        //noinspection TryWithIdenticalCatches
        try {
            return modelClass.newInstance();
        } catch (InstantiationException e) {
            throw new RuntimeException("Cannot create an instance of " + modelClass, e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException("Cannot create an instance of " + modelClass, e);
        }
    }
}
  • 假如传入Factory参数的话,就会用咱们自定义的Factory作来生成ViewModel。

  • of办法调用完毕返回的是ViewModelProvider, 然后调用的是get办法,咱们看下这个类的部分源码:

    public class ViewModelProvider {

    private static final String DEFAULT_KEY ="androidx.lifecycle.ViewModelProvider.DefaultKey";
    private final Factory mFactory;
    private final ViewModelStore mViewModelStore;
    //-----1-----
    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;
     }
     public <T extends ViewModel> T get(@NonNull Class<T> 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);
    }
    public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
        ViewModel viewModel = mViewModelStore.get(key);
        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.
            }
        }
        if (mFactory instanceof KeyedFactory) {
            viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
        } else {
            viewModel = (mFactory).create(modelClass);
        }
        mViewModelStore.put(key, viewModel);
        return (T) viewModel;
    }
    

    }

上面截取的部分源码其实是ViewModelProvider最重要的办法了,咱们先看此类中有两个重要的成员变量,其中一个是mFactory, 注释1处看到假如咱们没有传入factory的话,默认实现的是NewInstanceFactory, 印证了咱们之前的说法

还有一个是ViewModelStore, 它是怎样来的呢?由于ComponentActivity中实现了接口ViewModelStoreOwner,在ViewModelProvider的构造办法中调用owner.getViewModelStore(),这个owner便是ComponentActivity本身,然后获取到了ViewModelStore这个变量,实践调用的源码如下:

@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.");
    }
    //-----1-----
    if (mViewModelStore == null) {
        NonConfigurationInstances nc =
                (NonConfigurationInstances) getLastNonConfigurationInstance();
        if (nc != null) {
            // Restore the ViewModelStore from NonConfigurationInstances
            mViewModelStore = nc.viewModelStore;
        }
        if (mViewModelStore == null) {
            mViewModelStore = new ViewModelStore();
        }
    }
    return mViewModelStore;
}

在注释1处,判别假如mViewModelStore == null的话,就会调取getLastNonConfigurationInstance测验获取,假如获取到了就将获取到的赋值给mViewModelStore返回

这儿就涉及到一个重要的知识点了,为什么说ViewModel在横竖屏切换的时分能够耐久的保存数据,不需求像之前相同调用onSaveInstanceState? 由于在Activity被销毁的时分,还会调用别的一个办法onRetainNonConfigurationInstance, 咱们看它在ComponentActivity中的源码实现:

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;
    }
	//----1-----
    NonConfigurationInstances nci = new NonConfigurationInstances();
    nci.custom = custom;
    nci.viewModelStore = viewModelStore;
    return nci;
}

咱们看到在注释1的当地将咱们之前存在的viewModelStore存储到NonConfigurationInstances中了,然后在调用getViewModelStore的时分调用getLastNonConfigurationInstance这样就保证了Activity销毁之前和之后的viewModelStore是同一个,那它里边存储的ViewModel值也便是同样的了

所以ViewModel的生命周期能够用下图来概括:

接下来咱们剖析get办法:

private static final String DEFAULT_KEY =
        "androidx.lifecycle.ViewModelProvider.DefaultKey"
public <T extends ViewModel> T get(@NonNull Class<T> 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);
}
  • 首要获取类的全称的字符串姓名,和DEFAULT_KEY拼凑成一个Key,然后调用get的重载办法如下:

    public T get(@NonNull String key, @NonNull Class modelClass) {
    ViewModel viewModel = mViewModelStore.get(key);
    //—–1—–
    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.
    }
    }
    if (mFactory instanceof KeyedFactory) {
    viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
    } else {
    //—–2—–
    viewModel = (mFactory).create(modelClass);
    }
    mViewModelStore.put(key, viewModel);
    return (T) viewModel;
    }

注释1处判别咱们的modelClass是不是属于ViewModel类型的,并且判别mFactory的类型是否属于OnRequeryFactory类型,假如是的话,就返回值; 在注释2处运用Factory通过反射创立一个viewModel, 然后将其存入mViewModelStore中。咱们看下ViewModelStore的源码:

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<String> 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目标,用来寄存ViewModel。ViewModel的创立到此就完毕了。然后便是运用的问题, 运用如下

override fun onActivityCreated(savedInstanceState: Bundle?) {
	viewModel = ViewModelProviders.of(this,FactoryProvider.providerAppsFactory(requireContext()))
		        .get(AppsViewModel::class.java)
	viewModel.apps.observe(viewLifecycleOwner, Observer {
		  		//Update UI
    }
)	 

好了,以上便是今日要共享的内容,我们觉得有用的话,能够点赞共享一下;假如文章中有什么问题欢迎我们纠正;欢迎在评论区或后台讨论哈~