Android Jetpack Components of ViewModel 学习笔记

Android Jetpack Components of Lifecycle 学习笔记

Android Jetpack Components of LiveData 学习笔记

Android Jetpack Components of ViewModel 学习笔记

Demo 地址:https://github.com/mengzhinan/Lifecycle_LiveData_ViewModel_demo

ViewModel Google 文档:https://developer.android.google.cn/topic/libraries/architecture/viewmodel

环境配置:

与 Lifecycle 和 LiveData 略有不同,在其基础上,还需要导入依赖:

// viewmodel 需要添加的配置
implementation 'android.arch.lifecycle:extensions:1.1.1'

ViewModel 是什么?

ViewModel 类的设计目的是以生命周期意识的方式存储和管理与UI相关的数据。ViewModel 类允许数据在配置更改(如屏幕旋转)中生存。

以我的理解总结为:

1、ViewModel 可以实现在同一个 Activity 对象下的多个 Fragment 之间数据共享。

2、ViewModel 可以实现在手机屏幕旋转前后数据共享,避免不必要的重复数据请求。

先从 Demo 使用看起吧。还是上一篇文章的 LiveData Demo:

public class DataUtil {

    private MutableLiveData<String> name = new MutableLiveData<>();

    public LiveData<String> getNameLiveData(){
        return name;
    }

    public void requestHead() {
        // 模拟请求网络或 DB,然后更新数据
        name.setValue("requestHead success");
    }

    public void requestList() {
        // 模拟请求网络或 DB,然后更新数据
        name.setValue("requestList success");
    }

}
public class LiveDataActivity extends AppCompatActivity {

    private DataUtil dataUtil;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        dataUtil = new DataUtil();
        dataUtil.getNameLiveData().observe(this, data -> {
            // 得到数据更新



        });
    }

    @Override
    protected void onResume() {
        super.onResume();
        dataUtil.requestHead();
        dataUtil.requestList();
    }
}

上面代码其实有个问题,如果 DataUtil 重复创建的话,将会对应的重新创建 MutableLiveData 对象,故无法实现数据共享。先别钻牛角,看 ViewModel 的用法:

public class ListViewModel extends ViewModel {
    private MutableLiveData<String> headLiveData;
    private MutableLiveData<String> listLiveData;

    public LiveData<String> getHeadLiveData() {
        if (headLiveData == null) {
            headLiveData = new MutableLiveData<>();
        }
        return headLiveData;
    }

    public LiveData<String> getListLiveData() {
        if (listLiveData == null) {
            listLiveData = new MutableLiveData<>();
        }
        return listLiveData;
    }

    public void requestHead() {
        // TODO: 2019-07-29
        headLiveData.setValue("head request success");
    }

    public void requestList() {
        // TODO: 2019-07-29
        listLiveData.setValue("list request success");
    }
}
public class ViewModelActivity extends AppCompatActivity {

    private ListViewModel listViewModel;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        listViewModel = ViewModelProviders.of(this).get(ListViewModel.class);
        listViewModel.getHeadLiveData().observe(this, data->{
            // 头部数据更新

        });
        listViewModel.getListLiveData().observe(this, data->{
            // list 数据更新

        });

    }

    @Override
    protected void onResume() {
        super.onResume();
        listViewModel.requestHead();
        listViewModel.requestList();
    }
}

代码说明:

DataUtil 逻辑更改为了 ListViewModel,然后继承了 ViewModel 类。其他的没有本质变化。

在 ViewModelActivity 中,唯一的变化是 ListViewModel 的创建方式变为了:

listViewModel = ViewModelProviders.of(this).get(ListViewModel.class);

我们先来分析 ViewModel 类:

public abstract class ViewModel {
    /**
     * This method will be called when this ViewModel is no longer used and will be destroyed.
     * <p>
     * It is useful when ViewModel observes some data and you need to clear this subscription to
     * prevent a leak of this ViewModel.
     */
    @SuppressWarnings("WeakerAccess")
    protected void onCleared() {
    }
}

这是一个抽象类,其中只有一个方法 onCleared(),当 ViewModel 所在的 Activity 销毁时,回调此方法清理资源。

ViewModel 有一个比较常用的子类 AndroidViewModel,提供 application 对象:

/**
 * Application context aware {@link ViewModel}.
 * <p>
 * Subclasses must have a constructor which accepts {@link Application} as the only parameter.
 * <p>
 */
public class AndroidViewModel extends ViewModel {
    @SuppressLint("StaticFieldLeak")
    private Application mApplication;

    public AndroidViewModel(@NonNull Application application) {
        mApplication = application;
    }

    /**
     * Return the application.
     */
    @SuppressWarnings("TypeParameterUnusedInFormals")
    @NonNull
    public <T extends Application> T getApplication() {
        //noinspection unchecked
        return (T) mApplication;
    }
}

我们再回头分析 ViewModel 获取的方法:

ViewModelProviders.of(this).get(ListViewModel.class);

先追查 get() 方法:

@NonNull
    @MainThread
    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);
    }

继续追查 get() 重载方法:

@NonNull
    @MainThread
    public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
        ViewModel viewModel = mViewModelStore.get(key);

        if (modelClass.isInstance(viewModel)) {
            //noinspection unchecked
            return (T) viewModel;
        } else {
            //noinspection StatementWithEmptyBody
            if (viewModel != null) {
                // TODO: log a warning.
            }
        }

        viewModel = mFactory.create(modelClass);
        mViewModelStore.put(key, viewModel);
        //noinspection unchecked
        return (T) viewModel;
    }

如果传递的 ViewModel 已经缓存了,则把缓存的 ViewModel 对象直接返回;否则才创建新的对象。

那么 mViewModelStore 是如何做到缓存的呢?

public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
        mFactory = factory;
        this.mViewModelStore = store;
    }

发现 mViewModelStore 是在 ViewModelProvider 的构造函数中赋值的。追查到 ViewModelProviders 的 of 方法中:

@NonNull
    @MainThread
    public static ViewModelProvider of(@NonNull FragmentActivity activity,
            @Nullable Factory factory) {
        Application application = checkApplication(activity);
        if (factory == null) {
            factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
        }
        return new ViewModelProvider(ViewModelStores.of(activity), factory);
    }

追索到 ViewModelStores 的 of 方法:

@NonNull
    @MainThread
    public static ViewModelStore of(@NonNull FragmentActivity activity) {
        if (activity instanceof ViewModelStoreOwner) {
            return ((ViewModelStoreOwner) activity).getViewModelStore();
        }
        return holderFragmentFor(activity).getViewModelStore();
    }

找到 HolderFragment 中的 holderFragmentFor() 方法:

@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
    public static HolderFragment holderFragmentFor(FragmentActivity activity) {
        return sHolderFragmentManager.holderFragmentFor(activity);
    }

其实看到这儿,基本可以看出原理:

在 Activity 中创建一个 HolderFragment 对象,并添加到 FragmentManager 中。在 HolderFragment 中保存 ViewModel 集合。

因此,当 Activity 销毁时,也会同步销毁 HolderFragment。

那为什么说手机屏幕旋转前后,ViewModel 不会销毁呢?

追查代码,HolderFragment 类的构造函数:

public HolderFragment() {
        setRetainInstance(true);
    }
setRetainInstance(true); 这行代码应该是设置避免在屏幕旋转时重建 Fragment。

所以,总结 ViewModel 的特点:

ViewModel 是依附 HolderFragment 保存在 Activity 的 FragmentManager 中的。在同一个 Activity 下的任意位置、任意 Fragment 中通过 ViewModelProviders.of(this).get(xxx) 方式获取 ViewModel,都会是同一个对象,故避免了重复请求,实现数据共享。

由于 HolderFragment 的特点,可避免屏幕旋转后的数据丢失,避免重复请求。

Demo 地址:https://github.com/mengzhinan/Lifecycle_LiveData_ViewModel_demo

猜你喜欢

转载自blog.csdn.net/fesdgasdgasdg/article/details/100121615