ViewModel原理分析

2,200 阅读5分钟

先附上ViewModel生命周期

image.png

创建ViewModel流程

未命名文件 (1).png

从最简单的by viewModels()看起
【code block 1】

private val viewModel: MainViewModel by viewModels()

viewModels实现
【code block 2】

@MainThread
public inline fun <reified VM : ViewModel> ComponentActivity.viewModels(
    noinline factoryProducer: (() -> Factory)? = null
): Lazy<VM> {
    val factoryPromise = factoryProducer ?: {
        defaultViewModelProviderFactory
    }

    return ViewModelLazy(VM::class, { viewModelStore }, factoryPromise)
}

扩展了ComponentActivity,返回的是一个Lazy对象,传入了ViewModel class,ViewModelStore和默认的factoryPromise,使用reified字段避免了泛型擦除。

往下看ViewModelLazy
【code block 3】

public class ViewModelLazy<VM : ViewModel> (
    private val viewModelClass: KClass<VM>,
    private val storeProducer: () -> ViewModelStore,
    private val factoryProducer: () -> ViewModelProvider.Factory
) : Lazy<VM> {
    private var cached: VM? = null

    override val value: VM
        get() {
            val viewModel = cached
            // 如果viewModel为空,则通过ViewModelProvider创建一个
            return if (viewModel == null) {
                val factory = factoryProducer()
                val store = storeProducer()
                ViewModelProvider(store, factory).get(viewModelClass.java).also {
                    cached = it
                }
            } else {
                // 否则返回缓存的viewModel
                viewModel
            }
        }

    override fun isInitialized(): Boolean = cached != null
}

可以看到ViewModelLazy继承了Lazy类,并且重写了value属性,这样在使用viewModel的时候,就会直接访问value属性。

再看ViewModelProvider
【code block 4】

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

构造方法传入了ViewModelStore和Factory,查看get方法实现
【code block 5】

@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.class.java,再看里面调用的get方法
【code block 6】

@NonNull
@MainThread
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
    // 通过viewModelStore取viewModel
    ViewModel viewModel = mViewModelStore.get(key);
    
    // 如果是同一个类
    if (modelClass.isInstance(viewModel)) {
        if (mFactory instanceof OnRequeryFactory) {
            ((OnRequeryFactory) mFactory).onRequery(viewModel);
        }
        // 如果取到了viewModel,直接返回
        return (T) viewModel;
    } else {
        //noinspection StatementWithEmptyBody
        if (viewModel != null) {
            // TODO: log a warning.
        }
    }
    // 通过factory创建viewModel
    if (mFactory instanceof KeyedFactory) {
        viewModel = ((KeyedFactory) mFactory).create(key, modelClass);
    } else {
        viewModel = mFactory.create(modelClass);
    }
    // 将创建的viewModel存储到viewModelStore中
    mViewModelStore.put(key, viewModel);
    return (T) viewModel;
}

传入了DEFAULT_KEY + ":" + canonicalNamemodelClass

DEFAULT_KEY
【code block 7】

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

可以看到get方法首先从viewModelStore中取了viewModel,如果取到了,就直接返回,如果没取到,则通过factory创建viewModel,并将viewModel存储到ViewModelStore中,那么再看看ViewModelStore里有啥
【code block 8】

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) {
            // 如果之前创建了viewModel,将之前的viewModel销毁掉
            oldViewModel.onCleared();
        }
    }

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

    Set<String> keys() {
        return new HashSet<>(mMap.keySet());
    }

    /**
     *  Clears internal storage and notifies ViewModels that they are no longer used.
     */
    public final void clear() {
        for (ViewModel vm : mMap.values()) {
            vm.clear();
        }
        mMap.clear();
    }
}

很简单,就是一个放了HashMap的类,通过key来写入和读取viewModel。

至此,创建ViewModel的过程基本分析完毕了,那么ViewModel是怎样实现在屏幕旋转的时候不被销毁的呢

屏幕旋转时ViewModel是怎样存活下来的

  • 获取viewModelStore

未命名文件 (2).png

  • 保存lastNonConfigurationInstance

未命名文件 (4).png

  • 恢复lastNonConfigurationInstance

未命名文件 (6).png

Activity销毁时保存ViewModel过程

前面讲到viewModels()方法是扩展的ComponentActivity,而且viewModel是从ViewModelStore中取的,那么应该是Activity在销毁的时候保存了ViewModelStore,然后在重建的时候又恢复了ViewModelStore,翻一下ComponentActivity的代码
【code block 9】

public class ComponentActivity extends androidx.core.app.ComponentActivity implements
        ContextAware,
        LifecycleOwner,
        ViewModelStoreOwner,
        HasDefaultViewModelProviderFactory,
        SavedStateRegistryOwner,
        OnBackPressedDispatcherOwner,
        ActivityResultRegistryOwner,
        ActivityResultCaller {
        ...
     }

可以看到ComponentActivity实现了一大堆接口,着重看ViewModelStoreOwner
【code block 10】

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

很简单,就只有一个返回viewModelStore的方法,查看ComponentActivity中实现的地方
【code block 11】

@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.");
    }
    ensureViewModelStore();
    return mViewModelStore;
}

再翻翻ensureViewModelStore方法
【code block 12】

void ensureViewModelStore() {
    if (mViewModelStore == null) {
        NonConfigurationInstances nc =
                (NonConfigurationInstances) getLastNonConfigurationInstance();
        if (nc != null) {
            // Restore the ViewModelStore from NonConfigurationInstances
            mViewModelStore = nc.viewModelStore;
        }
        // ViewModelStore不存在,直接创建一个
        if (mViewModelStore == null) {
            mViewModelStore = new ViewModelStore();
        }
    }
}

喔原来是从NonConfigurationInstances中去读取的ViewModelStore。NonConfigurationInstances代码
【code block 13】

static final class NonConfigurationInstances {
    Object custom;
    ViewModelStore viewModelStore;
}

可以看到里面保存了一个viewModelStore。那么viewModelStore是什么时候赋值的呢,在ComponentActivity的onRetainNonConfigurationInstance方法中
【code block 14】

@Override
@Nullable
@SuppressWarnings("deprecation")
public final Object onRetainNonConfigurationInstance() {
    // Maintain backward compatibility.
    Object custom = onRetainCustomNonConfigurationInstance();

    ViewModelStore viewModelStore = mViewModelStore;
    // 如果viewModelStore不存在
    if (viewModelStore == null) {
        // No one called getViewModelStore(), so see if there was an existing
        // ViewModelStore from our last NonConfigurationInstance
        // 没有调用过getViewModelStore,看下上一个NonConfigurationInstance是否携带了viewModelStore
        NonConfigurationInstances nc =
                (NonConfigurationInstances) getLastNonConfigurationInstance();
        if (nc != null) {
            viewModelStore = nc.viewModelStore;
        }
    }
    
    // viewModelStore为空直接返回
    if (viewModelStore == null && custom == null) {
        return null;
    }
    
    // 新建一个NonConfigurationInstances对象,写入viewModelStore,然后返回
    NonConfigurationInstances nci = new NonConfigurationInstances();
    nci.custom = custom;
    nci.viewModelStore = viewModelStore;
    return nci;
}

那onRetainNonConfigurationInstance又是啥时候调用的呢,在Activity类中的retainNonConfigurationInstances方法中
【code block 15】

NonConfigurationInstances retainNonConfigurationInstances() {
    Object activity = onRetainNonConfigurationInstance();
    ...
    // 创建一个Activity中的NonConfigurationInstances类,存放ComponentActivity中的NonConfigurationInstances类
    NonConfigurationInstances nci = new NonConfigurationInstances();
    nci.activity = activity;
    ...
    
    return nci;
}

注意Activity中的NonConfigurationInstances类和ComponentActivity中的NonConfigurationInstances不是一个类

Activity中的NonConfigurationInstances类
【code block 16】

static final class NonConfigurationInstances {
    Object activity;
    HashMap<String, Object> children;
    FragmentManagerNonConfig fragments;
    ArrayMap<String, LoaderManager> loaders;
    VoiceInteractor voiceInteractor;
}

ComponentActivity中的NonConfigurationInstances类
【code block 17】

static final class NonConfigurationInstances {
    Object custom;
    ViewModelStore viewModelStore;
}

再看retainNonConfigurationInstances是啥时候调用的, 很遗憾,Android Studio找不到调用者了,经过一番查阅才知道,是ActivityThread的performDestroyActivity方法在调用,代码如下
【code block 18】

ActivityClientRecord performDestroyActivity(IBinder token,
                                            boolean finishing,
                                            int configChanges,
                                            boolean getNonConfigInstance,
                                            String reason
) {
    ActivityClientRecord r = mActivities.get(token);
    ...
    if (r != null) {
        
        ...
        
        if (getNonConfigInstance) {
            try {
                // 将NonConfigurationInstances存到ActivityClientRecord中的lastNonConfigurationInstances
                r.lastNonConfigurationInstances
                        = r.activity.retainNonConfigurationInstances();
            } catch (Exception e) {
                if (!mInstrumentation.onException(r.activity, e)) {
                    throw new RuntimeException(
                            "Unable to retain activity "
                            + r.intent.getComponent().toShortString()
                            + ": " + e.toString(), e);
                }
            }
        }
        ...
    }
    ... 
    return r;
}

ActivityThread的performDestroyActivity方法中,在Activity销毁的时候,将存有viewModelStore的NonConfigurationInstances保存到了ActivityClientRecord中,而ActivityClientRecord存放在ActivityThread中的一个ArrayMap中,通过tocken取到,至此,Activity销毁时保存ViewModel的流程捋清楚了,接下来看下Activity重建时是怎样恢复viewModel的。

Activity重建时恢复ViewModel

首先看下lastNonConfigurationInstances这个字段是哪里在读取,在ActivityThread中performLaunchActivity方法中读取的
【code block 19】

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    ActivityInfo aInfo = r.activityInfo;
    
    ...
    
    Activity activity = null;
    try {
        ... 
        
        activity = mInstrumentation.newActivity(
                cl, component.getClassName(), r.intent);
        ...
        
    } catch (Exception e) {
        if (!mInstrumentation.onException(activity, e)) {
            throw new RuntimeException(
                "Unable to instantiate activity " + component
                + ": " + e.toString(), e);
        }
    }

    try {
        ...
        
        if (activity != null) {
            ...
            
            activity.attach(appContext, this, getInstrumentation(), r.token,
                    r.ident, app, r.intent, r.activityInfo, title, r.parent,
                    r.embeddedID, r.lastNonConfigurationInstances, config,
                    r.referrer, r.voiceInteractor, window, r.configCallback,
                    r.assistToken);
            ...

        }
        ...
        
    } catch (SuperNotCalledException e) {
        throw e;

    } catch (Exception e) {
        if (!mInstrumentation.onException(activity, e)) {
            throw new RuntimeException(
                "Unable to start activity " + component
                + ": " + e.toString(), e);
        }
    }

    return activity;
}

再看Activity的attach方法
【code block 20】

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;
    
    ...
    
}

很简单,就是给mLastNonConfigurationInstances赋了个值,而这个值读取的地方在
【code block 21】

@Nullable
public Object getLastNonConfigurationInstance() {
    return mLastNonConfigurationInstances != null
            ? mLastNonConfigurationInstances.activity : null;
}

这个方法被ensureViewModelStore方法调用了,参考【code block 12】,至此ViewModelStore恢复的过程基本捋清楚了,但是还有一点没搞清楚,performLaunchActivity中的ActivityClientRecord是从哪来的,再翻翻看,performLaunchActivity方法是在ActivityThread类中的handleLaunchActivity方法中被调用到的
【code block 22】

@Override
public Activity handleLaunchActivity(ActivityClientRecord r,
        PendingTransactionActions pendingActions, Intent customIntent) {
    
    ...
    
    final Activity a = performLaunchActivity(r, customIntent);
    
    ...

    return a;
}

再看是哪里在调用handleLaunchActivity
【code block 23】

private void handleRelaunchActivityInner(ActivityClientRecord r, int configChanges,
        List<ResultInfo> pendingResults, List<ReferrerIntent> pendingIntents,
        PendingTransactionActions pendingActions, boolean startsNotResumed,
        Configuration overrideConfig, String reason) {
    ... 
    
    handleDestroyActivity(r.token, false, configChanges, true, reason);
    
    ...
    
    handleLaunchActivity(r, pendingActions, customIntent);
}

可以看到handleRelaunchActivityInner先销毁了activity,然后再重新创建了activity,再看handleRelaunchActivityInner是哪里在调用
【code block 24】

@Override
public void handleRelaunchActivity(ActivityClientRecord tmp,
        PendingTransactionActions pendingActions) {

    ...

    ActivityClientRecord r = mActivities.get(tmp.token);
   
    ...
    
    handleRelaunchActivityInner(r,
                                configChanges, 
                                tmp.pendingResults, 
                                tmp.pendingIntents,
                                pendingActions, 
                                tmp.startsNotResumed, 
                                tmp.overrideConfig, 
                                "handleRelaunchActivity");
    
}

真相大白了,原来是handleRelaunchActivity中通过token取到的ActivityClientRecord,致此,ViewModel的重建过程分析完毕。