JetPack之ViewModel組件

一酌呆、ViewModel的特點(diǎn):

  • ViewModel 可以實(shí)現(xiàn)Activity與fragment之間的數(shù)據(jù)共享,以及同一個(gè)Activity中多個(gè)Fragment之間的數(shù)據(jù)共享
  • ViewModel可以感知Activity的生命周期啼辣,actiivty ondestory前躏敢,會調(diào)用viewModel的onCleared()方法 供我們來釋放資源

二、viewModel是如何實(shí)現(xiàn) 數(shù)據(jù)共享和感知生命周期的

我們知道ViewModel 是一個(gè)抽象類岗照,自定義ViewModel是需繼承ViewModel類.

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

    /**
     * This method will be called when this ViewModel is no longer used and will be destroyed.
     * <p>
     * It is useful when ViewModel observes some data and you need to clear this subscription to
     * prevent a leak of this ViewModel.
     */
    @SuppressWarnings("WeakerAccess")
    protected void onCleared() {
    }

    @MainThread
    final void clear() {
        mCleared = true;
       
        //省略代碼...
        onCleared();
    }

TranslateViewModel.kt
如TranslateViewModel 繼承自ViewModel類

class TranslateViewModel constructor() : ViewModel(){
    
}

初始化如TranslateViewModel對象,卻不能直接使用new TranslateViewModel()來初始化該對象蚕钦。
需要調(diào)用ViewModelProviders.of(this).get(TranslateViewModel::class.java) 方法

錯(cuò)誤的初始化方法:

mViewModel = new TranslateViewModel()


正確的初始化方法:

mViewModel = ViewModelProviders.of(this).get(TranslateViewModel::class.java)

2.1 如何實(shí)現(xiàn)數(shù)據(jù)共享的

關(guān)鍵點(diǎn)就在于

 mViewModel = ViewModelProviders.of(this).get(TranslateViewModel::class.java)
2.1.1亭病、ViewModelProviders.of() 返回的是一個(gè)ViewModelProvider
public class ViewModelProviders {
  public static ViewModelProvider of(@NonNull FragmentActivity activity) {
        return of(activity, null);
    }

 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);
    }
}
2.1.2、ViewModelProvider用于提供一個(gè)ViewModel嘶居,構(gòu)建一個(gè)ViewModelProvider,需要兩個(gè)參數(shù)
  • ViewModelStore 提供ViewModel的存儲和復(fù)用機(jī)制
  • ViewModelProvider.Factory - ViewModel的生產(chǎn)工廠罪帖,用于生產(chǎn)一個(gè)新的ViewModel對象
  public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
        mFactory = factory;
        mViewModelStore = store;
    }

我們看一下ViewModelProvider.get()方法,此方法用戶返回一個(gè)viewModel對象

public class ViewModelProvider {
  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);
    }
    
     public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
        //關(guān)鍵代碼 
        ViewModel viewModel = mViewModelStore.get(key);

        if (modelClass.isInstance(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;
    }
}

可以發(fā)現(xiàn)

(1)會先去mViewModelStore 查找,有沒有XXX::class 對應(yīng)的ViewModel對象邮屁,如果有整袁,則直接返回
    ViewModel viewModel = mViewModelStore.get(key);

        if (modelClass.isInstance(viewModel)) {
            return (T) viewModel;
        } else {
            //noinspection StatementWithEmptyBody
            if (viewModel != null) {
                // TODO: log a warning.
            }
        }
(2)如果mViewModelStore中沒有 目標(biāo)ViewModel對象,則直接利用ViewModelProvider.Factory 生產(chǎn)一個(gè)新的ViewModel對象佑吝,同時(shí)保存到mViewModelStore中坐昙,以供下次復(fù)用。
 if (mFactory instanceof KeyedFactory) {
            viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
        } else {
            viewModel = (mFactory).create(modelClass);
        }
        mViewModelStore.put(key, viewModel);
2.1.3芋忿、ViewModelStore 用于存儲復(fù)用ViewModel對象炸客,

同時(shí)FragmentActivity 和Fragment都繼承了ViewModelStoreOwner ,可以獲取到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);
    }

    /**
     *  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();
    }
}
public interface ViewModelStoreOwner {
    /**
     * Returns owned {@link ViewModelStore}
     *
     * @return a {@code ViewModelStore}
     */
    @NonNull
    ViewModelStore getViewModelStore();
}

總結(jié):
至此原理清楚了戈钢。

  • 每個(gè)Activity或者Fragment當(dāng)中持有一個(gè)mViewModelStore嚷量,存儲和復(fù)用activity當(dāng)中用到的ViewModel
  • 創(chuàng)建ViewModel對象時(shí),如果傳入的是同一個(gè)Activity,那么ViewModelProvider中的ViewModelStore就是同一個(gè)對象逆趣。
  • ViewModelProvider調(diào)用get(XXXViewModel::class) ,獲取ViewModel時(shí),通過Activity的ViewModelStore實(shí)現(xiàn)了ViewModel的共享嗜历。

即 傳入的Activity是同一個(gè)的前提下宣渗,多次調(diào)用下面的方法抖所,獲取到的ViewModel是同一個(gè)對象。

 mViewModel = ViewModelProviders.of(this).get(TranslateViewModel::class.java)

2.2 如何感知Activity的生命周期的

首先明確一點(diǎn)痕囱,ViewModel只能感知activity生命周期中的onDestory()田轧。在activity銷毀前,執(zhí)行viewModel的clear(回調(diào)onCleared()鞍恢,進(jìn)行資源的釋放)傻粘。

那么我們來看activity中是在什么時(shí)候觸發(fā)viewModel的clear()操作的

public class FragmentActivity extends ComponentActivity implements XXX{
    
}

public class ComponentActivity extends androidx.core.app.ComponentActivity implements
        LifecycleOwner,
        ViewModelStoreOwner,
        SavedStateRegistryOwner,
        OnBackPressedDispatcherOwner {
        
        public ComponentActivity() {
        Lifecycle lifecycle = getLifecycle();
        //noinspection ConstantConditions
      
      
        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();
                    }
                }
            }
        });

        }
}

可以看到,在基類ComponentActivity的構(gòu)造方法中,注冊了LifeCycleObserver帮掉,檢測到Lifecycle.Event.ON_DESTROY事件之后弦悉,會自動調(diào)用getViewModelStore().clear()

public class ViewModelStore {
     public final void clear() {
            for (ViewModel vm : mMap.values()) {
                vm.onCleared();
            }
            mMap.clear();
        }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市蟆炊,隨后出現(xiàn)的幾起案子稽莉,更是在濱河造成了極大的恐慌,老刑警劉巖涩搓,帶你破解...
    沈念sama閱讀 211,817評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件污秆,死亡現(xiàn)場離奇詭異,居然都是意外死亡昧甘,警方通過查閱死者的電腦和手機(jī)良拼,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來充边,“玉大人庸推,你說我怎么就攤上這事⊥蠢瑁” “怎么了予弧?”我有些...
    開封第一講書人閱讀 157,354評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長湖饱。 經(jīng)常有香客問我掖蛤,道長,這世上最難降的妖魔是什么井厌? 我笑而不...
    開封第一講書人閱讀 56,498評論 1 284
  • 正文 為了忘掉前任蚓庭,我火速辦了婚禮,結(jié)果婚禮上仅仆,老公的妹妹穿的比我還像新娘器赞。我一直安慰自己,他們只是感情好墓拜,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,600評論 6 386
  • 文/花漫 我一把揭開白布港柜。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪夏醉。 梳的紋絲不亂的頭發(fā)上爽锥,一...
    開封第一講書人閱讀 49,829評論 1 290
  • 那天,我揣著相機(jī)與錄音畔柔,去河邊找鬼氯夷。 笑死,一個(gè)胖子當(dāng)著我的面吹牛靶擦,可吹牛的內(nèi)容都是我干的腮考。 我是一名探鬼主播,決...
    沈念sama閱讀 38,979評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼玄捕,長吁一口氣:“原來是場噩夢啊……” “哼踩蔚!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起桩盲,我...
    開封第一講書人閱讀 37,722評論 0 266
  • 序言:老撾萬榮一對情侶失蹤寂纪,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后赌结,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體捞蛋,經(jīng)...
    沈念sama閱讀 44,189評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,519評論 2 327
  • 正文 我和宋清朗相戀三年柬姚,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了拟杉。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,654評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡量承,死狀恐怖搬设,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情撕捍,我是刑警寧澤拿穴,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站忧风,受9級特大地震影響默色,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜狮腿,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,940評論 3 313
  • 文/蒙蒙 一腿宰、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧缘厢,春花似錦吃度、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,762評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春间护,著一層夾襖步出監(jiān)牢的瞬間删壮,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,993評論 1 266
  • 我被黑心中介騙來泰國打工兑牡, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人税灌。 一個(gè)月前我還...
    沈念sama閱讀 46,382評論 2 360
  • 正文 我出身青樓均函,卻偏偏與公主長得像,于是被迫代替她去往敵國和親菱涤。 傳聞我的和親對象是個(gè)殘疾皇子苞也,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,543評論 2 349

推薦閱讀更多精彩內(nèi)容