ViewModel与viewModelScope

ViewModel的生命周期,以往我们将UI展示的数据直接缓存在对应的UI组件中,遇到ConfigurationChange等事件UI组件重新创建,我们缓存的数据也随之销毁。但ViewModel可以在内存中长期被持有而不受ConfigurationChange的影响,直到相关联的UI组件真正销毁的时候ViewModel才随之释放。

实例的获取
myViewModel = new ViewModelProvider(this).get(MyViewModel.class);
//myViewModel = ViewModelProviders.of(this).get(MyViewModel.class);

第二种已经舍弃。

首先来看ViewModelProvider的构造函数。

 public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
        this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
                ? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
                : NewInstanceFactory.getInstance());
    }

ViewModelProvider:用于创建 ViewModel,其构造方法有两个参数,第一个参数传入 ViewModelStoreOwner ,确定了 ViewModelStore 的作用域,第二个参数为 ViewModelProvider.Factory,用于初始化 ViewModel 对象,默认为 getDefaultViewModelProviderFactory() 方法获取的 factory

ViewModelStoreOwner是一个接口,其实现类是FragmentActivity和Fragment.先看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) {
            NonConfigurationInstances nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) {
                // Restore the ViewModelStore from NonConfigurationInstances
                mViewModelStore = nc.viewModelStore;
            }
            if (mViewModelStore == null) {
                mViewModelStore = new ViewModelStore();
            }
        }
        return mViewModelStore;
    }

从上面的方法可以看到,mViewModelStore是通过NonConfigurationInstances得到。而NonConfigurationInstances的实例是通过getLastNonConfigurationInstance()获得。

getLastNonConfigurationInstance()是Activity中的一个方法。

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

也就是说要知道mViewModelStore怎么来的,只需要知道mLastNonConfigurationInstances什么时候赋值的即可。

全局搜索可以知道在attach()中进行赋值。而attach()在ActivityThread的performLaunchActivity()进行调用。

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
        ActivityInfo aInfo = r.activityInfo;
        
        ComponentName component = r.intent.getComponent();
        if (component == null) {
            component = r.intent.resolveActivity(
                mInitialApplication.getPackageManager());
            r.intent.setComponent(component);
        }

        if (r.activityInfo.targetActivity != null) {
            component = new ComponentName(r.activityInfo.packageName,
                    r.activityInfo.targetActivity);
        }

        ContextImpl appContext = createBaseContextForActivity(r);
        Activity activity = null;
        try {
            java.lang.ClassLoader cl = appContext.getClassLoader();
            activity = mInstrumentation.newActivity(
                    cl, component.getClassName(), r.intent);
        } catch (Exception e) {
            
        }

        try {
            Application app = r.packageInfo.makeApplication(false, mInstrumentation);

            if (activity != null) {
                CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
                Configuration config = new Configuration(mCompatConfiguration);
                if (r.overrideConfig != null) {
                    config.updateFrom(r.overrideConfig);
                }
                
                appContext.setOuterContext(activity);
                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);//1

                if (customIntent != null) {
                    activity.mIntent = customIntent;
                }
                r.lastNonConfigurationInstances = null;//2
                checkAndBlockForNetworkAccess();
                activity.mStartedActivity = false;
                int theme = r.activityInfo.getThemeResource();
                if (theme != 0) {
                    activity.setTheme(theme);
                }

                r.activity = activity;
            }
            r.setState(ON_CREATE);
            synchronized (mResourcesManager) {
                mActivities.put(r.token, r);
            }

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

从注释1可知,在attach方法中将lastNonConfigurationInstances赋值过去,所以整个流程为,handleRelaunchActivity()->handleRelaunchActivityInner()->handleDestroyActivity()->performDestroyActivity()->handleLaunchActivity()->performLaunchActivity()->activity.attach()。

在handleDestroyActivity中,getNonConfigInstance参数为true,则在performDestroyActivity,

if (getNonConfigInstance) {
                try {
                    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);
                    }
                }
            }

会将lastNonConfigurationInstances进行赋值保存, 这样在activity 重建时 mLastNonConfigurationInstances 能够得到上一次的值,使得 ViewModelStore 值不变 。

然后调用get()去获得ViewModel实例。

public  T get(@NonNull Class 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);
    }
    
public  T get(@NonNull String key, @NonNull Class modelClass) {
        ViewModel viewModel = mViewModelStore.get(key);

        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.
            }
        }
        if (mFactory instanceof KeyedFactory) {
            viewModel = ((KeyedFactory) mFactory).create(key, modelClass);
        } else {
            viewModel = mFactory.create(modelClass);
        }
        mViewModelStore.put(key, viewModel);
        return (T) viewModel;
    }

根据Factory去创建ViewModel,创建成功后并保存在mViewModelStore中。

ViewModelStoreOwner 代表着作用域,其内部唯一的方法返回 ViewModelStore 对象,也即不同的作用域对应不同的 ViewModelStore ,而 ViewModelStore 内部维护着 ViewModel 的 HashMap ,因此只要保证相同作用域的 ViewModelStore 对象相同就能保证相同作用域获取到相同的 ViewModel 对象。

由于 ViewModel 的设计,使得 activity/fragment 依赖它,而 ViewModel 不依赖 activity/fragment。因此只要不让 ViewModel 持有 context 或 view 的引用,就不会造成内存泄漏。

viewModelScope

在ViewModel中使用的协程。 它是ViewModel的扩展属性。

自动取消,不会造成内存泄漏,如果是CoroutineScope,就需要在onCleared()方法中手动取消了,否则可能会造成内存泄漏。

配合ViewModel,能减少样板代码,提高效率。

viewModelScope的使用
fun getBannerData(){
        viewModelScope.launch (Dispatchers.IO){
            //repo.getBanner(bannerLiveData)
        }
    }

进入viewModelScope的源码

public val ViewModel.viewModelScope: CoroutineScope
    get() {
        val scope: CoroutineScope? = this.getTag(JOB_KEY)
        if (scope != null) {
            return scope
        }
        return setTagIfAbsent(
            JOB_KEY,
            CloseableCoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
        )
    }

internal class CloseableCoroutineScope(context: CoroutineContext) : Closeable, CoroutineScope {
    override val coroutineContext: CoroutineContext = context

    override fun close() {
        coroutineContext.cancel()
    }
}

在get()中,通过setTagIfAbsent创建了协程,并且指定主线程。CloseableCoroutineScope实现Closeable接口,并重写唯一方法close(),并在方法中取消了协程。

 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) {
            // It is possible that we'll call close() multiple times on the same object, but
            // Closeable interface requires close method to be idempotent:
            // "if the stream is already closed then invoking this method has no effect." (c)
            closeWithRuntimeException(result);
        }
        return result;
    }

在setTagIfAbsent中,以HashMap的形式把协程对象保存起来了,并配有getTag方法。并且调用了closeWithRuntimeException(),这个方法中调用了Closeable接口的close()方法,而close()方法就是用来取消协程的。

而closeWithRuntimeException方法是谁调用的呢,主要是ViewModel中的clear()方法。

    @MainThread
    final void clear() {
        mCleared = true;
        if (mBagOfTags != null) {
            synchronized (mBagOfTags) {
                for (Object value : mBagOfTags.values()) {
                    closeWithRuntimeException(value);
                }
            }
        }
        onCleared();
    }

这里是循环保存协程的HashMap,然后调用closeWithRuntimeException取消协程。

那这个ViewModel中的clear()方法又是谁调用的呢?

查看源码,只有一处调用,就是在ViewModelStore中

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

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

在上面分析ViewModel的时候,我们知道在创建ViewModel,创建成功后并保存在mViewModelStore中。

回过头来再看ViewModelStore,同样也有一个clear()方法,同样循环调用vm.clear()。而这个是在ComponentActivity.java中调用的:

getLifecycle().addObserver(new LifecycleEventObserver() {
    @Override
    public void onStateChanged(@NonNull LifecycleOwner source,
            @NonNull Lifecycle.Event event) {
        if (event == Lifecycle.Event.ON_DESTROY) {
            if (!isChangingConfigurations()) {
                getViewModelStore().clear();
            }
        }
    }
});

先是获取Lifecycle,并添加生命周期监听。

在生命周期为onDestroy的时候,获取ViewModelStore,并调用其clear()方法。

因此前面说的,ViewModel和ViewModelScope不会造成内存泄露的原因就是这个。

你可能感兴趣的:(ViewModel与viewModelScope)