ViewModel原理分析

2020年最后一篇,哈哈。

本篇文章主要分析ViewModel在Activity从销毁到重建时是如何保存并恢复的。

源码版本:androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0

基本流程

首先创建一个简单的ViewModel类。然后通过一个例子来对比一下普通对象和ViewModel对象。

class NameViewModel : ViewModel() {

    //创建一个持有String类型数据的LiveData
    val currentName: MutableLiveData = MutableLiveData("Hello world")
}

简单使用

class ViewModelActivity : AppCompatActivity() {
    //普通的字符串对象
    private var normalText = "Hello world"
    private lateinit var model: NameViewModel

    private lateinit var tvViewModelText: TextView
    private lateinit var tvNormalText: TextView

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_view_model)
        tvNormalText = findViewById(R.id.tv_normal_text)
        tvViewModelText = findViewById(R.id.tv_view_model_text)
        //在onCreate中获取NameViewModel对象。
        model = ViewModelProvider(this).get(NameViewModel::class.java)

        //给TextView设置text
        tvNormalText.text = normalText
        tvViewModelText.text = model.currentName.value

        //观察LiveData
        model.currentName.observe(this, Observer { t -> tvViewModelText.text = t })
    }

    fun onClick(view: View) {
        model.currentName.value = "Hello world clicked"
        normalText = "Hello world clicked"
        tvNormalText.text = normalText
    }
}
  1. 开始我们将普通对象normalText和ViewModel中的LiveData持有的字符串都赋值为 Hello world
  2. 点击按钮将两个值都赋值为Hello world clicked
  3. 旋转屏幕Activity重建,普通对象normalText被重新初始化为Hello world。ViewModel中的LiveData持有的字符串还是Hello world clicked
ViewModel配置变化.gif

结论:普通对象在Activity由于配置信息发生变化而重新创建的时候,会被重新初始化。ViewModel对象则会保存下来,不会因为Activity重建而重新初始化。

接下来开始分析其中的原理

//获取ViewModel对象
model = ViewModelProvider(this).get(NameViewModel::class.java)

ViewModelProvider的两个构造函数。

public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
    //注释1处,调用两个参数的构造函数
    this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
            ? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
            : NewInstanceFactory.getInstance());
}

public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
    //注释2处,Factory用来创建ViewModels
    mFactory = factory;
    mViewModelStore = store;
}

注释1处,AppCompatActivity的间接父类ComponentActivity实现了ViewModelStoreOwner接口。

public class ComponentActivity extends androidx.core.app.ComponentActivity implements
    LifecycleOwner,
    ViewModelStoreOwner,
    SavedStateRegistryOwner,
    OnBackPressedDispatcherOwner {
        
    private ViewModelStore mViewModelStore;
        
    @NonNull
    @Override
    public ViewModelStore getViewModelStore() {
        if (mViewModelStore == null) {
            NonConfigurationInstances nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) {
                // 从NonConfigurationInstances中恢复ViewModelStore 
                mViewModelStore = nc.viewModelStore;
            }
            if (mViewModelStore == null) {
                mViewModelStore = new ViewModelStore();
            }
        }
        return mViewModelStore;
    }
        
    //...
    
}

这里我们先简单认为ComponentActivity的getViewModelStore()方法,返回了一个ViewModelStore对象。后面还会仔细分析这一部分。

ViewModelProvider的构造函数的注释1处,AppCompatActivity及其父类都没有实现HasDefaultViewModelProviderFactory接口。所以注释2处传入的Factory默认是NewInstanceFactory.ViewModelProvider的一个单例类。

接下来我们看下ViewModelProvider的get方法


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

@MainThread
public  T get(@NonNull Class modelClass) {
    //类的全限定类型
    String canonicalName = modelClass.getCanonicalName();
    if (canonicalName == null) {
        throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");
    }
    //根据类名获取ViewModel对象
    return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
}
@MainThread
public  T get(@NonNull String key, @NonNull Class modelClass) {
     //注释1处,从ViewModelStore中获取
     ViewModel viewModel = mViewModelStore.get(key);

     if (modelClass.isInstance(viewModel)) {//类型匹配
         if (mFactory instanceof OnRequeryFactory) {
             ((OnRequeryFactory) mFactory).onRequery(viewModel);
         }
         //直接返回
         return (T) viewModel;
     } else {
         //类型不匹配,丢弃viewModel
         if (viewModel != null) {
             // TODO: log a warning.
         }
     }
     //注释2处,根据类对象创建ViewModel对象
     if (mFactory instanceof KeyedFactory) {
         viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
     } else {
         viewModel = (mFactory).create(modelClass);
     }
     //加入缓存
     mViewModelStore.put(key, viewModel);
     //返回创建的对象
     return (T) viewModel;
}

逻辑也很简单,注释1处根据传入的modelClass对象的类名称先从ViewModelStore中获取对象。

ViewModelStore类:就是一个存放ViewModel的HashMap。

public class ViewModelStore {

    private final HashMap 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 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();
    }
}

如果获取不到,就通过反射创建ViewModel对象并加入到ViewModelStore中来保存。

NewInstanceFactory的create方法,通过反射创建ViewModel对象。

@NonNull
@Override
public  T create(Class 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);
    }
}

到现在,铺垫的差不多了,该进入正题了。我们回过头来看ComponentActivity的getViewModelStore()方法。

核心原理

@NonNull
@Override
public ViewModelStore getViewModelStore() {
    if (mViewModelStore == null) {
        //注释1处
        NonConfigurationInstances nc = (NonConfigurationInstances) 
                             getLastNonConfigurationInstance();
        if (nc != null) {
            //从NonConfigurationInstances中恢复ViewModelStore
            mViewModelStore = nc.viewModelStore;
        }
        //注释2处,如果mViewModelStore依然为null,则创建新的ViewModelStore对象。
        if (mViewModelStore == null) {
            mViewModelStore = new ViewModelStore();
        }
    }
    return mViewModelStore;
}

注释1处,调用Activity的getLastNonConfigurationInstance方法。

/**
 * 获取先前由{@link#onRetainNonConfigurationInstance()}方法返回的和配置信息无关
 * (non-configuration)的实例数据。
 * 当新的Activity的实例的{@link#onCreate}和{@link#onStart}方法被调用的时候,
 * 可以调用这个方法来获取前一个被销毁的Activity通过{@link#onRetainNonConfigurationInstance()}方法
 * 保存的一些数据。
 **/
@Nullable
public Object getLastNonConfigurationInstance() {
    return mLastNonConfigurationInstances != null
            ? mLastNonConfigurationInstances.activity : null;
}

如果mLastNonConfigurationInstances不为null,返回mLastNonConfigurationInstancesactivity字段,否则返回null。

我们先看一下Activity.NonConfigurationInstances类

static final class NonConfigurationInstances {
    //这个activity字段是Object类型
    Object activity;
    HashMap children;
    FragmentManagerNonConfig fragments;
    ArrayMap loaders;
    VoiceInteractor voiceInteractor;
}

我们知道当Activity配置信息发生变化的时候,Activity会销毁然后重新创建,Activity.NonConfigurationInstances类就是用来在Activity销毁的时候用来保存一些有用的数据的。和Bundle相比,Activity.NonConfigurationInstances几乎可以保存任意对象(比如Bitmap)。

接下来,我们看一下Activity.NonConfigurationInstances对象是怎么被保存的。

当Activity因配置信息发生变化销毁然后重新创建,调用ActivityThread的handleRelaunchActivity方法。

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

    //...
     handleRelaunchActivityInner(r, configChanges, tmp.pendingResults, tmp.pendingIntents,
                    pendingActions, tmp.startsNotResumed, tmp.overrideConfig, "handleRelaunchActivity");
    //...
}

ActivityThread的handleRelaunchActivityInner方法。

private void handleRelaunchActivityInner(ActivityClientRecord r, int configChanges,
            List pendingResults, List pendingIntents,
            PendingTransactionActions pendingActions, boolean startsNotResumed,
            Configuration overrideConfig, String reason) {
    //...
    //注释1处
    handleDestroyActivity(r.token, false, configChanges, true, reason);

   //...
}

注意:注释1处,传入的倒数第二个参数为true。

ActivityThread的handleDestroyActivity方法。

@Override
public void handleDestroyActivity(IBinder token, boolean finishing, int configChanges,
        boolean getNonConfigInstance, String reason) {

    ActivityClientRecord r = performDestroyActivity(token, finishing,
                    configChanges, getNonConfigInstance, reason);
    //...

}

ActivityThread的performDestroyActivity方法。

ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,
            int configChanges, boolean getNonConfigInstance, String reason) {

    ActivityClientRecord r = mActivities.get(token);

    if (getNonConfigInstance) {
        try {
            //注释1处
            r.lastNonConfigurationInstances = r.activity.retainNonConfigurationInstances();
        } catch (Exception e) {
            //...
        }
   }

}

注释1处,getNonConfigInstance为true,会调用Activity的retainNonConfigurationInstances方法。

NonConfigurationInstances retainNonConfigurationInstances() {
    //注释1处
    Object activity = onRetainNonConfigurationInstance();
    HashMap children = onRetainNonConfigurationChildInstances();
    FragmentManagerNonConfig fragments = mFragments.retainNestedNonConfig();

    // We're already stopped but we've been asked to retain.
    // Our fragments are taken care of but we need to mark the loaders for retention.
    // In order to do this correctly we need to restart the loaders first before
    // handing them off to the next activity.
    mFragments.doLoaderStart();
    mFragments.doLoaderStop(true);
    ArrayMap loaders = mFragments.retainLoaderNonConfig();

    if (activity == null && children == null && fragments == null && loaders == null
            && mVoiceInteractor == null) {
        return null;
    }
    //注释2处,正常返回一个Activity.NonConfigurationInstances对象
    NonConfigurationInstances nci = new NonConfigurationInstances();
    nci.activity = activity;
    nci.children = children;
    nci.fragments = fragments;
    nci.loaders = loaders;
    if (mVoiceInteractor != null) {
        mVoiceInteractor.retainInstance();
        nci.voiceInteractor = mVoiceInteractor;
    }
    return nci;
}

注释1处,获取Activity.NonConfigurationInstances的activity字段要保存的信息。

ComponentActivity重写了Activity的onRetainNonConfigurationInstance方法。

@Override
@Nullable
public final Object onRetainNonConfigurationInstance() {

    Object custom = onRetainCustomNonConfigurationInstance();
    //获取当前ViewModelStore对象
    ViewModelStore viewModelStore = mViewModelStore;
    if (viewModelStore == null) {
        // viewModelStore为null,说明当前Activity实例没有调用过getViewModelStore()
        //来创建ViewModelStore对象,所以检查前一个Activity销毁的时候是否在
        //NonConfigurationInstances中保存有ViewModelStore对象。
        NonConfigurationInstances nc =
                (NonConfigurationInstances) getLastNonConfigurationInstance();
        if (nc != null) {
            viewModelStore = nc.viewModelStore;
        }
    }
    //如果没有需要保存的,就返回null
    if (viewModelStore == null && custom == null) {
        return null;
    }
    //注释1处,正常返回一个ComponentActivity.NonConfigurationInstances对象
    NonConfigurationInstances nci = new NonConfigurationInstances();
    nci.custom = custom;
    nci.viewModelStore = viewModelStore;
    return nci;
}

注释1处,正常返回一个ComponentActivity.NonConfigurationInstances对象。注意和Activity的Activity.NonConfigurationInstances不一样。

ComponentActivity.NonConfigurationInstances类

static final class NonConfigurationInstances {
    Object custom;
    //用来保存创建的ViewModelStore
    ViewModelStore viewModelStore;
}

我们回到ActivityThread的performDestroyActivity方法。最终是将一个Activity.NonConfigurationInstances对象保存到了ActivityClientRecord中。

然后我们看是怎么保存的数据是如何恢复的。

当重新创建Activity的时候,ActivityThread会调用performLaunchActivity方法。

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    //...
    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);
   //...
}

调用了Activity的attach方法。并传入了我们在销毁上一个Activity的时候保存在ActivityClientRecord中的lastNonConfigurationInstances对象。

Activity的attach方法

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

    attachBaseContext(context);
    //...
    //为当前Activity实例的mLastNonConfigurationInstances赋值。
    mLastNonConfigurationInstances = lastNonConfigurationInstances;

}

Activity的attach方法在Activity的onCreate方法之前被调用。在Activity的attach方法内部为当前Activity实例的mLastNonConfigurationInstances赋值。

重新创建的ViewModelActivity的onCreate方法中,我们创建ViewModelProvider会调用ComponentActivity的getViewModelStore方法。

model = ViewModelProvider(this).get(NameViewModel::class.java)

@NonNull
@Override
public ViewModelStore getViewModelStore() {
        
    if (mViewModelStore == null) {
        //注释1处
        NonConfigurationInstances nc =(NonConfigurationInstances) 
                getLastNonConfigurationInstance();
        if (nc != null) {
            //从NonConfigurationInstances中恢复ViewModelStore
            mViewModelStore = nc.viewModelStore;
        }
        //注释2处,如果mViewModelStore依然为null,则创建新的ViewModelStore对象。
        if (mViewModelStore == null) {
            mViewModelStore = new ViewModelStore();
        }
    }
    return mViewModelStore;
}

这个时候,注释1处会从Activity的mLastNonConfigurationInstances.activity获取销毁的Activity保存下来的mViewModelStore对象。

然后ViewModelProvider的get方法,也就是从mViewModelStore中获取ViewModel对象。

@MainThread
public  T get(@NonNull String key, @NonNull Class modelClass) {
    //注释1处,mViewModelStore中获取
    ViewModel viewModel = mViewModelStore.get(key);

    if (modelClass.isInstance(viewModel)) {//类型匹配
        if (mFactory instanceof OnRequeryFactory) {
            ((OnRequeryFactory) mFactory).onRequery(viewModel);
        }
        //直接返回
        return (T) viewModel;
    } else {
        //类型不匹配,丢弃缓存中的viewModel
        if (viewModel != null) {
            // TODO: log a warning.
        }
    }
    //注释2处,根据类对象创建ViewModel对象
    if (mFactory instanceof KeyedFactory) {
        viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
    } else {
        viewModel = (mFactory).create(modelClass);
    }
    //加入缓存
    mViewModelStore.put(key, viewModel);
    //返回创建的对象
    return (T) viewModel;
}

注释1处,这个时候获取的ViewModel对象不为空,所以不会重新创建ViewModel对象。

总结一下:

  1. 当Activity由于配置信息发生改变而重建的时候,会保存一个Activity.NonConfigurationInstances对象。

  2. AppCompatActivity的间接父类ComponentActivity在此过程中保存了一个ComponentActivity.NonConfigurationInstancesActivity.NonConfigurationInstances中。ComponentActivity.NonConfigurationInstances中保存了AppCompatActivity当前的ViewModelStore对象。ViewModelStore对象中存储着当前AppCompatActivity的所有ViewModel对象。

  3. 在新创建的AppCompatActivity的attach方法中恢复了ViewModelStore对象。该方法在onCreate方法之前被调用。

  4. 在新创建的AppCompatActivity的onCreate方法中可以获取到恢复了的ViewModelStore对象。从而可以获取对应的ViewModel对象。

其他

为啥Activity正常销毁的时候不保存Activity.NonConfigurationInstances对象?

public class DestroyActivityItem extends ActivityLifecycleItem {

    private boolean mFinished;
    private int mConfigChanges;

    @Override
    public void execute(ClientTransactionHandler client, IBinder token,
            PendingTransactionActions pendingActions) {
        Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityDestroy");

        //注释1处,这里的getNonConfigInstance为fasle
        client.handleDestroyActivity(token, mFinished, mConfigChanges,
                false /* getNonConfigInstance */, "DestroyActivityItem");
        Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
    }
}

当Activity正常结束,不需要重新创建的时候,的时候会执行DestroyActivityItem,调用ActivityThread的handleDestroyActivity方法的时候传入的getNonConfigInstance参数为false,不会保存Activity.NonConfigurationInstances对象。

ViewModelStore何时清空保存的ViewModel

ComponentActivity的构造函数

public ComponentActivity() {
    Lifecycle lifecycle = getLifecycle();
        
    getLifecycle().addObserver(new LifecycleEventObserver() {
        @Override
        public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
            //注释1处
            if (event == Lifecycle.Event.ON_DESTROY) {
                if (!isChangingConfigurations()) {
                    getViewModelStore().clear();
                }
            }
        }
    });
    //...
}

ComponentActivity监听了声明周期变化,当ComponentActivity销毁的时候,如果不是因为配置发生变化而销毁的话,ViewModelStore就清空保存的ViewModel对象。

参考链接:

  • Android JetPack ViewModel源码分析
  • ViewModel和LiveData 使用

你可能感兴趣的:(ViewModel原理分析)