Android Jetpack架構(gòu)組件(四)—ViewModel

一、ViewModel簡介

ViewModel:是以感知生命周期的形式來存儲和管理視圖相關(guān)的數(shù)據(jù)。

ViewModel主要有以下的特點:

  1. 當Activity被銷毀時,我們可以使用onSaveInstanceState()方法恢復(fù)其數(shù)據(jù),這種方法僅適用于恢復(fù)少量的支持序列化、反序列化的數(shù)據(jù),不適用于大量數(shù)據(jù),如用戶列表或位圖。而ViewModel不僅支持大量數(shù)據(jù),還不需要序列化、反序列化操作。
  2. Activity/Fragment(視圖控制器)主要用于顯示視圖數(shù)據(jù),如果它們也負責數(shù)據(jù)庫或者網(wǎng)絡(luò)加載數(shù)據(jù)等操作,那么一旦邏輯過多,會導(dǎo)致視圖控制器臃腫,ViewModel可以更容易,更有效的將視圖數(shù)據(jù)相關(guān)邏輯和視圖控制器分離開來。
  3. 視圖控制器經(jīng)常需要一些時間才可能返回的異步調(diào)用,視圖控制器需要管理這些調(diào)用,在合適的時候清理它們,以確保它們的生命周期不會大于自身,避免內(nèi)存泄漏。而ViewModel恰恰可以避免內(nèi)存泄漏的發(fā)生。


    生命周期

Activity的生命周期不斷變化,經(jīng)歷了被銷毀重新創(chuàng)建,而ViewModel的生命周期沒有發(fā)生變化。只有當Activity真正Finished了,ViewModel才會執(zhí)行onCleared()方法銷毀。

二、ViewModel的使用

1、自定義ViewModel

繼承ViewMode,實現(xiàn)自定義ViewModel。

import android.arch.lifecycle.LiveData;
import android.arch.lifecycle.MutableLiveData;
import android.arch.lifecycle.ViewModel;

public class UserModel extends ViewModel {
       private final MutableLiveData<User> userLiveData = new MutableLiveData<>();
  
       public LiveData<User> getUser() {
           return userLiveData;
       }
  
       public UserModel() {
       }
  
       public void doAction() {
          userLiveData.setValue(new User())
       }
   }

2、使用ViewModel

在Activity中使用LoginViewModel

import android.arch.lifecycle.Observer;
import android.arch.lifecycle.ViewModelProviders;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;

public class UserActivity extends Activity {
  
        @Override
       protected void onCreate(Bundle savedInstanceState) {
           super.onCreate(savedInstanceState);
           setContentView(R.layout.activity_user);
           final UserModel viewModel = new ViewModelProvider(this).get(UserModel.class);
           viewModel.getUser().observe(this, new Observer<User>() {
                @Override
               public void onChanged(@Nullable User data) {
                   // 更新UI
               }
           });
           findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
                @Override
               public void onClick(View v) {
                    viewModel.doAction();
               }
           });
       }
   }

三、ViewModel的原理

首先查看ViewModel源碼

public abstract class ViewModel {
    @Nullable
    private final Map<String, Object> mBagOfTags = new HashMap<>();
    private volatile boolean mCleared = false;

    /**
     * 當不再使用此 ViewModel 并將被銷毀時,將調(diào)用此方法。當 ViewModel 觀察到一些數(shù)據(jù)并且您需要清除此訂閱以防止此 ViewModel 泄漏時,它很有用。
     */
    @SuppressWarnings("WeakerAccess")
    protected void onCleared() {
    }

    @MainThread
    final void clear() {
        mCleared = true;
        // 由于 clear() 是最終的,因此仍然會在模擬對象上調(diào)用此方法,在這些情況下,mBagOfTags 為空。它總是空的,因為 setTagIfAbsent 和 getTag 不是最終的,所以我們可以跳過清除它
        if (mBagOfTags != null) {
            synchronized (mBagOfTags) {
                for (Object value : mBagOfTags.values()) {
                    // see comment for the similar call in setTagIfAbsent
                    closeWithRuntimeException(value);
                }
            }
        }
        onCleared();
    }

    /**
     * 設(shè)置與此視圖模型關(guān)聯(lián)的標簽和鍵。如果給定的 newValue 是可關(guān)閉的,它將在 clear() 后關(guān)閉。如果已經(jīng)為給定鍵設(shè)置了值,則此調(diào)用不執(zhí)行任何操作并返回當前關(guān)聯(lián)的值,則將忽略給定的 newValue 如果 ViewModel 已被清除,則如果它實現(xiàn) Closeable,則將對返回的對象調(diào)用 close()。同一個對象可能會收到多個關(guān)閉調(diào)用,因此方法應(yīng)該是冪等的。
     */
    @SuppressWarnings("unchecked")
    <T> 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) {
            // 我們可能會在同一個對象上多次調(diào)用 close(),但 Closeable 接口要求 close 方法是冪等的:“如果流已經(jīng)關(guān)閉,則調(diào)用此方法無效?!?            closeWithRuntimeException(result);
        }
        return result;
    }

    /**
     * 返回與此視圖模型關(guān)聯(lián)的標簽和指定的鍵。
     */
    @SuppressWarnings({"TypeParameterUnusedInFormals", "unchecked"})
    <T> T getTag(String key) {
        if (mBagOfTags == null) {
            return null;
        }
        synchronized (mBagOfTags) {
            return (T) mBagOfTags.get(key);
        }
    }

    private static void closeWithRuntimeException(Object obj) {
        if (obj instanceof Closeable) {
            try {
                ((Closeable) obj).close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

1、ViewModel初始化

final UserModel viewModel = new ViewModelProvider(this).get(UserModel.class);

可以看到,我們并沒有手動調(diào)用 ViewModel 的構(gòu)造函數(shù)來創(chuàng)建 ViewModel 實例,而是由 ViewModelProvider 來獲取,其實ViewModel 初始化是在ViewModelProvider內(nèi)部自己通過反射來構(gòu)建出 ViewModel 實例。

ViewModelProvider源碼
public class ViewModelProvider {

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

    public interface Factory {
        @NonNull
        <T extends ViewModel> T create(@NonNull Class<T> modelClass);
    }

    static class OnRequeryFactory {
        void onRequery(@NonNull ViewModel viewModel) {
        }
    }

 
    abstract static class KeyedFactory extends OnRequeryFactory implements Factory {

        @NonNull
        public abstract <T extends ViewModel> T create(@NonNull String key,
                @NonNull Class<T> modelClass);

        @NonNull
        @Override
        public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
            throw new UnsupportedOperationException("create(String, Class<?>) must be called on "
                    + "implementaions of KeyedFactory");
        }
    }

    private final Factory mFactory;
    private final ViewModelStore mViewModelStore;

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

    @NonNull
    @MainThread
    public <T extends ViewModel> T get(@NonNull Class<T> 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);
    }

    @SuppressWarnings("unchecked")
    @NonNull
    @MainThread
    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;
    }

    
    public static class NewInstanceFactory implements Factory {

        private static NewInstanceFactory sInstance;

        @NonNull
        static NewInstanceFactory getInstance() {
            if (sInstance == null) {
                sInstance = new NewInstanceFactory();
            }
            return sInstance;
        }

        @SuppressWarnings("ClassNewInstance")
        @NonNull
        @Override
        public <T extends ViewModel> T create(@NonNull Class<T> 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);
            }
        }
    }

    public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory {

        private static AndroidViewModelFactory sInstance;

        @NonNull
        public static AndroidViewModelFactory getInstance(@NonNull Application application) {
            if (sInstance == null) {
                sInstance = new AndroidViewModelFactory(application);
            }
            return sInstance;
        }

        private Application mApplication;

        public AndroidViewModelFactory(@NonNull Application application) {
            mApplication = application;
        }

        @NonNull
        @Override
        public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
            if (AndroidViewModel.class.isAssignableFrom(modelClass)) {
                //noinspection TryWithIdenticalCatches
                try {
                    return modelClass.getConstructor(Application.class).newInstance(mApplication);
                } catch (NoSuchMethodException e) {
                    throw new RuntimeException("Cannot create an instance of " + modelClass, e);
                } catch (IllegalAccessException e) {
                    throw new RuntimeException("Cannot create an instance of " + modelClass, e);
                } catch (InstantiationException e) {
                    throw new RuntimeException("Cannot create an instance of " + modelClass, e);
                } catch (InvocationTargetException e) {
                    throw new RuntimeException("Cannot create an instance of " + modelClass, e);
                }
            }
            return super.create(modelClass);
        }
    }
}
1. ViewModelProvider 構(gòu)造函數(shù)

ViewModelProvider 一共包含三個構(gòu)造函數(shù),可以看到,不管是哪種方式,最終都是要拿到兩個構(gòu)造參數(shù):ViewModelStore 和 Factory,且都不能為 null。

    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;
    }
  • 三個構(gòu)造函數(shù),其中最終都會調(diào)用第三個構(gòu)造函數(shù);
  • 第一個構(gòu)造函數(shù)是我們在Activity和Fragment中用的;因為AppCompatActivity 的父類 androidx.activity.ComponentActivity和Fragment 都已經(jīng)實現(xiàn)了 ViewModelStoreOwner 和 HasDefaultViewModelProviderFactory 兩個接口,所以我們可以直接使用只包含一個參數(shù)的構(gòu)造函數(shù);
  • 而如果傳入的 ViewModelStoreOwner 實例沒有繼承 HasDefaultViewModelProviderFactory 接口的話,mFactory 就使用 NewInstanceFactory 來初始化;
2. ViewModelStore 和 Factory的獲取

Activity和Fragment實現(xiàn)了ViewModelStoreOwner和HasDefaultViewModelProviderFactory接口,這兩個接口分別提供了ViewModelStore 和 Factory;

1. ViewModelStore通過ViewModelStoreOwner接口獲取,用于存儲ViewModel實例;
2. Factory 是 ViewModelProvider 的內(nèi)部接口,用于實現(xiàn)初始化 ViewModel 的邏輯。
  • Activity和Fragment中通過getDefaultViewModelProviderFactory() 方法返回的是 SavedStateViewModelFactory;
  • 其他方式創(chuàng)建的話使用NewInstanceFactory ,通過反射來實例化 ViewModel 實例,但是也只適用于不包含構(gòu)造參數(shù)的情況,如果是有參構(gòu)造函數(shù)的話就需要我們來主動實現(xiàn) Factory 接口,畢竟構(gòu)造參數(shù)也需要我們來主動傳入。
public class ComponentActivity extends androidx.core.app.ComponentActivity implements
        ContextAware,
        LifecycleOwner,
        ViewModelStoreOwner,
        HasDefaultViewModelProviderFactory,
        SavedStateRegistryOwner,
        OnBackPressedDispatcherOwner,
        ActivityResultRegistryOwner,
        ActivityResultCaller,
        MenuHost {

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

    @SuppressWarnings("WeakerAccess")
    void ensureViewModelStore() {
        if (mViewModelStore == null) {
            NonConfigurationInstances nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) {
                // Restore the ViewModelStore from NonConfigurationInstances
                mViewModelStore = nc.viewModelStore;
            }
            if (mViewModelStore == null) {
                mViewModelStore = new ViewModelStore();
            }
        }
    }


    @NonNull
    @Override
    public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {
        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 (mDefaultFactory == null) {
            mDefaultFactory = new SavedStateViewModelFactory(
                    getApplication(),
                    this,
                    getIntent() != null ? getIntent().getExtras() : null);
        }
        return mDefaultFactory;
    }

}
3. ViewModelProvider(this).get()方法

既然 Factory 實例也有了,下一步就是來調(diào)用 ViewModelProvider(this).get() 方法了。get() 方法需要我們傳入 Class 對象,ViewModelProvider 需要拿到 Class 才能完成反射操作。在此方法里主要是通過 modelClass 來自動生成一個字符串 Key,并將參數(shù)轉(zhuǎn)發(fā)給另外一個 get() 方法。

ViewModelProvider中
@NonNull
    @MainThread
    public <T extends ViewModel> T get(@NonNull Class<T> 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);
    }

    @SuppressWarnings("unchecked")
    @NonNull
    @MainThread
    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;
    }

該方法會通過 key 從 ViewModelStore 里取 ViewModel 實例,如果取不到值或者是取出來的值類型不符,則會通過 mFactory.create(modelClass) 方法來反射初始化 ViewModel,并在返回初始化結(jié)果前將它存到 mViewModelStore 中,這樣就完成了 ViewModel 的初始化流程了。

2、ViewModel保持不變

結(jié)論:ViewModel保持不變是因為ViewModelStore沒有變化。

原因:Activity 每次獲取 ViewModel 實例都會先嘗試從 mViewModelStore 中取值,只有在取不到值的時候才會去重新構(gòu)建一個新的 ViewModel 實例,且構(gòu)建后的 ViewModel 實例也會被保存在mViewModelStore 中。那既然 Activity 可以在頁面銷毀重建的情況下獲取到之前的 ViewModel 實例,那么不也就間接說明了在這種情況下 ViewModelStore 也是一直被保留著而沒有被回收。

ViewModelStore 本身實現(xiàn)的邏輯挺簡單的,通過一個 HashMap 來緩存每一個 ViewModel 實例,并提供了存取 ViewModel 的方法。

ViewModelStore源碼
public class ViewModelStore {

    private final HashMap<String, ViewModel> 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<String> keys() {
        return new HashSet<>(mMap.keySet());
    }

    public final void clear() {
        for (ViewModel vm : mMap.values()) {
            vm.clear();
        }
        mMap.clear();
    }
}
ComponentActivity
@NonNull
    @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;
    }

    @SuppressWarnings("WeakerAccess")
    void ensureViewModelStore() {
        if (mViewModelStore == null) {
            NonConfigurationInstances nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) {
                // Restore the ViewModelStore from NonConfigurationInstances
                mViewModelStore = nc.viewModelStore;
            }
            if (mViewModelStore == null) {
                mViewModelStore = new ViewModelStore();
            }
        }
    }

ComponentActivity 的 getViewModelStore() 方法獲取 ViewModelStore 實例的來源有兩種:

  • 如果 NonConfigurationInstances 不為 null 則通過它獲取。對應(yīng) Activity 由于配置更改導(dǎo)致重建的情況,NonConfigurationInstances 當中就保留了頁面重建過程中被保留下來的數(shù)據(jù),此時就可以獲取到上一個 Activity 保存的 ViewModelStore 實例了
  • 直接初始化 ViewModelStore 實例返回。對應(yīng) Activity 正常被啟動的情況。

這里只要看第一種情況

1. NonConfigurationInstances源碼

NonConfigurationInstances 是 ComponentActivity 的一個靜態(tài)內(nèi)部類,其內(nèi)部就包含了一個 ViewModelStore 成員變量,在 Activity 被重建時,其對應(yīng)的 ViewModelStore 就被保存在了這。

ComponentActivity源碼中
static final class NonConfigurationInstances {
        Object custom;
        ViewModelStore viewModelStore;
    }
2. NonConfigurationInstances.viewModelStore 變量的賦值

通過查找引用,可以找到 ComponentActivity 就是在 onRetainNonConfigurationInstance() 方法里來完成 NonConfigurationInstances.viewModelStore 變量的賦值。從該方法名可以猜出,該方法就用于獲取非配置項實例,以便在后續(xù)重建 Activity 時恢復(fù)數(shù)據(jù)。

ComponentActivity源碼中
    @Override
    @Nullable
    @SuppressWarnings("deprecation")
    public final Object onRetainNonConfigurationInstance() {
        Object custom = onRetainCustomNonConfigurationInstance();

        ViewModelStore viewModelStore = mViewModelStore;
        if (viewModelStore == null) {
            // 沒有人調(diào)用 getViewModelStore(),所以看看我們最后的 NonConfigurationInstance 是否存在一個現(xiàn)有的 ViewModelStore
            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;
    }
3. onRetainNonConfigurationInstance()方法調(diào)用時機

通過查找方法引用,可以知道 onRetainNonConfigurationInstance() 又是被父類 android.app.Activity 的以下方法所調(diào)用,由父類去負責保留 NonConfigurationInstances 對象。

    NonConfigurationInstances retainNonConfigurationInstances() {
        Object activity = onRetainNonConfigurationInstance();
        HashMap<String, Object> children = onRetainNonConfigurationChildInstances();
        FragmentManagerNonConfig fragments = mFragments.retainNestedNonConfig();

        mFragments.doLoaderStart();
        mFragments.doLoaderStop(true);
        ArrayMap<String, LoaderManager> loaders = mFragments.retainLoaderNonConfig();

        if (activity == null && children == null && fragments == null && loaders == null
                && mVoiceInteractor == null) {
            return null;
        }

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

從以上流程可以看出 Activity 的一些設(shè)計思路。由于 android.app.Activity 的邏輯是和特定的系統(tǒng)版本 SDK 關(guān)聯(lián)的,我們無法決定用戶手中的手機系統(tǒng)版本。而我們?nèi)粘i_發(fā)中都是選擇直接繼承于androidx.appcompat.app.AppCompatActivity,它又是作為一個依賴庫來存在的,開發(fā)者可以自行決定要使用哪個版本號,Google 官方也可能隨時推出新版本。所以,android.app.Activity 就將非配置項實例數(shù)據(jù)均當做一個 Object 實例來處理,由子類通過實現(xiàn)onRetainNonConfigurationInstance() 方法來返回,父類 Activity 不限制方法返回值需要特定類型,不同的子類可以返回不同的類型,父類只負責在需要的時候?qū)嵗4嫫饋?,然后在重建時返回給子類即可,由子類自己來進行數(shù)據(jù)的拆解和重建。這樣,不管用戶使用的手機是哪個系統(tǒng)版本,都可以保證三方依賴庫有最大的發(fā)揮余地。

4. retainNonConfigurationInstances() 方法調(diào)用時機

在 ActivityThread 類的以下方法存在調(diào)用,該方法用于回調(diào) Activity 的 onDestroy 方法,在回調(diào)前會先將數(shù)據(jù)保存到 ActivityClientRecord 的 lastNonConfigurationInstances 字段中。

ActivityThread源碼中
/** Core implementation of activity destroy call. */
ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,
        int configChanges, boolean getNonConfigInstance, String reason) {
    ActivityClientRecord r = mActivities.get(token);
    ···
        if (getNonConfigInstance) {
            try {
                //保存 Activity 返回的 NonConfigurationInstances
                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);
                }
            }
        }

    ···
    //調(diào)用 Activity 的 onDestroy 方法
    mInstrumentation.callActivityOnDestroy(r.activity);    
    ···
    return r;
}
5. 從ActivityClientRecord中恢復(fù)NonConfigurationInstances到重啟的Activity中

在重新啟動 Activity 時,又會將數(shù)據(jù) attach 到新的 Activity 實例上,將其作為 getLastNonConfigurationInstance() 方法的返回值。通過這種數(shù)據(jù)交接,重建前的 ViewModelStore 實例就會被重建后的 Activity 拿到,當中就保留了重建前 Activity 初始化的所有 ViewModel 實例,從而保障了 ViewModel 實例的不變性。

ActivityThread源碼中
/**  Core implementation of activity launch. */
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    ···
    Activity activity = null;
    try {
        java.lang.ClassLoader cl = appContext.getClassLoader();
        activity = mInstrumentation.newActivity(
                cl, component.getClassName(), r.intent);
        StrictMode.incrementExpectedActivityCount(activity.getClass());
        r.intent.setExtrasClassLoader(cl);
        r.intent.prepareToEnterProcess();
        if (r.state != null) {
            r.state.setClassLoader(cl);
        }
    } catch (Exception e) {
        if (!mInstrumentation.onException(activity, e)) {
            throw new RuntimeException(
                "Unable to instantiate activity " + component
                + ": " + e.toString(), e);
        }
    }

    ···

    //將 r.lastNonConfigurationInstances 傳遞進去
    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);
    ···
    return activity;
}

3、ViewModel構(gòu)造函數(shù)有值

ViewModelProvider 提供的 Factory 接口實現(xiàn)類有兩個:

  • NewInstanceFactory:通過反射來實例化 ViewModel,適用于包含無參構(gòu)造函數(shù)的情況
  • AndroidViewModelFactory:通過反射來實例化 ViewModel,適用于構(gòu)造參數(shù)只有一個,且參數(shù)類型為 Application 的情況

如果想要通過其它類型的構(gòu)造函數(shù)來初始化 ViewModel 的話,就需要我們自己來實現(xiàn) ViewModelProvider.Factory 接口聲明初始化邏輯了:

class MainActivity : AppCompatActivity() {

    private val myViewModelA by lazy {
        ViewModelProvider(this, object : ViewModelProvider.Factory {
            override fun <T : ViewModel> create(modelClass: Class<T>): T {
                return MyViewModel("Mary") as T
            }
        }).get(
            MyViewModel::class.java
        ).apply {
            ld_name.observe(this@MainActivity, {

            })
        }
    }

    private val myViewModelB by lazy {
        ViewModelProvider(this, object : ViewModelProvider.Factory {
            override fun <T : ViewModel> create(modelClass: Class<T>): T {
                return MyViewModel("Lily") as T
            }
        }).get(
            MyViewModel::class.java
        ).apply {
            ld_name.observe(this@MainActivity, {

            })
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        Log.e("myViewModelA", myViewModelA.toString() + " name: " + myViewModelA.name)
        Log.e("myViewModelB", myViewModelB.toString() + " name: " + myViewModelB.name)
    }

}

class MyViewModel(val name: String) : ViewModel() {
    val ld_name = MutableLiveData<String>()
}

注意:需要注意的是,雖然 myViewModelA 和 myViewModelB 都有各自不同的入?yún)?shù),但它們其實是同一個對象,即最先初始化的那個 ViewModel 實例會被緩存下來重復(fù)使用。是因為在初始化 myViewModelA 和 myViewModelB 的時候它們默認對應(yīng)的都是同個 Key,ViewModelProvider 默認情況下是以 DEFAULT_KEY + ":" + canonicalName 作為 key 值來從 mViewModelStore 中取值,所以在初始化 myViewModelB 的時候就直接把之前已經(jīng)初始化好的 myViewModelA 給返回了。

如果想使用同一個ViewModel類對應(yīng)不同的實例對象,那么就需要在初始化的時候主動為它們指定不同的 Key,這樣它們就可以一起被存到 ViewModelStore 的 HashMap 中了。

    ViewModelProvider(this, object : ViewModelProvider.Factory {
        override fun <T : ViewModel> create(modelClass: Class<T>): T {
            return MyViewModel("Mary") as T
        }
    }).get(
        "keyA", MyViewModel::class.java
    ).apply {
        ld_name.observe(this@MainActivity, {

        })
    }

4、ViewModel回收

ViewModel回收是由于ViewModelStore清空 HashMap。

在 ComponentActivity 中通過監(jiān)聽Lifecycle狀態(tài),在Activity 在收到 ON_DESTROY 事件時,如果判斷到是由于配置項更改導(dǎo)致了 Activity 被銷毀,那么就不會調(diào)用 getViewModelStore().clear() 。如果是正常退出 Activity,那就會調(diào)用 getViewModelStore().clear() 方法,這樣就會清空掉所有緩存的 ViewModel 實例了,ViewModel 的 clear() 方法也同時會被調(diào)用。

ComponentActivity源碼中
public class ComponentActivity extends androidx.core.app.ComponentActivity implements
        ContextAware,
        LifecycleOwner,
        ViewModelStoreOwner,
        HasDefaultViewModelProviderFactory,
        SavedStateRegistryOwner,
        OnBackPressedDispatcherOwner,
        ActivityResultRegistryOwner,
        ActivityResultCaller,
        MenuHost {
  public ComponentActivity() {
    ···
    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();
                }
            }
        }
    });
  }
}
ViewModelStore源碼中
public class ViewModelStore {

    private final HashMap<String, ViewModel> mMap = new HashMap<>();
    ···
    public final void clear() {
        for (ViewModel vm : mMap.values()) {
            vm.clear();
        }
        mMap.clear();
    }
}

參考:從源碼看Jetpack

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容