Jetpack-ViewModel源碼解析

官方文檔:https://developer.android.com/topic/libraries/architecture/viewmodel

前言

ViewModel的源碼比較簡單,代碼也不是很多,網(wǎng)上已經(jīng)有了很多的文章來講 ViewModel,但是最近查看源碼時發(fā)現(xiàn)源碼中狀態(tài)保存的實現(xiàn)邏輯有了較大的變化,所以還是記錄下來,并重新梳理了一遍源碼的分析過程。

PS:本文是基于 android.arch.lifecycle:viewmodel:1.1.1 的源碼進(jìn)行分析

簡單示例

class CountLiveData : LiveData<Int>() {
    private var count = 0

    fun doCount() {
        value = ++count
    }
}
class MyViewModel : ViewModel() {
    var data = CountLiveData()

    fun doCount() {
        data.doCount()
    }
}
class ViewModelDemoActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_demo_view_model)
        // 獲取 ViewModel 對象
        val viewModel = ViewModelProviders.of(this).get(MyViewModel::class.java)
        // 監(jiān)聽數(shù)據(jù)源
        viewModel.data.observe(this, Observer { tv_test.text = it.toString() })
        // 點(diǎn)擊按鈕改變數(shù)據(jù)
        btn_test.setOnClickListener {
            viewModel.doCount()
        }
    }
}

源碼解析

核心類

  • ViewModelProviders
  • ViewModelProvider
  • ViewModelStore
  • FragmentActivity

一、ViewModel的創(chuàng)建

ViewModel的實例化很簡單,代碼如下

val viewModel = ViewModelProviders.of(activity).get(MyViewModel::class.java)

1) ViewModelProviders.of(activity, factory) 方法

@NonNull
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity, @Nullable Factory factory) {
    Application application = checkApplication(activity);
    if (factory == null) {
        factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
    }
    return new ViewModelProvider(activity.getViewModelStore(), factory);
}

參數(shù)也可以是Fragment,這里以Activity為例,先來看of方法的第二個參數(shù) Factory

public interface Factory {
    /**
     * Creates a new instance of the given {@code Class}.
     * <p>
     *
     * @param modelClass a {@code Class} whose instance is requested
     * @param <T>        The type parameter for the ViewModel.
     * @return a newly created ViewModel
     */
    @NonNull
    <T extends ViewModel> T create(@NonNull Class<T> modelClass);
}

Factory 是 ViewModelProvider 的內(nèi)部類,定義了一個 create 方法,作用就是創(chuàng)建一個 ViewModel 對象,這個很好理解。Factory 接口默認(rèn)有兩個實現(xiàn)類,分別是 NewInstanceFactory 和 AndroidViewModelFactory,其中 NewInstanceFactory 的實現(xiàn)就是通過反射調(diào)用構(gòu)造器創(chuàng)建 ViewModel 對象,AndroidViewModelFactory 則是 NewInstanceFactory 的子類,在 NewInstanceFactory 的基礎(chǔ)上多一個 Context 字段,以下是兩個類的源碼。

  • NewInstanceFactory 源碼
public static class NewInstanceFactory implements Factory {

    @SuppressWarnings("ClassNewInstance")
    @NonNull
    @Override
    public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
        //noinspection TryWithIdenticalCatches
        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);
        }
    }
}
  • AndroidViewModelFactory 源碼
public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory {
    
    private static AndroidViewModelFactory sInstance;

    /**
     * Retrieve a singleton instance of AndroidViewModelFactory.
     *
     * @param application an application to pass in {@link AndroidViewModel}
     * @return A valid {@link AndroidViewModelFactory}
     */
    @NonNull
    public static AndroidViewModelFactory getInstance(@NonNull Application application) {
        if (sInstance == null) {
            sInstance = new AndroidViewModelFactory(application);
        }
        return sInstance;
    }

    private Application mApplication;

    /**
     * Creates a {@code AndroidViewModelFactory}
     *
     * @param application an application to pass in {@link AndroidViewModel}
     */
    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);
    }
}

我們繼續(xù)回來看 ViewModelProviders.of(activity, factory) 方法,不用往上翻了,我再貼一下源碼:

@NonNull
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity, @Nullable Factory factory) {
    Application application = checkApplication(activity);
    if (factory == null) {
        factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
    }
    return new ViewModelProvider(activity.getViewModelStore(), factory);
}

這里 factory 如果為 null,則會創(chuàng)建一個默認(rèn)的 Factory 對象,類型就是上面講到的 AndroidViewModelFactory。最后,of 方法返回了一個通過構(gòu)造方法創(chuàng)建的 ViewModelProvider 對象,第一個參數(shù)是 activity.getViewModelStore(),這里的activity 是 FragmentActivity 類型

public class FragmentActivity extends ComponentActivity implements
        ViewModelStoreOwner,
        ActivityCompat.OnRequestPermissionsResultCallback,
        ActivityCompat.RequestPermissionsRequestCodeValidator {
    @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.");
        }
        if (mViewModelStore == null) {
            mViewModelStore = new ViewModelStore();
        }
        return mViewModelStore;
    }
}

在 FragmentActivity 的源碼中可以看到,F(xiàn)ragmentActivity 實現(xiàn)了
ViewModelStoreOwner 接口,接口中定義了 getViewModelStore() 方法,F(xiàn)ragmentActivity 實現(xiàn) getViewModelStore() 方法并返回了一個 ViewModelStore 對象。

public interface ViewModelStoreOwner {
    @NonNull
    ViewModelStore getViewModelStore();
}

在 ViewModelStore 中,定義了一個 HashMap 對象,存儲的 key 是包含各 ViewModel 子類完整類名的字符串(在后面的源碼中會看到),value 則是 ViewModel 對象。所以 ViewModelStore 其實就是一個容器,內(nèi)部保存了所有的 ViewModel 實例。

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

    /**
     *  Clears internal storage and notifies ViewModels that they are no longer used.
     */
    public final void clear() {
        for (ViewModel vm : mMap.values()) {
            vm.onCleared();
        }
        mMap.clear();
    }
}

2) ViewModelProvider 類

ViewModelProviders.of() 方法最后創(chuàng)建并返回了一個 ViewModelProvider 對象,那我們就繼續(xù)來看 ViewModelProvider 類,首先來看它的構(gòu)造方法:

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

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

在構(gòu)造方法中保存了 ViewModelStore 和 Factory 對象,這兩個參數(shù)在之前都已經(jīng)介紹過了,ViewModelStore 用于保存 ViewModel 對象,F(xiàn)actory 用于創(chuàng)建 ViewModel 對象。

到這里,一個 ViewModelProvider 對象就創(chuàng)建好了,然后我們會調(diào)用 VideModelProvider 的 get 方法來獲取 ViewModel 對象,看源碼:

public class ViewModelProvider {

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

    @NonNull
    @MainThread
    public <T extends ViewModel> T get(@NonNull Class<T>     modelClass) {
        // canonicalName 是 modelClass 的完整類名(例如:     com.evan.demo.MyViewModel)
        String canonicalName = modelClass.getCanonicalName();
        if (canonicalName == null) {
            throw new IllegalArgumentException("Local and     anonymous classes can not be ViewModels");
        }
        return get(DEFAULT_KEY + ":" + canonicalName,     modelClass);
    }
    
    @NonNull
    @MainThread
    public <T extends ViewModel> T get(@NonNull String key,     @NonNull Class<T> modelClass) {
        ViewModel viewModel = mViewModelStore.get(key);
    
        if (modelClass.isInstance(viewModel)) {
            //noinspection unchecked
            return (T) viewModel;
        } else {
            //noinspection StatementWithEmptyBody
            if (viewModel != null) {
                // TODO: log a warning.
            }
        }
    
        viewModel = mFactory.create(modelClass);
        mViewModelStore.put(key, viewModel);
        //noinspection unchecked
        return (T) viewModel;
    } 
}

get 方法傳入的參數(shù)應(yīng)是 ViewModel 子類的 Class 對象,然后在 mViewModelStore 查看是否有緩存,如果有則直接返回,如果沒有則通過 mFactory 創(chuàng)建一個并存入 mViewModelStore。

到這里為止,我們已經(jīng)讀完了獲取 ViewModel 對象的源碼。

小結(jié):
  1. 通過 ViewModelProviders.of(activity, factory) 獲取 ViewModelProvider 實例,ViewModelProvider 中包含一個 ViewModelStore 實例,用于保存已創(chuàng)建的 ViewModel 實例(ViewModelStore 通過 FragmentActivity 的 getViewModelStore() 方法獲?。琭actory則是用于創(chuàng)建ViewModel實例
  2. 通過 ViewModelProvider.get(modelClass) 方法可以獲取 ViewModel實例,實際就是從 ViewModelStore 容器中取出對應(yīng)的 ViewModel實力緩存,如果沒有則通過 factory 創(chuàng)建實例并存入 ViewModelStore

二、ViewModel 實現(xiàn)數(shù)據(jù)持久化原理

還記得么?之前有提到過,ViewModelStore 的實例是由 FragmentActivity 中的 getViewModelStore() 方法提供的,我們再來看源碼:

public class FragmentActivity extends ComponentActivity implements
        ViewModelStoreOwner,
        ActivityCompat.OnRequestPermissionsResultCallback,
        ActivityCompat.RequestPermissionsRequestCodeValidator {
    @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.");
        }
        if (mViewModelStore == null) {
            mViewModelStore = new ViewModelStore();
        }
        return mViewModelStore;
    }
}

從代碼中可以看到,F(xiàn)ragmentActivity中有一個 mViewModelStore 字段,保存了 ViewModelStore 實例,我們可以在 FragmentActivity 的源碼中對搜索 mViewModelStore,就可以找到幾處對 mViewModelStore 的邏輯處理,這些代碼的位置就是 ViewModel 能夠做到狀態(tài)保存的核心代碼,我們一起來看一下。

/**
 * Retain all appropriate fragment state.  You can NOT
 * override this yourself!  Use {@link #onRetainCustomNonConfigurationInstance()}
 * if you want to retain your own state.
 */
@Override
public final Object onRetainNonConfigurationInstance() {
    // 開發(fā)者自定義的狀態(tài)保存
    Object custom = onRetainCustomNonConfigurationInstance();

    FragmentManagerNonConfig fragments = mFragments.retainNestedNonConfig();

    if (fragments == null && mViewModelStore == null && custom == null) {
        return null;
    }

    NonConfigurationInstances nci = new NonConfigurationInstances();
    nci.custom = custom;
    nci.viewModelStore = mViewModelStore;
    nci.fragments = fragments;
    // 返回的 NonConfigurationInstances 對象不會被回收
    return nci;
}

上面這段代碼是最核心的部分,大家都知道 Activity 在橫豎屏切換時會回調(diào) onSaveInstanceState 方法用于保存數(shù)據(jù),之后再通過 onCreate 或 onRestoreInstanceState 方法還原數(shù)據(jù)。但是,可能很多人不知道的是,在 Activity 中還有兩個方法也同樣可以用于保存Activity的狀態(tài),它們是 onRetainNonConfigurationInstance 和 getLastNonConfigurationInstance 方法。Activity 在重建時會回調(diào) onRetainNonConfigurationInstance 方法,該方法會返回一個Object對象保存狀態(tài),而不再是Bundle類型了。在 Activity 恢復(fù)時可以調(diào)用 getLastNonConfigurationInstance() 方法獲取最近一次 onRetainNonConfigurationInstance() 方法返回的 Object 對象用于恢復(fù)狀態(tài)。

我們可以看到在源碼中 onRetainNonConfigurationInstance 方法返回的是一個 NonConfigurationInstances 對象,NonConfigurationInstances 類很簡單,只保存了三個對象,分別是custon(自定義狀態(tài))、viewModelStore、fragments(Activity中的所有Fragment的保存狀態(tài)),既然 viewModelStore 跟著 NonConfigurationInstances 實例一起在 onRetainNonConfigurationInstance 方法中返回了,viewModelStore 自然也就被保留了下來,不會被在橫豎屏切換時回收。

static final class NonConfigurationInstances {
    Object custom;
    ViewModelStore viewModelStore;
    FragmentManagerNonConfig fragments;
}

那既然 viewModelStore 保存了下來,那肯定也會有恢復(fù)狀態(tài)的邏輯,請看以下源碼:

/**
 * Perform initialization of all fragments.
 */
@SuppressWarnings("deprecation")
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    mFragments.attachHost(null /*parent*/);

    super.onCreate(savedInstanceState);

    NonConfigurationInstances nc = (NonConfigurationInstances) getLastNonConfigurationInstance();
    if (nc != null) {
        mViewModelStore = nc.viewModelStore;
    }
    // ...
}

可以看到,在 FragmentActivity 的 onCreate 中調(diào)用 getLastNonConfigurationInstance 方法做了相應(yīng)的處理

PS:細(xì)心的同學(xué)應(yīng)該也注意到了 onRetainNonConfigurationInstance 方法源碼中的注釋,官方不允許開發(fā)者直接覆寫onRetainNonConfigurationInstance()方法,應(yīng)該該方法中已經(jīng)了默認(rèn)實現(xiàn),其中就包括 ViewModelStore 實例的保存,如要開發(fā)者需要保存恢復(fù)其他數(shù)據(jù),則應(yīng)該覆寫 onRetainCustomNonConfigurationInstance方法。

三、ViewModel 的資源回收

根據(jù)官方文檔描述 ViewModel的生命周期范圍涵蓋了整個Activity或Fragment的生命周期,包括Activity重建觸發(fā)的生命周期(比如屏幕旋轉(zhuǎn)或配置更改等情況),當(dāng)生命周期結(jié)束時,會回調(diào)onCleared方法,如下圖所示:


viewmodel-lifecycle.png

FragmentActivity 在 onDestroy 方法中將 mViewModelStore 資源回收了

/**
 * Destroy all fragments.
 */
@Override
protected void onDestroy() {
    super.onDestroy();

    if (mViewModelStore != null && !isChangingConfigurations()) {
        mViewModelStore.clear();
    }

    mFragments.dispatchDestroy();
}

最后看 ViewModelStore 的源碼,在 clear 方法中,調(diào)用了所有 ViewModel 實例的 onCleared() 方法

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

    /**
     *  Clears internal storage and notifies ViewModels that they are no longer used.
     */
    public final void clear() {
        for (ViewModel vm : mMap.values()) {
            vm.onCleared();
        }
        mMap.clear();
    }
}

至此,ViewModel 源碼解析就結(jié)束了。

總結(jié)

ViewModel 的代碼并不復(fù)雜,其核心代碼其實就是通過 onRetainNonConfigurationInstance 方法進(jìn)行了狀態(tài)保存。

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

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

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