3

剖析 Android 架构组件之 ViewModel

 3 years ago
source link: http://www.androidchina.net/9293.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

ViewModel 是 Android 架构组件之一,用于分离 UI 逻辑与 UI 数据。在发生 Configuration Changes 时,它不会被销毁。在界面重建后,方便开发者呈现界面销毁前的 UI 状态。

本文主要分析 ViewModel 的以下3个方面:

  • 获取和创建过程。
  • Configuration Changes 存活原理。
  • 销毁过程。

1. 依赖库

implementation "androidx.fragment:fragment:1.0.0"
implementation "androidx.lifecycle:lifecycle-viewmodel:2.0.0"
implementation "androidx.lifecycle:lifecycle-extensions:2.0.0"

2. 主要类与接口

import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.lifecycle.ViewModel;
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.ViewModelProvider;
import androidx.lifecycle.ViewModelProvider.Factory;
import androidx.lifecycle.ViewModelProviders;
import androidx.lifecycle.ViewModelStore;
import androidx.lifecycle.ViewModelStoreOwner;

3. ViewModel

ViewModel 是一个抽象类,类中只定义了一个空实现的 onCleared() 方法。

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() {
    }
}

3.1 AndroidViewModel

AndroidViewModel 类扩展了 ViewModel 类,增加了 Application 字段,在构造方法初始化,并提供了 getApplication() 方法。

public class AndroidViewModel extends ViewModel {
    private Application mApplication;

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

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

4. 获取和创建过程分析

获取 ViewModel 对象代码如下:

ViewModelProviders.of(activityOrFragment).get(ViewModel::class.java)

4.1 ViewModelProviders

ViewModelProviders 类提供了4个静态工厂方法 of() 创建新的 ViewModelProvider 对象。

ViewModelProviders.of(Fragment)
ViewModelProviders.of(FragmentActivity)
ViewModelProviders.of(Fragment, Factory)
ViewModelProviders.of(FragmentActivity, Factory)

4.2 ViewModelProvider

ViewModelProvider 负责提供 ViewModel 对象,类中定义了以下两个字段:

private final Factory mFactory;
private final ViewModelStore mViewModelStore;

先说说这两个类的功能。

4.3 ViewModelProvider.Factory

Factory 接口定义了一个创建 ViewModel 的接口 create()ViewModelProvider 在需要时调用该方法新建 ViewModel 对象。

public interface Factory {
    <T extends ViewModel> T create(@NonNull Class<T> modelClass);
}

Android 已经内置了2个 Factory 实现类,分别是:

  • AndroidViewModelFactory 实现类,可以创建 ViewModelAndroidViewModel 子类对象。
  • NewInstanceFactory 类,只可以创建 ViewModel 子类对象。

它们的实现都是通过反射机制调用 ViewModel 子类的构造方法创建对象。

public static class NewInstanceFactory implements Factory {
    @Override
    public <T extends ViewModel> T create(Class<T> modelClass) {
        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);
        }
    }
}

AndroidViewModelFactory 继承 NewInstanceFactory 类,是个单例,支持创建 AndroidViewModel 子类对象。

public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory {

    private static AndroidViewModelFactory sInstance;

    public static AndroidViewModelFactory getInstance(Application application) {
        if (sInstance == null) {
            sInstance = new AndroidViewModelFactory(application);
        }
        return sInstance;
    }

    private Application mApplication;

    public AndroidViewModelFactory(Application application) {
        mApplication = application;
    }

    @Override
    public <T extends ViewModel> T create(Class<T> modelClass) {
        if (AndroidViewModel.class.isAssignableFrom(modelClass)) {
            try {
                return modelClass.getConstructor(Application.class).newInstance(mApplication);
            } catch (NoSuchMethodException e) {
                throw new RuntimeException("Cannot create an instance of " + modelClass, e);
            } catch (IllegalAccessException e) {
                throw new RuntimeException("Cannot create an instance of " + modelClass, e);
            } catch (InstantiationException e) {
                throw new RuntimeException("Cannot create an instance of " + modelClass, e);
            } catch (InvocationTargetException e) {
                throw new RuntimeException("Cannot create an instance of " + modelClass, e);
            }
        }
        return super.create(modelClass);
    }
}

4.4 ViewModelStore

ViewModelStore 类中维护一个 Map<String, ViewModel> 对象存储已创建的 ViewModel 对象,并提供 put()get() 方法。

public class ViewModelStore {
    private final HashMap<String, ViewModel> mMap = new HashMap<>();

    final void put(String key, ViewModel viewModel) {
        ViewModel oldViewModel = mMap.put(key, viewModel);
    }

    final ViewModel get(String key) {
        return mMap.get(key);
    }
}

4.5 ViewModelStoreOwner

ViewModelStore 是来自于 FragmentActivityFragment,它们实现了 ViewModelStoreOwner 接口,返回当前 UI 作用域里的 ViewModelStore 对象。

public interface ViewModelStoreOwner {
    ViewModelStore getViewModelStore();
}

Fragment 类中的实现如下:

public ViewModelStore getViewModelStore() {
    if (getContext() == null) {
        throw new IllegalStateException("Can't access ViewModels from detached fragment");
    }
    if (mViewModelStore == null) {
        mViewModelStore = new ViewModelStore();
    }
    return mViewModelStore;
}

FragmentActivity 类中的实现如下:

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) {
        mViewModelStore = new ViewModelStore();
    }
    return mViewModelStore;
}

4.6 创建 ViewModelProvider

回到 of() 方法的实现。

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

在创建 ViewModelProvider 对象时需要传入 ViewModelStoreFactory 对象。若 factorynull,将使用 AndroidViewModelFactory 单例对象。

4.7 获取 ViewModel 对象

调用 ViewModelProvider 对象的 get() 方法获取 ViewModel 对象,如果在 ViewModelStore 里不存在,则使用 Factory 创建一个新的对象并存放到 ViewModelStore 里。

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

    if (modelClass.isInstance(viewModel)) {
        return (T) viewModel;
    }

    viewModel = mFactory.create(modelClass);
    mViewModelStore.put(key, viewModel);

    return (T) viewModel;
}

5. Configuration Changes 存活原理

ActivityFragment 被系统重建时,ViewModel 对象不会被销毁,新的 ActivityFragment 对象拿到的是同一个 ViewModel 对象。

FragmentActivity#onRetainNonConfigurationInstance() 方法中,会将 ViewModelStore 对象保留起来。

public final Object onRetainNonConfigurationInstance() {
    Object custom = onRetainCustomNonConfigurationInstance();

    FragmentManagerNonConfig fragments = mFragments.retainNestedNonConfig();

    if (fragments == null && mViewModelStore == null && custom == null) {
        return null;
    }

    NonConfigurationInstances nci = new NonConfigurationInstances();
    nci.custom = custom;
    nci.viewModelStore = mViewModelStore;
    nci.fragments = fragments;
    return nci;
}

然后在 onCreate() 方法能获取之前保留起来的 ViewModelStore 对象。

protected void onCreate(Bundle savedInstanceState) {
    mFragments.attachHost(null /*parent*/);
    super.onCreate(savedInstanceState);

    NonConfigurationInstances nc = (NonConfigurationInstances) getLastNonConfigurationInstance();
    if (nc != null) {
        mViewModelStore = nc.viewModelStore;
    }
    // ...
}

Fragment 作用域里是如何实现的呢?在 FragmentActivityonRetainNonConfigurationInstance() 方法中里有这样一句代码:

FragmentManagerNonConfig fragments = mFragments.retainNestedNonConfig();

实现保留的机制是一样的,只不过放在 FragmentManagerNonConfig 对象中。是在 FragmentManager#saveNonConfig() 方法中将 ViewModelStore 对象保存到 FragmentManagerNonConfig 里的。

void saveNonConfig() {
    ArrayList<Fragment> fragments = null;
    ArrayList<FragmentManagerNonConfig> childFragments = null;
    ArrayList<ViewModelStore> viewModelStores = null;
    if (mActive != null) {
        for (int i=0; i<mActive.size(); i++) {
            Fragment f = mActive.valueAt(i);
            if (f != null) {
                if (f.mRetainInstance) {
                    if (fragments == null) {
                        fragments = new ArrayList<Fragment>();
                    }
                    fragments.add(f);
                    f.mTargetIndex = f.mTarget != null ? f.mTarget.mIndex : -1;
                    if (DEBUG) Log.v(TAG, "retainNonConfig: keeping retained " + f);
                }
                FragmentManagerNonConfig child;
                if (f.mChildFragmentManager != null) {
                    f.mChildFragmentManager.saveNonConfig();
                    child = f.mChildFragmentManager.mSavedNonConfig;
                } else {
                    // f.mChildNonConfig may be not null, when the parent fragment is
                    // in the backstack.
                    child = f.mChildNonConfig;
                }

                if (childFragments == null && child != null) {
                    childFragments = new ArrayList<>(mActive.size());
                    for (int j = 0; j < i; j++) {
                        childFragments.add(null);
                    }
                }

                if (childFragments != null) {
                    childFragments.add(child);
                }
                if (viewModelStores == null && f.mViewModelStore != null) {
                    viewModelStores = new ArrayList<>(mActive.size());
                    for (int j = 0; j < i; j++) {
                        viewModelStores.add(null);
                    }
                }

                if (viewModelStores != null) {
                    viewModelStores.add(f.mViewModelStore);
                }
            }
        }
    }
    if (fragments == null && childFragments == null && viewModelStores == null) {
        mSavedNonConfig = null;
    } else {
        mSavedNonConfig = new FragmentManagerNonConfig(fragments, childFragments,
        viewModelStores);
    }
}

该方法的调用顺序是:FragmentActivity#onSaveInstanceState() -> FragmentManager#saveAllState() -> FragmentManager#saveNonConfig()

6. 销毁过程

FragmentActivity 类的 onDestory() 方法中。

@Override
protected void onDestroy() {
    super.onDestroy();
    if (mViewModelStore != null && !isChangingConfigurations()) {
        mViewModelStore.clear();
    }
    mFragments.dispatchDestroy();
}

Fragment 类的 onDestory() 方法中。

public void onDestroy() {
    mCalled = true;
    FragmentActivity activity = getActivity();
    boolean isChangingConfigurations = activity != null && activity.isChangingConfigurations();
    if (mViewModelStore != null && !isChangingConfigurations) {
        mViewModelStore.clear();
    }
}

先判断是否有发生 Configuration Changes,如果没有则会调用 ViewModelStoreclear() 方法,再一一调用每一个 ViewModelonCleared() 方法。

public final void clear() {
    for (ViewModel vm : mMap.values()) {
        vm.onCleared();
    }
    mMap.clear();
}

以上便是 ViewModel 3个主要过程的剖析,这里做一下总结。

  • 通过 ViewModelProviders 创建 ViewModelProvider 对象,调用该对象的 get() 方法获取 ViewModel 对象。 当 ViewModelStore 里不存在想要的对象,ViewModelProvider 会使用 Factory 新建一个对象并存放到 ViewModelStore 里。
  • 当发生 发生 Configuration Changes 时,FragmentActivity 利用 getLastNonConfigurationInstance()onRetainNonConfigurationInstance() 方法实现 ViewModelStore 的保留与恢复,进而实现 ViewModel 对象的保活。
  • FragmentActivityFragment 被销毁时,会根据是否发生 Configuration Changes 来决定是否销毁 ViewModel

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK