yunshuipiao / Potato

Read the fucking source code for the Android interview
Apache License 2.0
80 stars 12 forks source link

AAC: ViewModel #37

Open yunshuipiao opened 5 years ago

yunshuipiao commented 5 years ago

AAC: ViewModel

[TOC]

前一篇文章介绍了 AAC 最基本的功能,处理生命周期。

在之前的 mvc 开发模式中,与 UI 无关的逻辑都写在 Activity 或者 Fragment 中,文件过大且不利于测试;后来处理 mvp 分离逻辑部分,但是需要处理生命周期的问题,对状态的大量判断和空的判断。有了 lifecycle 处理生命周期,所以 这里的 viewmodel 可以用来纯粹的处理逻辑部分,并用来保存 UI 中的数据和状态。

ViewModel是存储UI相关数据并不会因为旋转而销毁的类。

简单认识

这里还是从 FragmentActivity 来认识一下 ViewModel.

public class FragmentActivity extends ComponentActivity implements
        ViewModelStoreOwner,
        ActivityCompat.OnRequestPermissionsResultCallback,
        ActivityCompat.RequestPermissionsRequestCodeValidator {
        }

@SuppressWarnings("WeakerAccess")
public interface ViewModelStoreOwner {
    /**
     * Returns owned {@link ViewModelStore}
     *
     * @return a {@code ViewModelStore}
     */
    @NonNull
    ViewModelStore getViewModelStore();
}
public class ViewModelStore {

    private final HashMap<String, ViewModel> mMap = new HashMap<>();
    public final void clear() {
        for (ViewModel vm : mMap.values()) {
            vm.onCleared();
        }
        mMap.clear();
    }
}

 */
public abstract class ViewModel {
    // 当viewmodel 销毁时调用,清除数据,防止泄漏。
    @SuppressWarnings("WeakerAccess")
    protected void onCleared() {
    }
}

这里有下面几个问题需要弄清楚:

  1. viewModel 如何创建和保存
  2. viewModel 如何在屏幕旋转后不销毁而后恢复数据

ViewModel 的创建和保存

在上面的 ViewModelStore 中,可以看到将 ViewModel 保存的代码,这里可以猜测创建时候就保存在 hashMap 中。

另一方面在 UI 中使用 ViewModelProviders 来创建 ViewModel。

 public class MyFragment extends Fragment {
     public void onStart() {
         UserModel userModel = ViewModelProviders.of(getActivity()).get(UserModel.class);
     }
 }
// 创建当给定 Activity 存活状态一致的 ViewModel。
@NonNull
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity,
        @Nullable Factory factory) {
    Application application = checkApplication(activity);
    // 实例化 ViewModel 的工厂
    if (factory == null) {
        factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
    }
    return new ViewModelProvider(activity.getViewModelStore(), factory);
}

    @NonNull
    @MainThread
    public static ViewModelProvider of(@NonNull Fragment fragment, @Nullable Factory factory) {
        Application application = checkApplication(checkActivity(fragment));
        if (factory == null) {
            factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
        }
        return new ViewModelProvider(fragment.getViewModelStore(), factory);
    }
// ViewModelProvider.java
    @NonNull
    @MainThread
    public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {
        String canonicalName = modelClass.getCanonicalName();
        // viewmodel 不能是局部或者匿名类
        if (canonicalName == null) {
            throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
        }
        return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
    }
@NonNull
@MainThread
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
    ViewModel viewModel = mViewModelStore.get(key);
        // 判断 viewmodel 是不是对应的 Class
    if (modelClass.isInstance(viewModel)) {
        //noinspection unchecked
        return (T) viewModel;
    } else {
        //noinspection StatementWithEmptyBody
        if (viewModel != null) {
            // TODO: log a warning.
        }
    }
        // 没有则创建,并保存在 store。
    viewModel = mFactory.create(modelClass);
    mViewModelStore.put(key, viewModel);
    //noinspection unchecked
    return (T) viewModel;
}

ViewModel 的生命周期

1813550-dfc5732b031a4fd5

ViewModel 会永远留在内存中,知道其 of() 对象的销毁。

总结

ViewModel 作为 UI 之后处理逻辑,保存状态和数据的类,随着屏幕的旋转 Activity 销毁重建后恢复数据, 并且可以在不同 Fragment 之前传入使用同一个 ViewModel 共享数据。