Activity生命周期探索以及ViewModel恢復狀態


學習android的同學應該都了解Activity,關於Activity的相關介紹也有很多,下面簡要羅列出Activity的生命周期:

 上圖列出了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苛刻的多。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM