美文网首页
Android Architecture Component源码

Android Architecture Component源码

作者: 珞泽珈群 | 来源:发表于2019-05-19 00:16 被阅读0次

目录

源码解析目录

问题

用过AAC的人都知道,ViewModel的使用是很简单的,没有太多复杂的东西,而ViewModel本身的源码也异常简单。真正“神奇”的地方在于ViewModel的生命周期:

在Activity的configuration(配置)发生变化时,Activity会被重建,但是ViewModel却不会,直至Activity真正消失的时候,ViewModel的onCleared方法才会被调用。这就是我们的核心问题,这一切是怎么实现的?
(源码版本androidx.lifecycle:lifecycle-viewmodel:2.2.0

1. ViewModel的获取

ViewModel本身是异常简单的:

public abstract class ViewModel {

    protected void onCleared() {
    }

}

ViewModel就是这样,仅仅包含一个onCeared方法(最初的ViewModel的确只有这一个方法,后来又增加了一些方法,但是这些方法都不是给我们使用的,所以可以忽略)。
AAC给我们提供的获取ViewModel的方法很简单:
ViewModelProvider(activity/fragment).get(ViewModel.class) (之前更常见的一种方式是ViewModelProviders.of(activity/fragment).get(ViewModel.class),但是现在这种方式已经被删除了)。

我们来看一下ViewModelProvider是怎么实现的:

@SuppressWarnings("WeakerAccess")
public class ViewModelProvider {

    private static final String DEFAULT_KEY =
            "androidx.lifecycle.ViewModelProvider.DefaultKey";

    /**
     * 创建ViewModel的Factory
     */
    public interface Factory {
        @NonNull
        <T extends ViewModel> T create(@NonNull Class<T> modelClass);
    }

    private final Factory mFactory;
    private final ViewModelStore mViewModelStore;

    public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
        //这里的ViewModelStoreOwner 一般而言不是Activity就是Fragment,并且它们都实现了HasDefaultViewModelProviderFactory,也就是说Activity/Fragment都会提供默认的ViewModelFactory
        this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
                ? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
                : NewInstanceFactory.getInstance());
    }

    /**
     * @param store   ViewModel存储的地方
     * @param factory ViewModel构建的地方
     */
    public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
        mFactory = factory;
        mViewModelStore = store;
    }

    /**
     * 如果不提供key,默认的key就是ViewModel的类名(有前缀)
     */
    @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);
    }

    /**
     * 如果ViewModel在ViewModelStore中已经存在,则可以直接返回使用
     * 如果不存在,则通过Factory新建,默认的工厂是SavedStateViewModelFactory,新建ViewModel的方式是反射
     */
    @NonNull
    @MainThread
    public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
        ViewModel viewModel = mViewModelStore.get(key);

        if (modelClass.isInstance(viewModel)) {
            //给你一个机会再重新获取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);
        //noinspection unchecked
        return (T) viewModel;
    }
}

总结一下ViewModelProvider的作用。ViewModelProvider是我们获取ViewModel的地方,创建ViewModelProvider需要我们提供两个参数:1. ViewModelStore 2. ViewModelProvider.Factory。它们的作用也很明了,分别用来存储ViewModel和创建ViewModel。首先在ViewModelStore中查找,没有的话再通过Factory创建。
ViewModelProviderget方法是可以提供一个key来说明我们需要的是哪个ViewModel。不过,我们可能不常提供,不提供的话默认的key是ViewModel的类名(前面加了一个前缀)。如果我们的Activity或者Fragment需要同一种ViewModel类的两个对象时,key的作用就很明显了。

2. ViewModel的创建和存储

2.1 ViewModelProvider.Factory

默认的ViewModelProvider.Factory是SavedStateViewModelFactory,从这个名字可以看出,这是一个可以为ViewModel提供SavedState的Factory,ViewModel的SavedState是2.x版本新添加的功能,目的是为了在ViewModel中方便的保存状态,在Activity重建的时候使用。不了解这些没关系,因为如果你不使用这个SavedState功能的话,这个SavedStateViewModelFactory其实会退化成AndroidViewModelFactory,它只是通过反射创建ViewModel而已。这要求我们的ViewModel必须有默认构造函数。从ViewModel的生命周期图上可以看出,ViewModel的生命周期要比Activity或者Fragment的更长,ViewModel中自然就不能包含Activity或者Fragment,如果实在需要在ViewModel中使用Context,那么只能使用ApplicationContext,为了方便我们使用,AAC中还定义了一个AndroidViewModel

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也可以继承自AndroidViewModel,这样在ViewModel中就可以方便地使用Application了。而AndroidViewModelFactory也可以通过反射创建继承自AndroidViewModel的ViewModel。
想了解更多关于ViewModel-SavedState的内容,可以查看这篇文章ViewModel如何保存状态——SavedState源码解析

2.2 ViewModelStore

兼容包中的Activity和Fragment皆实现了ViewModelStoreOwner接口,该接口只包含一个方法getViewModelStore

2.2.1 Activity与ViewModelStore

public class ComponentActivity implements ViewModelStoreOwner {

    static final class NonConfigurationInstances {
        Object custom;
        ViewModelStore viewModelStore;
    }
    
    // 延迟创建,直到 getViewModelStore() 被调用时才会被创建
    private ViewModelStore mViewModelStore;
    
    /**
     * 保留所有的 non-config state. 不随配置变化的那些变量。
     * 最重要的就是保留了 ViewModelStore
     */
    @Override
    @Nullable
    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;
        }

        NonConfigurationInstances nci = new NonConfigurationInstances();
        nci.custom = custom;
        nci.viewModelStore = viewModelStore;
        return nci;
    }
    
    /**
     * 从 NonConfigurationInstances 中取出 viewModelStore
     * 没有则新建一个 ViewModelStore
     */
    @NonNull
    @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.");
        }
        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;
    }
}

对于Activity而言,它本身就包含有一个ViewModelStore,在调用getViewModelStore方法时会被创建(如果需要的话)。ViewModel最神奇的地方就是它不会随配置的变化而重建,表现在生命周期上就是文中的第一幅图。这一切是怎么实现的呢?在上述代码中我们已经可以看出来了。Activity的ViewModelStore会在配置发生变化时通过onRetainNonConfigurationInstance方法被保留下来,在getViewModelStore方法中又首先会从NonConfigurationInstances中取出被保留下来的ViewModelStore(如果有的话)。这样,ViewModelStore就不会因为配置的变化而重建,也就是说,我们的ViewModel不受配置变化的影响。

2.2.2 Fragment与ViewModelStore

public class Fragment implements ViewModelStoreOwner {

    //自身并没有实现,委托给了FragmentManager
    @NonNull
    @Override
    public ViewModelStore getViewModelStore() {
        if (mFragmentManager == null) {
            throw new IllegalStateException("Can't access ViewModels from detached fragment");
        }
        return mFragmentManager.getViewModelStore(this);
    }
}

再来看看FragmentManager

final class FragmentManagerImpl extends FragmentManager {
    private FragmentManagerViewModel mNonConfig;
    
    //又委托给了mNonConfig
    @NonNull
    ViewModelStore getViewModelStore(@NonNull Fragment f) {
        return mNonConfig.getViewModelStore(f);
    }
    
    /**
     * 这个方法会在上述方法 getViewModelStore 之前被调用,是mNonConfig被赋值的地方
     * 其中host一般情况下是我们的Activity
     * parent是指父Fragment
     */
    public void attachController(@NonNull FragmentHostCallback host,
                                 @NonNull FragmentContainer container, @Nullable Fragment parent) {
        if (mHost != null) throw new IllegalStateException("Already attached");
        mHost = host;
        mContainer = container;
        mParent = parent;
        if (parent != null) { //父Fragment不为null,则从父Fragment的FragmentManager中获取NonConfig
            mNonConfig = parent.mFragmentManager.getChildNonConfig(parent);
        } else if (host instanceof ViewModelStoreOwner) {
            //host是我们的Activity,它的确实现了ViewModelStoreOwner接口
            ViewModelStore viewModelStore = ((ViewModelStoreOwner) host).getViewModelStore();
            mNonConfig = FragmentManagerViewModel.getInstance(viewModelStore);
        } else {
            mNonConfig = new FragmentManagerViewModel(false);
        }
    }
}

Fragment将getViewModelStore委托给了其FragmentManager,FragmentManager又把getViewModelStore委托给了NonConfig。从NonConfig这个名字可以猜测出,它应该是保存Fragment中与配置无关的变量的地方(比如说我们的ViewModelStore)。而NonConfig的类型却是FragmentManagerViewModel,用一个ViewModel去保存ViewModelStore,这是什么骚操作?

看看FragmentManagerViewModel就知道了

/**
 * FragmentManagerViewModel is the always up to date view of the Fragment's non configuration state
 * FragmentManagerViewModel保存了最新的Fragment的non configuration state
 */
class FragmentManagerViewModel extends ViewModel {

    private static final ViewModelProvider.Factory FACTORY = new ViewModelProvider.Factory() {
        @NonNull
        @Override
        @SuppressWarnings("unchecked")
        public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
            FragmentManagerViewModel viewModel = new FragmentManagerViewModel(true);
            return (T) viewModel;
        }
    };

    /**
     * 从上一段代码中可以看出,此处的ViewModelStore其实就是Activity中的ViewModelStore
     * 也就是说这个FragmentManagerViewModel是被保存在Activity的ViewModelStore中的
     */
    @NonNull
    static FragmentManagerViewModel getInstance(ViewModelStore viewModelStore) {
        ViewModelProvider viewModelProvider = new ViewModelProvider(viewModelStore,
                FACTORY);
        return viewModelProvider.get(FragmentManagerViewModel.class);
    }

    //保存子Fragment的NonConfig,对于我们而言,其实就是保存子Fragment的ViewModelStore
    private final HashMap<String, FragmentManagerViewModel> mChildNonConfigs = new HashMap<>();
    //保存Fragment的ViewModelStore
    private final HashMap<String, ViewModelStore> mViewModelStores = new HashMap<>();

    
    @NonNull
    FragmentManagerViewModel getChildNonConfig(@NonNull Fragment f) {
        FragmentManagerViewModel childNonConfig = mChildNonConfigs.get(f.mWho);
        if (childNonConfig == null) {
            childNonConfig = new FragmentManagerViewModel(mStateAutomaticallySaved);
            mChildNonConfigs.put(f.mWho, childNonConfig);
        }
        return childNonConfig;
    }

    @NonNull
    ViewModelStore getViewModelStore(@NonNull Fragment f) {
        ViewModelStore viewModelStore = mViewModelStores.get(f.mWho);
        if (viewModelStore == null) {
            viewModelStore = new ViewModelStore();
            mViewModelStores.put(f.mWho, viewModelStore);
        }
        return viewModelStore;
    }
}

如注释所说,FragmentManagerViewModel的作用是保存Fragment最新的non configuration state,我们要保存的Fragment的ViewModelStore也是non configuration state,保存在FragmentManagerViewModel中正合适。但是,FragmentManagerViewModel是怎么做到这一点的呢?答案很简单,把FragmentManagerViewModel放到Activity的ViewModelStore中,我们上面已经分析过了,Activity的ViewModelStore在配置发生变化时会被保留下来,所以FragmentManagerViewModel自然也就不受配置变化的影响。想想也是,FragmentManagerViewModel也就是个普通的ViewModel,自然拥有ViewModel的特点,只不过AAC正好利用了这一点,把它用来保存Fragment的non configuration state(我们最关心的Fragment的ViewModelStore也在其中)。

Activity的ViewModelStore是不随配置变化的,Fragment的ViewModelStore也自然不随配置变化。

2.2.3 ViewModelStore

说了这么多ViewModelStore,到底ViewModelStore长啥样?显然ViewModelStore就是存储ViewModel的地方。如前所述,我们实际上是通过字符串key去获取的ViewModel,那么ViewModelStore自然是以Map的形式来存储ViewModel。嗯,基本上这就是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());
    }

    /**
     *  会在Activity、Fragment destroy并且不是因为配置发生变化的时候被调用
     */
    public final void clear() {
        for (ViewModel vm : mMap.values()) {
            vm.clear();
        }
        mMap.clear();
    }
}

3. 隐藏技能

  1. 如果我们需要在同一个Activity或者Fragment中创建多个同一类型的ViewModel,我们可以在获取ViewModel的时候用不同的key进行区分。
  2. 默认情况下AndroidViewModelFactory会帮我们以反射的方式创建ViewModel,但是ViewModel是在View和Model之间起到桥梁的作用,ViewModel一般情况下都需要包含Model层,而使用AndroidViewModelFactory又要求我们的ViewModel必须包含默认构造函数(AndroidViewModel情况除外),这就导致我们不能使用构造函数的方式去注入Model层,有时候这很不方便。还好,我们可以提供自己的ViewModelProvider.Factory。恰好,dagger2有一种能力叫作MultiBindings,是以依赖注入创建ViewModelProvider.Factory的绝佳方式,详见当Dagger2撞上ViewModel
  3. 你可能觉得Activity的onRetainNonConfigurationInstance是个保存NonConfig State的好地方,如果我们也有什么不随配置变化的变量,也可以利用onRetainNonConfigurationInstance来保存,甚至Activity中有个方法叫onRetainCustomNonConfigurationInstance,专门帮我们保存自己的NonConfig State。恭喜你,从源码中又学到了一点没有卵用的东西。Activity觉得你很聪明,并向你扔出一个Deprecated警告。onRetainCustomNonConfigurationInstance方法已经被标记为废弃的,并推荐你使用ViewModel来保存NonConfig State。至于原因,不言自明。

4. 总结

ViewModel要解决的核心问题是怎样在配置发生变化时保留ViewModel。利用Activity提供的onRetainNonConfigurationInstance方法,可以方便的保留不受配置变化影响的数据。
在实现了Activity的ViewModelStore之后,我们便可以利用ViewModel本身的特点去保存Fragment的ViewModelStore,如此这般,Fragment的ViewModel便也不再受配置变化的影响。用Activity中的ViewModelStore去保存FragmentManagerViewModel,而FragmentManagerViewModel又保存了Fragment的ViewModelStore。嗯,真真是踩在自己的肩膀上向上爬。

相关文章

网友评论

      本文标题:Android Architecture Component源码

      本文链接:https://www.haomeiwen.com/subject/uyzxzqtx.html