Jetpack之ViewMode初識,使用和源碼講解

今天是2022年最后一天,祝大家元旦快樂。


image.png

ViewModel初識

ViewModelJetpack組件之一,它注重生命周期的方式存儲和管理界面的數(shù)據(jù),它是讓數(shù)據(jù)在屏幕旋轉(zhuǎn)等配置更改后繼續(xù)留存。通俗一點(diǎn)就是:手機(jī)屏幕發(fā)生旋轉(zhuǎn)后,數(shù)據(jù)依然還在。

ViewModel沒出來之前,怎么存取數(shù)據(jù)?

ViewModel出現(xiàn)之前,一般屏幕發(fā)生旋轉(zhuǎn)時(shí)候,Activity生命周期會重新創(chuàng)建,我們會在onSaveInstance()里面保存數(shù)據(jù),然后在onCreate(Bundle saveInstance)里面取數(shù)據(jù),但是這個(gè)數(shù)據(jù)有弊端,必須要實(shí)現(xiàn)序列化和反序列化,并且這個(gè)數(shù)據(jù)不能太大。

class ViewModelActivity : AppCompatActivity() {

     override fun onCreate(savedInstanceState: Bundle?) {
          super.onCreate(savedInstanceState)
               //(2)取數(shù)據(jù)
     }

     override fun onSaveInstanceState(outState: Bundle) {
          super.onSaveInstanceState(outState)
               //(1)存數(shù)據(jù)
     }
}

所以ViewModel就是為了解決這個(gè)問題的。

ViewModel的使用
(1)添加ViewModel依賴

implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0"

(2)定義一個(gè)類繼承ViewModel(AndroidViewModel)即可

class MyViewModel : ViewModel() {
      val updateLiveData by lazy { MutableLiveData<String>() }
}

(3)Activity創(chuàng)建ViewModel實(shí)例

class ViewModelActivity : AppCompatActivity() {

      override fun onCreate(savedInstanceState: Bundle?) {
          super.onCreate(savedInstanceState)

          var myViewModel = ViewModelProvider(this).get(MyViewModel::class.java)
          myViewModel.updateLiveData.observe(this) {
           
             //update  data
          }
     }
}

Activity中,我們可以使用ViewModelProvider來得到 ViewMode的實(shí)例。

如果在ViewModel中,我們需要使用到上下文Context對象(toast 或者獲取系統(tǒng)服務(wù)等等),我們可以繼承AndroidViewModel來構(gòu)建ViewModel

class AndroidModel(app:Application): AndroidViewModel(app) {

}

此時(shí)這個(gè)AndroidModel的創(chuàng)建和上面的類似,也是用ViewModelProvider來獲得。

ViewModel常常結(jié)合LiveData使用,然后在我們的Activity或者Fragemnt中去監(jiān)聽LiveData的改變

然后去在Activity中做UI的更新邏輯,例如,我們需要根據(jù)網(wǎng)絡(luò)請求來決定是否彈出一個(gè)DialogFragment,我們的網(wǎng)絡(luò)請求放在ViewModel中,DialogFragment必須在Activity或者Fragment中彈出來(因?yàn)?code>DialogFragment的彈出不能使用Application作為Context),所以此時(shí)我們必須使用LiveData。當(dāng)網(wǎng)絡(luò)請求完成之后,我們改變LiveData的值,并且在Activity或者Fragment監(jiān)聽LiveData的變化,然后作出彈出DailogFragment的操作

Fragment之間共享數(shù)據(jù)

現(xiàn)在的App中使用Fragment是很常見的,之前我們從Activity中向Fragment中傳遞數(shù)據(jù),我們使用Bundle來傳遞(在創(chuàng)建Fragment的時(shí)候),但是在Activity中如果需要動態(tài)傳遞(隨時(shí)傳遞)數(shù)據(jù)給Fragment,我們平常的做法可能是在Activity中持有Fragment的應(yīng)用,然后在Activity中去調(diào)用對于Fragment的某些方法傳遞數(shù)據(jù),或者利用通知系統(tǒng)(EventBus),但是我們?nèi)绻?code>BFragment中促使Activity中的數(shù)據(jù)改變,要通知到CFragment的Ui修改的話,目前的場景只能使用EventBus。當(dāng)然也可以使用我們這里的ViewModel

class ShareViewModel : ViewModel() {
      val selected = MutableLiveData<Item>()

      fun select(item: Item) {
         selected.value = item
      }
}

class ListFragment : Fragment() {

    private lateinit var itemSelector: Selector

    // Use the 'by activityViewModels()' Kotlin property delegate
    // from the fragment-ktx artifact
    private val model: ShareViewModel?=null

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        model = ViewModelProvider(requireActivity()).get(ShareViewModel::class.java)
        itemSelector.setOnClickListener { item ->
            // Update the UI
     }
}

class DetailFragment : Fragment() {

    // Use the 'by activityViewModels()' Kotlin property delegate
    // from the fragment-ktx artifact
    private val model: ShareViewModel?=null

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
    
        model = ViewModelProvider(requireActivity()).get(ShareViewModel::class.java)
        model.selected.observe(viewLifecycleOwner, Observer<Item> { item ->
            // Update the UI
    })
}

在上面的兩個(gè)Fragment 在獲取ViewModel的時(shí)候 傳遞的都是requireActivity(),那么獲取到的ViewModel的實(shí)例其實(shí)就是同一個(gè)SharedViewModel,所以當(dāng)Activity 或者任何一個(gè)Fragment中 改變了SharedViewModel中LiveData的數(shù)據(jù),都會及時(shí)的通知到。這種方法有以下好處:

Activity 不需要執(zhí)行任何操作,也不需要對此通信有任何了解。
除了 SharedViewModel之外,Fragment不需要相互了解。如果其中一個(gè) Fragment消失,另一個(gè) Fragment將繼續(xù)照常工作。
每個(gè) Fragment都有自己的生命周期,而不受另一個(gè) Fragment的生命周期的影響。如果一個(gè)Fragment替換另一個(gè)Fragment,界面將繼續(xù)工作而沒有任何問題。

ViewModel的源碼解析

image.png

這里基本只需要知道onCleared()方法就行了,自定義ViewModel并重寫這個(gè)方法,講釋放資源的邏輯放在這個(gè)方法中就行

ViewModelProvider

獲取ViewModel的實(shí)例時(shí),我們是使用ViewModelProvider來獲取的

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

創(chuàng)建ViewModelProvider的時(shí)候需要傳遞一個(gè)ViewModelStore 和一個(gè)Factory,而我們構(gòu)建的時(shí)候只傳遞了一個(gè)this(Activity),其實(shí)就是一個(gè)ViewModelStoreOwner,

AppCompatActivity -->FragmentActivity -->ComponentActivity --> ViewModelStoreOwner

有上面這樣一個(gè)繼承實(shí)現(xiàn)關(guān)系,我們的AppCompatActivity其實(shí)可以說是實(shí)現(xiàn)了ViewModelStoreOwner的,最終返回的是ComponentActivity中的mViewModelStore 關(guān)于Factory后面再講

@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") /* synthetic access */
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();
        }
    }
}

這里就是獲取ViewModelStore的方法,可以看到在ensureViewModelStore方法中,我們會首先判斷mViewModelStore 是否為null,然后通過getLastNonConfigurationInstance()得到一個(gè)NonConfigurationInstances實(shí)例,這里其實(shí)就是當(dāng)Activity旋轉(zhuǎn)的時(shí)候ViewModel中的數(shù)據(jù)還會存在的奧秘,通過nc可以獲取重建之前的mViewModelStore,然后從ViewModelStore里面根據(jù)類名獲取ViewModel的實(shí)例,所以獲取到的ViewModel在旋轉(zhuǎn)前后其實(shí)是同一個(gè)實(shí)例,在我們的App系統(tǒng)configuration發(fā)生改變的時(shí)候 就會回調(diào)onRetainNonConfigurationInstance()這個(gè)方法

@SuppressWarnings("deprecation")
public final Object onRetainNonConfigurationInstance() {
    // Maintain backward compatibility.
    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;
}

//Activity中 的方法
@Nullable
public Object getLastNonConfigurationInstance() {
    return mLastNonConfigurationInstances != null
        ? mLastNonConfigurationInstances.activity : null;
}

這里就是將ViewModelStore進(jìn)行保存。 這里getLastNonConfigurationInstance方法 最后其實(shí)是返回Activity中的mLastNonConfigurationInstances 變量的activity對象,我們看看這mLastNonConfigurationInstances 在哪里賦值,我們知道,我們的Activity的啟動其實(shí)最終都會走到ActivityThread類中,當(dāng)我們啟動一個(gè)Activity的時(shí)候會執(zhí)行其中的 performLaunchActivity方法最終會調(diào)用到 Activityattach方法,lastNonConfigurationInstances是存在ActivityClientRecord中的一個(gè)組件信息

ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,
    int configChanges, boolean getNonConfigInstance, String reason) {
    ActivityClientRecord r = mActivities.get(token);
    Class<? extends Activity> activityClass = null;
    if (localLOGV) Slog.v(TAG, "Performing finish of " + r);
    if (r != null) {
        activityClass = r.activity.getClass();
        r.activity.mConfigChangeFlags |= configChanges;
        if (finishing) {
            r.activity.mFinished = true;
        }

        performPauseActivityIfNeeded(r, "destroy");

        if (!r.stopped) {
            callActivityOnStop(r, false /* saveState */, "destroy");
        }
        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);
                }
            }
        }
        try {
            r.activity.mCalled = false;
            mInstrumentation.callActivityOnDestroy(r.activity);
            if (!r.activity.mCalled) {
                throw new SuperNotCalledException(
                "Activity " + safeToComponentShortString(r.intent) +
                " did not call through to super.onDestroy()");
            }
            if (r.window != null) {
                r.window.closeAllPanels();
            }
        } catch (SuperNotCalledException e) {
            throw e;
        } catch (Exception e) {
            if (!mInstrumentation.onException(r.activity, e)) {
                throw new RuntimeException(
                    "Unable to destroy activity " + safeToComponentShortString(r.intent)
                    + ": " + e.toString(), e);
            }
        }
        r.setState(ON_DESTROY);
    }
    schedulePurgeIdler();
    // updatePendingActivityConfiguration() reads from mActivities to update
    // ActivityClientRecord which runs in a different thread. Protect modifications to
    // mActivities to avoid race.
    synchronized (mResourcesManager) {
        mActivities.remove(token);
    }
    StrictMode.decrementExpectedActivityCount(activityClass);
    return r;
}

在屏幕旋轉(zhuǎn)造成的的Activity重建的時(shí)候 就會給lastNonConfigurationInstances這個(gè)變量賦值,這樣就能夠在Activity重建的時(shí)候 獲取到之前的ViewModel了。而我們的ViewModelonClear方法什么時(shí)候執(zhí)行呢,在我們的ComponetActivity構(gòu)方法中

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

上面注冊了一個(gè)Lifecycle的監(jiān)聽,在我們的ActivityonDestory之后 并且isChangingConfigurations()為false的時(shí)候,才會去執(zhí)行getViewModelStore().clear(); 的操作間接調(diào)用到ViewModel的onCleared()方法

get()獲取ViewModel
在這里通過get方法創(chuàng)建ViewModel傳入的參數(shù)是 所需要創(chuàng)建的ViewModelClass對象

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

最終通過下面的get方法獲取ViewModel,當(dāng)我們從ViewModelStore 根據(jù)key值去獲取ViewModel為null的時(shí)候,如果為null 就是用Factory進(jìn)行創(chuàng)建。所以后面我們也可以定義自己的Factory 來創(chuàng)建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);
    }

    Set<String> keys() {
        return new HashSet<>(mMap.keySet());
    }

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

這里其實(shí)就是一個(gè) HashMap存放了ViewModel,主要是ViewModel的存取.

總結(jié)

1.現(xiàn)在的Activity底層已經(jīng)繼承了 ComponentActivity ,并實(shí)現(xiàn)了 ViewModelStoreOwner 接口,通過ViewModelProvider使用默認(rèn)工廠 創(chuàng)建了 viewModel,并通過唯一Key值 進(jìn)行標(biāo)識,
存儲到了 ViewModelStore中。等下次需要的時(shí)候即可通過唯一Key值進(jìn)行獲取。
2.由于ComponentActivity 實(shí)現(xiàn)了ViewModelStoreOwner接口,實(shí)現(xiàn)了 getViewModelStore方法,當(dāng)屏幕旋轉(zhuǎn)的時(shí)候,會先調(diào)用 onRetainNonConfigurationInstance()方法
viewModelStore保存起來,當(dāng)屏幕旋轉(zhuǎn)之后,會在ensureViewModel()方法中再調(diào)用 getLastNonConfigurationInstance方法將數(shù)據(jù)恢復(fù),如果為空的話,會重新創(chuàng)建viewModelStore ,
并存儲在全局中,以便以下次發(fā)生變化的時(shí)候,能夠通過onRetainNonConfigurationInstance 保存起來。
3.最后當(dāng)頁面銷毀并且沒有配置更改的時(shí)候,會將viewModelStore中的數(shù)據(jù) 進(jìn)行清除操作。

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

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

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