jetpack-ViewModel 源码解析

888 阅读4分钟

前言

1.ViewModel 设计的职责可以用来管理 Activity 、Fragment 中的数据,并可以处理Activity 和 Fragment 之间的数据传递即通信;
2. ViewModel通常在有周期的组件内创建 如 Activity /Fragment 创建时创建,只要组件为销毁,ViewModel 就是可用的
3.ViewModel 在 横竖屏切换时即使Activity 销毁了 ,ViewModel不会销毁,新的实例依然可以拿到值
4. ViewModel 设计的职责只是用来保存view 绑定的数据,千万不要用来做View 引用维持View 的存储和恢复

一、ViewModel 抽象类

ViewModel 是个抽象类

public abstract class ViewModel {
    //这里不用ConCurrentHashMap 是因为会导致数据丢失
    @Nullable
    private final Map<String, Object> mBagOfTags = new HashMap<>();
    //是否是清除状态
    private volatile boolean mCleared = false;
    //当数据被清除时调用 及调用了 clear 就会触发
    @SuppressWarnings("WeakerAccess")
    protected void onCleared() {
    }
   
    @MainThread
    final void clear() {
        mCleared = true;
        if (mBagOfTags != null) {
            synchronized (mBagOfTags) {
                for (Object value : mBagOfTags.values()) {
                    // see comment for the similar call in setTagIfAbsent
                    closeWithRuntimeException(value);
                }
            }
        }
        onCleared();
    }
    //添加数据 key 和 value,如果已经存在返回存在的result 否则返回 新加的result
    @SuppressWarnings("unchecked")
    <T> T setTagIfAbsent(String key, T newValue) {
        T previous;
        synchronized (mBagOfTags) {
            previous = (T) mBagOfTags.get(key);
            if (previous == null) {
                mBagOfTags.put(key, newValue);
            }
        }
        T result = previous == null ? newValue : previous;
        //如果是清除状态,则再执行一次关闭操作,如果已经关闭再执行关闭时是无效的
        if (mCleared) {
            closeWithRuntimeException(result);
        }
        return result;
    }

    //通过key 获取value 
    <T> T getTag(String key) {
        if (mBagOfTags == null) {
            return null;
        }
        synchronized (mBagOfTags) {
            return (T) mBagOfTags.get(key);
        }
    }
    // 关闭操作
    private static void closeWithRuntimeException(Object obj) {
        if (obj instanceof Closeable) {
            try {
                ((Closeable) obj).close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

二、ViewModelStore

ViewModelStore是用来存储 各种ViewModel的,内部是一个HashMap 来保存数据

public class ViewModelStore {
    //存放 ViewModel
    private final HashMap<String, ViewModel> mMap = new HashMap<>();
    //保存ViewModel 如果已经存在,则 触发一次清空逻辑
    final void put(String key, ViewModel viewModel) {
        ViewModel oldViewModel = mMap.put(key, viewModel);
        if (oldViewModel != null) {
            oldViewModel.onCleared();
        }
    }
    //通过key 获取 ViewModel
    final ViewModel get(String key) {
        return mMap.get(key);
    }
    //获取所有存储的key 
    Set<String> keys() {
        return new HashSet<>(mMap.keySet());
    }
    //清除所有ViewModel 并触发 clear 操作
    public final void clear() {
        for (ViewModel vm : mMap.values()) {
            vm.clear();
        }
        mMap.clear();
    }
}

ViewModelStore 的获取方式是怎的呢 ?


三、 ViewModelStoreOwner

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

ViewModelStoreOwner 是ViewModelStore 的持有者,是个接口类,那就是谁实现了这个接口,谁就是提供ViewModelStore的生产者。

public class ComponentActivity extends androidx.core.app.ComponentActivity implements
        ViewModelStoreOwner,
        HasDefaultViewModelProviderFactory {
      
    private ViewModelStore mViewModelStore;
    private ViewModelProvider.Factory mDefaultFactory;
    //实现 ViewModelStoreOwner 接口,可以获取 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;
    }

    //实现 HasDefaultViewModelProviderFactory 接口,对外提供 ViewModelProvider.Factory 生成ViewModel
    @NonNull
    @Override
    public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {
        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 (mDefaultFactory == null) {
            mDefaultFactory = new SavedStateViewModelFactory(
                    getApplication(),
                    this,
                    getIntent() != null ? getIntent().getExtras() : null);
        }
        return mDefaultFactory;
    }

}

ComponentActivity 实现了 ViewStoreOwner 和 HasDefaultViewModelProviderFactory 接口 用来返回 ViewModelStore 和 ViewModelProvider.Factory

​ 我们看到 都判断了 getApplication() 为null 的情况下会抛出异常,getApplication 返回的 mApplication 是在 Activity 的 attach中赋值的 ,也就是在 attach 之前会一直为空,这之前使用会抛出异常。

Activity.Java
  
@UnsupportedAppUsage
final void attach(Context context, ActivityThread aThread,
        Instrumentation instr, IBinder token, int ident,
        Application application, Intent intent, ActivityInfo info,
        CharSequence title, Activity parent, String id,
        NonConfigurationInstances lastNonConfigurationInstances,
        Configuration config, String referrer, IVoiceInteractor voiceInteractor,
        Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken) {
    /**省略其他代码***/
    mApplication = application;
    /***省略其他代码***/
  
}

四、ViewModelProvider.Factory

我们看到 ViewModelProvider中定义了接口 Factory,实现该接口可以返回一个 Factory.

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

ComponentActivity 默认返回的是 SavedStateViewModelFactory,该类实现了 Factory,并禁用了一个参数的 create 接口,实现了两个参数的接口,需要带一个key:

        @NonNull
        public abstract <T extends ViewModel> T create(@NonNull String key,
                @NonNull Class<T> modelClass);

        @NonNull
        @Override
        public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
            throw new UnsupportedOperationException("create(String, Class<?>) must be called on "
                    + "implementaions of KeyedFactory");
       }

五、ViewModelProvider

ViewModelProvider 有两个成员变量: ViewModel 工厂类,和ViewModel 存储的地方

private final Factory mFactory;
private final ViewModelStore mViewModelStore;

ViewModelProvider 有三个构造方法:

//传入 ViewModelStoreOwner owner 实现接口返回 ViewModelStore 和 Factoty,如果不是 HasDefaultViewModelProviderFactory,则 NewInstanceFactory 生成一个(采用单例)
public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
    this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
            ? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
            : NewInstanceFactory.getInstance());
}
//传入 ViewModelStoreOwner owner 实现接口返回 ViewModelStore ,Factory 自定义传入
public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) {
    this(owner.getViewModelStore(), factory);
}
//传入 ViewModelStoreOwner  ViewModelStore 和 Factory 自定义传入
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
    mFactory = factory;
    mViewModelStore = store;
}

ViewModelProvider 获取ViewModel有两种方法:

//不传入key,默认key 是 androidx.lifecycle.ViewModelProvider.DefaultKey + modelClass.getCanonicalName()
@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);
}

//传入key,如果有已经存在 则返回已经存在的否则生成一个 放入 ViewModelStore 中
@SuppressWarnings("unchecked")
@NonNull
@MainThread
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
    ViewModel viewModel = mViewModelStore.get(key);
    //这里判断了 ViewModel 是都已经存在 
    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.
        }
    }
    //新生成ViewModel 并放入 ViewModelStore中
    if (mFactory instanceof KeyedFactory) {
        viewModel = ((KeyedFactory) mFactory).create(key, modelClass);
    } else {
        viewModel = mFactory.create(modelClass);
    }
    mViewModelStore.put(key, viewModel);
    return (T) viewModel;
}

可以看到 viewModel 是通过 ViewModelStore 按key和 value 存储的,那么就为数据可恢复提供了机制。

六、数据恢复

我们先看下要恢复数据,就要看如何恢复 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;
}

可以看到 获取ViewModelStore会先从 NonConfigurationInstances 获取

接下来我们看下 NonConfigurationInstances 这个是从哪里得来的?,可以看到 mLastNonConfigurationInstances 是在activity attach 时参数传入和赋值的

@UnsupportedAppUsage
final void attach(Context context, ActivityThread aThread,
        Instrumentation instr, IBinder token, int ident,
        Application application, Intent intent, ActivityInfo info,
        CharSequence title, Activity parent, String id,
        NonConfigurationInstances lastNonConfigurationInstances,
        Configuration config, String referrer, IVoiceInteractor voiceInteractor,
        Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken) {
    /**省略部分代码**/
    mLastNonConfigurationInstances = lastNonConfigurationInstances;
    /**省略部分代码**/
}

attach 调用是在 ActivityThread.java 中调用 performLanchActivity 中中调用的。

那么 lastNonConfigurationInstances 是什么时候保存的呢?

ComponentActivity.java
@Override
@Nullable
@SuppressWarnings("deprecation")
public final Object onRetainNonConfigurationInstance() {
    // Maintain backward compatibility.
    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;
}

onRetainNonConfigurationInstance 是在 performDestroyActivity 中调用保存的

那么 mLastNonConfigurationInstances 会置空么?

Activity.java
final void performResume(boolean followedByPause, String reason) {
    dispatchActivityPreResumed();
    mLastNonConfigurationInstances = null;
}

mLastNonConfigurationInstances 会在 performResume中置空。

总结

a. CompontentActivity 实现了 ViewModelOwner 和 Factoty 接口,可以提供 ViewModelStore和 ViewModelFactory
b.ViewModelFactory 用来生成ViewModel
c. ViewModelStore 存储数据时采用hashmap,用来存储数据,key 即是 ViewModel 的类名或者自定义的key
d.ViewModelProvider 封装了 ViewModelStore的获取、存储, Factory 生成viewModel 等,一个人承担了所有
e. activityThread 在 performDestroy 时会 调用 onRetainNonConfigurationInstance 来保存数据,在activityAttach 时会 将activity 保存的 NonConfigurationInstances 恢复,并恢复ViewModelStore 中的数据