Jetpack系列-ViewModel使用和源码分析

1 简介和简单使用

1.1 简介

ViewModel以注重生命周期的方式存储和管理界面相关的数据。ViewModel类让数据可在发生屏幕旋转等配置更改后继续留存,保证数据的安全持久性。

如果Activity/Fragment销毁或重新创建界面,则存储在其中的任何瞬态界面相关数据都会丢失。对于简单的数据,Activity可以使用onSaveInstanceState()方法从onCreate()中的捆绑包恢复其数据,但此方法仅适合可以序列化再反序列化的少量数据,而不适合数量可能较大的数据。

另一个问题是,Activity/Fragment经常需要进行可能需要一些时间才能返回的异步调用。Activity/Fragment需要管理这些调用,并确保系统在其销毁后清理这些调用以避免潜在的内存泄漏。此项管理需要大量的维护工作,并且在重新创建对象的情况下,会造成资源的浪费,因为对象可能需要重新发出已经发出过的调用。

ViewModelActiivity重建时会自动保存,可以确保数据不会丢失,并且ViewModel的生命周期比较长,ViewModel对象存在的时间范围是获取ViewModel时传递给ViewModelProviderLifecycleViewModel将一直留在内存中,直到限定其存在时间范围的Lifecycle永久消失:对于Activity,是在Activity完成时;而对于Fragment,是在Fragment分离时。

ViewModel的生命周期:

Google官方文档:https://developer.android.google.cn/topic/libraries/architecture/viewmodel?hl=zh_cn

1.2 使用

创建RandomVideModel来保存数据,继承RandomVideModel

class RandomVideModel : ViewModel() {

    var num: Int = 0

}

Activity中实例化RandomVideModel,并从中获取数据。

class VideModelActivity : AppCompatActivity() {

    private lateinit var randomVideModel: RandomVideModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_vide_model)

        randomVideModel = ViewModelProvider(
            this,
            ViewModelProvider.NewInstanceFactory()
        ).get(RandomVideModel::class.java)

        tv_content.text = "${randomVideModel.num}"

        btn_random.setOnClickListener {
            tv_content.text = "${++randomVideModel.num}"
        }
    }
}

2 源码分析

首先看下ViewModel的源码,ViewModel代码很简单,内部用一个HashMap存储数据。

public abstract class ViewModel {
    // Can't use ConcurrentHashMap, because it can lose values on old apis (see b/37042460)
    @Nullable
    private final Map mBagOfTags = new HashMap<>();
    ...
}

ViewModel有3个子类,AndroidViewModelFragmentManagerViewModelLoaderViewModel

AndroidViewModel内部持有一个Application,可以方便使用上下文。

可以看到,是业务层ActivityViewModel不是直接new出来的,而是通过实例化一个ViewModelProvider对象,然后调用ViewModelProvider的get方法,传入了ViewModel的class文件,所以,ViewModel是通过反射创建出来的。

ViewModelProvider的构造方法,传入两个参数,一个是ViewModelStoreOwner,一个是FactoryAppCompatActivity继承的是ComponentActivityComponentActivity实现了ViewModelStoreOwner接口,所以这里传this就可以。另一个参数传Factory的实现类NewInstanceFactory

public constructor(owner: ViewModelStoreOwner, factory: Factory) : this(
    owner.viewModelStore,
    factory
)

接着通过this调用了构造方法ViewModelProvider(ViewModelStore,Factory)ViewModelStore就是存储ViewModel的类,里边是一个HashMap,提供了putgetclear方法。

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

    //遍历HashMap,清除所有数据
    public final void clear() {
        for (ViewModel vm : mMap.values()) {
            vm.clear();
        }
        mMap.clear();
    }
}

回到业务层Activity中,调用ViewModelProviderget方法传入了自定义ViewModel的class。

@MainThread
public open operator fun  get(modelClass: Class): T {
    val canonicalName = modelClass.canonicalName
    ?: throw IllegalArgumentException("Local and anonymous classes can not be ViewModels")
    return get("$DEFAULT_KEY:$canonicalName", modelClass)
}

接着调用get(key: String, modelClass: Class)。在get方法中,创建了ViewModel对象,然后将ViewModel存放到ViewModelStore中。

@Suppress("UNCHECKED_CAST")
@MainThread
public open operator fun  get(key: String, modelClass: Class): T {
    var viewModel = store[key]
    if (modelClass.isInstance(viewModel)) {
        (factory as? OnRequeryFactory)?.onRequery(viewModel)
        return viewModel as T
    } else {
        @Suppress("ControlFlowWithEmptyBody")
        if (viewModel != null) {
            // TODO: log a warning.
        }
    }
    viewModel = if (factory is KeyedFactory) {
        factory.create(key, modelClass)
    } else {
        //由于传进来的是NewInstanceFactory,会走到这里。
        factory.create(modelClass)
    }
    //将ViewModel存放到ViewModelStore中。
    store.put(key, viewModel)
    return viewModel
}

由于传入的是NewInstanceFactory,所以最终调用的是Factory接口中的create(modelClass: Class)

public interface Factory {
    public fun  create(modelClass: Class): T
}

而具体实现在NewInstanceFactory中,NewInstanceFactory中的create方法中最终通过反射创建了ViewModel对象。

@Override
public  T create(@NonNull Class modelClass) {
    //noinspection TryWithIdenticalCatches
    try {
        //通过反射创建ViewModel对象。
        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中,上面说过,ComponentActivity实现了ViewModelStoreOwner接口。

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

ViewModelStoreOwner中有一个接口方法getViewModelStoreComponentActivity中实现了该方法。

@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去拿到ViewModelStore

void ensureViewModelStore() {
    if (mViewModelStore == null) {
        //这里的NonConfigurationInstances是ComponentActivity中的静态内部类。
        NonConfigurationInstances nc =
            (NonConfigurationInstances) getLastNonConfigurationInstance();
        if (nc != null) {
            // Restore the ViewModelStore from NonConfigurationInstances
            mViewModelStore = nc.viewModelStore;
        }
        if (mViewModelStore == null) {
            mViewModelStore = new ViewModelStore();
        }
    }
}

ensureViewModelStore方法中实例化了一个NonConfigurationInstances,调用了getLastNonConfigurationInstance,从NonConfigurationInstances中拿ViewModelStore

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

而这个方法中又调用了mLastNonConfigurationInstances.activity,点进去跳转到了Activity类中的静态内部类NonConfigurationInstances

static final class NonConfigurationInstances {
    Object activity;
    HashMap children;
    FragmentManagerNonConfig fragments;
    ArrayMap loaders;
    VoiceInteractor voiceInteractor;
}

这里的activityretainNonConfigurationInstances方法中被赋值。

NonConfigurationInstances retainNonConfigurationInstances() {
    //调用onRetainNonConfigurationInstance获取activity
    Object activity = onRetainNonConfigurationInstance();
    ...
    NonConfigurationInstances nci = new NonConfigurationInstances();
    //赋值给NonConfigurationInstances中的activity
    nci.activity = activity;
    ...
    return nci;
}

接着来到了Activity类中的onRetainNonConfigurationInstance,该方法的具体实现在ComponentActivity类中。

Activity中的onRetainNonConfigurationInstance

public Object onRetainNonConfigurationInstance() {
    return null;
}

ComponentActivity中的onRetainNonConfigurationInstance

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

到这里,业务层Activity就拿到了ViewModelStore,就可以操作ViewModel中的数据了。

而如何保证数据的稳健性不会丢失,可以看ComponentActivity的构造方法。在ComponentActivity无参构造方法中有如下代码:

public ComponentActivity() {
    Lifecycle lifecycle = getLifecycle();
    ...
    getLifecycle().addObserver(new LifecycleEventObserver() {
        @Override
        public void onStateChanged(@NonNull LifecycleOwner source,
                                   @NonNull Lifecycle.Event event) {
            if (event == Lifecycle.Event.ON_DESTROY) {
                // Clear out the available context
                mContextAwareHelper.clearAvailableContext();
                // And clear the ViewModelStore
                //如果配置了
                if (!isChangingConfigurations()) {
                    getViewModelStore().clear();
                }
            }
        }
    });
 }

这里对Lifecycle状态进行了监听,当状态处于ON_DESTROY,也就是页面销毁时,先判断isChangingConfigurations是否为true,为true的话,就不用调用getViewModelStore().clear(),这样就保证了ViewModel中数据的持久稳健性。

public boolean isChangingConfigurations() {
    return mChangingConfigurations;
}

mChangingConfigurations的值,默认是false,但是在因为Configuration的改变被销毁了又重建的时候(最常见的就是横竖屏切换),mChangingConfigurations的值为true,ViewModelStore不调用clear,数据不会清除。这就保证了Activity销毁重建的时候数据不会丢失,还可以继续正常使用。

/** true if the activity is being destroyed in order to recreate it with a new configuration */
/*package*/ boolean mChangingConfigurations = false;

4 流程图

阅读全文
下载说明:
1、本站所有资源均从互联网上收集整理而来,仅供学习交流之用,因此不包含技术服务请大家谅解!
2、本站不提供任何实质性的付费和支付资源,所有需要积分下载的资源均为网站运营赞助费用或者线下劳务费用!
3、本站所有资源仅用于学习及研究使用,您必须在下载后的24小时内删除所下载资源,切勿用于商业用途,否则由此引发的法律纠纷及连带责任本站和发布者概不承担!
4、本站站内提供的所有可下载资源,本站保证未做任何负面改动(不包含修复bug和完善功能等正面优化或二次开发),但本站不保证资源的准确性、安全性和完整性,用户下载后自行斟酌,我们以交流学习为目的,并不是所有的源码都100%无错或无bug!如有链接无法下载、失效或广告,请联系客服处理!
5、本站资源除标明原创外均来自网络整理,版权归原作者或本站特约原创作者所有,如侵犯到您的合法权益,请立即告知本站,本站将及时予与删除并致以最深的歉意!
6、如果您也有好的资源或教程,您可以投稿发布,成功分享后有站币奖励和额外收入!
7、如果您喜欢该资源,请支持官方正版资源,以得到更好的正版服务!
8、请您认真阅读上述内容,注册本站用户或下载本站资源即您同意上述内容!
原文链接:https://www.dandroid.cn/archives/19442,转载请注明出处。
0

评论0

显示验证码
没有账号?注册  忘记密码?