学习android的同学应该都了解Activity,关于Activity的相关介绍也有很多,下面简要罗列出Activity的生命周期:

android activity跳转生命周期 activity跳转的生命周期_ide

 上图列出了Activity在不同情况下的生命周期变化,内容来自Android开发艺术探索。

补充:

1. Activity之间的跳转,比如A->B,生命周期的变化情况:

A: onPause -> B: onCreate -> B: onStart -> B: onResume -> A: onStop

由于A的onPause调用之后会创建B Activity,所以不适合在onPause中进行一些耗时操作,可以在onStop中进行操作。

2. Activity在资源配置发生改变以及被系统回收情况下的生命周期变化:

当Activity在异常情况下被销毁时,其生命周期变化会略有不同,最常见的就是屏幕翻转导致Activity销毁重建,此时有些数据便会丢失,但某些数据不会,这是因为Activity在异常情况下被销毁时,会调用onSaveInstanceState方法来保存一些状态,也会调用View的onSaveInstanceState来保存View的一些状态,而在恢复时会将保存的数据传递给onCreate和onRestoreInstanceState中的savedInstanceState,注意状态保存方法只有在Activity在异常销毁情况下才会调用,所以正常情况下是不能通过该方法来保存数据。onRestoreInstanceState一定会在onPostCreate之前调用,并在onStart之后调用,所以注意状态恢复后的UI初始化。如果是由于资源配置的变化引起的销毁重建,还会调用onConfigurationChanged方法,因此可在此方法中判断资源发生了什么变化。

3. ViewModel为何能在屏幕翻转Activity销毁后保存状态

当屏幕翻转时,Activity会调用onDestroy进行销毁,如果是正常情况下,ViewModel也应该被销毁,但是ViewModel却能够保存其状态,其本质是因为当Activity因资源配置变化重建时,ViewModel还是原来的ViewModel,并没有被销毁,接下来具体分析:

首先我们知道Activity实现了ViewModelStoreOwner接口,而从ViewModelStoreOwner中可以获得ViewModelStore,ViewModelStore内部存在一个HashMap,用来保存所创建的ViewModel。

创建ViewModel方式:

/**
* this表示Activity
*/
viewModel = ViewModelProvider(this)[ViewModel::class.java]

ViewModel的创建需要ViewModelStoreOwner,而Activity的父类继承了ViewModelStoreOwner接口,所以可以直接传入this,并且ViewModelProvider持有ViewModelStoreOwner的ViewModelStore,从以下代码可以验证:

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

    public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) {
        this(owner.getViewModelStore(), factory);
    }

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

由于ViewModelProvider持有ViewModelStore,所以便可以创建新的ViewModel并保存到ViewModelStore的HashMap中,或者从中取出ViewModel,在创建ViewModel时,便是调用ViewModelStore的get方法来获取ViewModel,如果不存在,则创建:

public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> 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;
    }

到此,便产生了所需要的ViewModel。

当Activity销毁时:

由于Activity实现了LifycycleOwner接口,所以当Activity生命周期有所变化时,便会通知到所有注册的观察者:

public ComponentActivity() {
        Lifecycle lifecycle = getLifecycle();
        //noinspection ConstantConditions
        if (lifecycle == null) {
            throw new IllegalStateException("getLifecycle() returned null in ComponentActivity's "
                    + "constructor. Please make sure you are lazily constructing your Lifecycle "
                    + "in the first call to getLifecycle() rather than relying on field "
                    + "initialization.");
        }
        if (Build.VERSION.SDK_INT >= 19) {
            getLifecycle().addObserver(new LifecycleEventObserver() {
                @Override
                public void onStateChanged(@NonNull LifecycleOwner source,
                        @NonNull Lifecycle.Event event) {
                    if (event == Lifecycle.Event.ON_STOP) {
                        Window window = getWindow();
                        final View decor = window != null ? window.peekDecorView() : null;
                        if (decor != null) {
                            decor.cancelPendingInputEvents();
                        }
                    }
                }
            });
        }
        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();
                    }
                }
            }
        });

        if (19 <= SDK_INT && SDK_INT <= 23) {
            getLifecycle().addObserver(new ImmLeaksCleaner(this));
        }
    }

由上面代码可知,当Activity的生命周期状态为ON_DESTROY时,会首先判断是不是配置发生了改变,如果不是,则清空ViewModelStore中的HashMap。所以如果因为配置变化的原因导致Activity销毁,其ViewModel并没有销毁。那如果没有销毁,当Activity重新创建时怎么找到它的呢?

其实当Activity因为配置改变而需要重现创建时,Activity还会调用onRetainNonConfigurationInstance方法,该方法在onStop和onDestroy之间调用,并且接下来要立即创建新的Activity实例,就是在这里将ViewModelStore保存了起来:

public final Object onRetainNonConfigurationInstance() {
        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;
    }

既然保存起来了,那我们怎么获取它的呢,其实答案就是在创建 ViewModel时来获取的,还记得在创建ViewModel时,ViewModelProvider持有了ViewModelStore,而这个ViewModelStore是从ViewModelStoreOwner的getViewModelStore方法得来的,所以肯定是Activity在重写此方法中做了处理:

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

可以看到Activity首先是调用getlastNonConfigurationInstance方法来获取ViewModelStore,而该方法就是取出之前onRetainNonConfigurationInstance方法所保存的数据,所以就可以成功的找到了之前所创建的ViewModel,也就可以恢复之前的状态了。

总结:

ViewModel之所以能够屏幕翻转时保存状态,是因为在销毁之前保存了ViewModel(onRetainNonConfigurationInstance),并且在重新创建时取出之前的ViewModel(getViewModelStore)。onRetainNonConfigurationInstance方法只有在因为资源配置发生变化,Activity需要销毁重建时才会调用,所以如果是因为内存不足而销毁,则不会调用此方法,也就不能通过ViewModel的方式恢复之前的状态,此时可以调用onSaveInstanceState方法来进行恢复,所以调用onRetainnonConfigurationinstance方法的条件要比onSaveInstanceState苛刻的多。