ViewModel源碼分析

一箕昭、ViewModel的簡(jiǎn)介

2018年谷歌IO大會(huì)上正式發(fā)布了JetPack組件灵妨,其中包括Databing、Lifecycles落竹、LiveData泌霍、Navigation、Paging述召、Room朱转、ViewModel、WorkManager等一系列框架积暖。并且發(fā)布androidx包藤为,這些框架的最新版本組件將引入到androidx包下。
JetPack架構(gòu)組件的發(fā)布呀酸,意味著google終于拿出了官方推薦的Android開發(fā)架構(gòu)凉蜂,一直以來在Android開發(fā)上老生常談三種開發(fā)模式琼梆,MVC性誉、MVP、MVVM茎杂。整體的思想就是做一些代碼封裝和操作上的分離错览,具體三種架構(gòu)的區(qū)別不在這詳細(xì)描述。說一點(diǎn)兒個(gè)人對(duì)這三中開發(fā)模式的理解煌往。
MVC:Activity承擔(dān)C的任務(wù)倾哺,強(qiáng)行carry大量業(yè)務(wù)和數(shù)據(jù)代碼。
MVP:Presenter承擔(dān)主要代碼任務(wù)刽脖,連接數(shù)據(jù)層和視圖層的交互羞海。
MVVM:Model負(fù)責(zé)數(shù)據(jù)、View負(fù)責(zé)展示曲管、ViewModel負(fù)責(zé)Model和View的交互却邓,同時(shí),最好的一點(diǎn)是View層自動(dòng)監(jiān)聽ViewModel的數(shù)據(jù)變化院水。
在JetPack組件出現(xiàn)之前腊徙,一直以來構(gòu)建MVVM架構(gòu)的方式都是代用Databing來充當(dāng)ViewModel简十,而Databing是采用標(biāo)簽方式寫入布局文件中的,這樣一來當(dāng)出現(xiàn)問題的時(shí)候不是太好進(jìn)行調(diào)試撬腾。所以ViewMode的出現(xiàn)對(duì)于MVVM模式開發(fā)具有重大的意義螟蝙。

二、ViewModel的用法和特點(diǎn)
ViewModel的使用是要結(jié)合LiveData框架進(jìn)行的民傻,LiveData框架也是JetPack組件的一部分胰默,這里先不詳細(xì)進(jìn)行介紹。ViewModel有兩個(gè)特點(diǎn)漓踢,一是更加方便的保存數(shù)據(jù)初坠,第二個(gè)特點(diǎn),也是最重要的特點(diǎn)彭雾,就是保證數(shù)據(jù)不受Activity的銷毀重建所影響碟刺,當(dāng)Activity銷毀重建后仍然能收到之前的數(shù)據(jù)。

1)繼承ViewModel

 class MainViewModel : ViewModel() {
    private val repertory: MainRepository by lazy { MainRepository() }
    var data: MutableLiveData<JsonBean> = MutableLiveData()
    fun getDataFromServer(){
    repertory.getDataFromServer(data)
     }
    }

ViewModel中持有LiveData薯酝,LiveData是ViewModel持有數(shù)據(jù)的載體半沽。

2)Activity通過ViewModelProviders獲取ViewModel

class MainActivity : AppCompatActivity() {
private lateinit var mModel: MainViewModel

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    initData()
}

private fun initData() {
    mModel = ViewModelProviders.of(this)[MainViewModel::class.java]
    mModel.data?.observe(this, Observer {
        val mainAdapter = MainAdapter(this, it)
        val linearLayoutManager = LinearLayoutManager(this)
        rv.layoutManager = linearLayoutManager
        rv.adapter = mainAdapter
    })
    mModel.getDataFromServer()
 }  
}

3)通過ViewModel中的方法獲取數(shù)據(jù)然后改變LiveData狀態(tài),通過響應(yīng)式的方式通知到Activity進(jìn)行視圖更新吴菠。用法上比較簡(jiǎn)單者填。

三、源碼分析

個(gè)人認(rèn)為對(duì)于一個(gè)框架的學(xué)習(xí)僅僅掌握框架的用法是不夠的做葵,因?yàn)檫@太簡(jiǎn)單了占哟,曾經(jīng)看到過一篇博客,里面有一句話說的很好酿矢,”如果你只會(huì)用這個(gè)框架榨乎,而不清楚其中的原理和設(shè)計(jì),那么即便這個(gè)框架性能再好瘫筐,再牛逼蜜暑,那也是寫框架的那個(gè)人牛逼,而不是你牛逼“策肝。我認(rèn)為這句話說的很好肛捍,所以當(dāng)我們學(xué)習(xí)一中新框架的時(shí)候,不能單單只會(huì)用之众,一定要清楚其中的原理拙毫,盡量去理解作者的設(shè)計(jì)思想,就像品茶一樣棺禾,這樣才能品出里面的滋味缀蹄。不扯閑篇了,由于作者能力也有限,所以以上和以下分析袍患,如有不同觀點(diǎn)或作者描述有誤請(qǐng)給予批評(píng)指正坦康,歡迎來噴。

1)先從獲取ViewModel的類入手诡延,”ViewModelProviders“從類名中也能分析出這個(gè)類是用于提供ViewModel的滞欠。源碼如下:

public class ViewModelProviders {
public ViewModelProviders() {
}

private static Application checkApplication(Activity activity) {
    Application application = activity.getApplication();
    if (application == null) {
        throw new IllegalStateException("Your activity/fragment is not yet attached to "
                + "Application. You can't request ViewModel before onCreate call.");
    }
    return application;
}

private static Activity checkActivity(Fragment fragment) {
    Activity activity = fragment.getActivity();
    if (activity == null) {
        throw new IllegalStateException("Can't create ViewModelProvider for detached fragment");
    }
    return activity;
}
@NonNull
@MainThread
public static ViewModelProvider of(@NonNull Fragment fragment) {
    ViewModelProvider.AndroidViewModelFactory factory =
            ViewModelProvider.AndroidViewModelFactory.getInstance(
                    checkApplication(checkActivity(fragment)));
    return new ViewModelProvider(ViewModelStores.of(fragment), factory);
}
@NonNull
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity) {
    ViewModelProvider.AndroidViewModelFactory factory =
            ViewModelProvider.AndroidViewModelFactory.getInstance(
                    checkApplication(activity));
    return new ViewModelProvider(ViewModelStores.of(activity), factory);
}
@NonNull
@MainThread
public static ViewModelProvider of(@NonNull Fragment fragment, @NonNull Factory factory) {
    checkApplication(checkActivity(fragment));
    return new ViewModelProvider(ViewModelStores.of(fragment), factory);
}
@NonNull
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity,
        @NonNull Factory factory) {
    checkApplication(activity);
    return new ViewModelProvider(ViewModelStores.of(activity), factory);
}
@SuppressWarnings("WeakerAccess")
@Deprecated
public static class DefaultFactory extends ViewModelProvider.AndroidViewModelFactory {
    /**
     * Creates a {@code AndroidViewModelFactory}
     *
     * @param application an application to pass in {@link AndroidViewModel}
     * @deprecated Use {@link ViewModelProvider.AndroidViewModelFactory} or
     * {@link ViewModelProvider.AndroidViewModelFactory#getInstance(Application)}.
     */
    @Deprecated
    public DefaultFactory(@NonNull Application application) {
        super(application);
    }
}
}

我們從中可以看到,這個(gè)類中的方法全是靜態(tài)的肆良,說明這個(gè)類相當(dāng)于一個(gè)工具類筛璧,從of方法中我們可以看到真正實(shí)現(xiàn)ViewModel創(chuàng)建的類是ViewModelProvider,而這個(gè)類的構(gòu)建又需要通過ViewModelStores的of方法獲取惹恃,ViewModelStores的代碼如下:

public class ViewModelStores {
private ViewModelStores() {
}

@NonNull
@MainThread
public static ViewModelStore of(@NonNull FragmentActivity activity) {
    if (activity instanceof ViewModelStoreOwner) {
        return ((ViewModelStoreOwner) activity).getViewModelStore();
    }
    return holderFragmentFor(activity).getViewModelStore();
}

@NonNull
@MainThread
public static ViewModelStore of(@NonNull Fragment fragment) {
    if (fragment instanceof ViewModelStoreOwner) {
        return ((ViewModelStoreOwner) fragment).getViewModelStore();
    }
    return holderFragmentFor(fragment).getViewModelStore();
}
}

從這個(gè)類中可以發(fā)現(xiàn)獲取ViewModelStore是通過構(gòu)建一個(gè)HoldFragment夭谤,而這個(gè)HoldFragment的作用就是持有ViewModelStore的引用,然后將這個(gè)Fragment添加到Activity中巫糙,而且這個(gè)Fragmet有一個(gè)重要的特點(diǎn)朗儒,在實(shí)例化的時(shí)候會(huì)調(diào)用setRetainInstance(true)方法,這個(gè)方法的作用是讓Fragment不受Activity銷毀重建影響参淹,這樣一來就能保證ViewModel不會(huì)由于Activity的銷毀重建導(dǎo)致數(shù)據(jù)丟失醉锄,這是ViewModel的一個(gè)重要特性。這種設(shè)計(jì)確實(shí)很巧妙浙值,HolderFragment的代碼如下:

@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public class HolderFragment extends Fragment implements ViewModelStoreOwner {
.......省略部分代碼

public HolderFragment() {
    setRetainInstance(true);
}

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    sHolderFragmentManager.holderFragmentCreated(this);
}
@Override
public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
}
@Override
public void onDestroy() {
    super.onDestroy();
    mViewModelStore.clear();
}
@NonNull
@Override
public ViewModelStore getViewModelStore() {
    return mViewModelStore;
}
/**
 * @hide
 */
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public static HolderFragment holderFragmentFor(FragmentActivity activity) {
    return sHolderFragmentManager.holderFragmentFor(activity);
}

/**
 * @hide
 */
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public static HolderFragment holderFragmentFor(Fragment fragment) {
    return sHolderFragmentManager.holderFragmentFor(fragment);
}
.....省略部分代碼
    private static HolderFragment createHolderFragment(FragmentManager fragmentManager) {
        HolderFragment holder = new HolderFragment();
        fragmentManager.beginTransaction().add(holder, HOLDER_TAG).commitAllowingStateLoss();
        return holder;
    }

    HolderFragment holderFragmentFor(FragmentActivity activity) {
        FragmentManager fm = activity.getSupportFragmentManager();
        HolderFragment holder = findHolderFragment(fm);
        if (holder != null) {
            return holder;
        }
        holder = mNotCommittedActivityHolders.get(activity);
        if (holder != null) {
            return holder;
        }

        if (!mActivityCallbacksIsAdded) {
            mActivityCallbacksIsAdded = true;
            activity.getApplication().registerActivityLifecycleCallbacks(mActivityCallbacks);
        }
        holder = createHolderFragment(fm);
        mNotCommittedActivityHolders.put(activity, holder);
        return holder;
    }

    HolderFragment holderFragmentFor(Fragment parentFragment) {
        FragmentManager fm = parentFragment.getChildFragmentManager();
        HolderFragment holder = findHolderFragment(fm);
        if (holder != null) {
            return holder;
        }
        holder = mNotCommittedFragmentHolders.get(parentFragment);
        if (holder != null) {
            return holder;
        }

        parentFragment.getFragmentManager()
                .registerFragmentLifecycleCallbacks(mParentDestroyedCallback, false);
        holder = createHolderFragment(fm);
        mNotCommittedFragmentHolders.put(parentFragment, holder);
        return holder;
    }
}

之后來到ViewModelProvider恳不,代碼量不是很多,但是里面的設(shè)計(jì)確實(shí)很有意思开呐。首先來分析它的成員變量烟勋,成員變量只有兩個(gè),F(xiàn)actory從字面的意思就能理解筐付,它是生產(chǎn)ViewModel的工廠卵惦,所以這里用到了工廠模式。ViewModelStore它用來存儲(chǔ)ViewModel家妆,相當(dāng)于ViewModel的緩存鸵荠。內(nèi)部是一個(gè)HashMap,key是ViewModel的類名稱伤极。ViewModelProvider代碼如下:

public class ViewModelProvider {
....省略部分代碼
private final Factory mFactory;
private final ViewModelStore mViewModelStore;

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

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) {
    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;
    .......省略部分代碼
}

2)Factory是ViewModelProvider中的一個(gè)接口,定義如下:

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

通過create方法創(chuàng)建ViewModel姨伤。Factory有兩個(gè)實(shí)現(xiàn)類AndroidViewModelFactory哨坪、NewInstanceFactory都是ViewModelProvider中的靜態(tài)內(nèi)部類。同時(shí)AndroidViewModelFactory又繼承于NewInstanceFactory乍楚。有一種依賴注入的思想在里面当编。接下來我們來分析兩個(gè)Factory的實(shí)現(xiàn)類。NewInstanceFactory最終通過newinstance方法創(chuàng)建ViewModel實(shí)例徒溪。

public static class NewInstanceFactory implements Factory {
    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);
        }
    }
}
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);
    }
}

最后忿偷,真正存儲(chǔ)ViewModel的容器是ViewModelStore金顿,ViewModelStore中維護(hù)了一個(gè)HashMap,代碼如下:

 public class ViewModelStore {

private final HashMap<String, ViewModel> mMap = new HashMap<>();

final void put(String key, ViewModel viewModel) {
    ViewModel oldViewModel = mMap.get(key);
    if (oldViewModel != null) {
        oldViewModel.onCleared();
    }
    mMap.put(key, viewModel);
}

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

}

文章代碼庫:https://github.com/24KWYL/MVVM.git

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末鲤桥,一起剝皮案震驚了整個(gè)濱河市揍拆,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌茶凳,老刑警劉巖嫂拴,帶你破解...
    沈念sama閱讀 217,542評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異贮喧,居然都是意外死亡筒狠,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門箱沦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來辩恼,“玉大人,你說我怎么就攤上這事谓形≡舜欤” “怎么了?”我有些...
    開封第一講書人閱讀 163,912評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵套耕,是天一觀的道長(zhǎng)谁帕。 經(jīng)常有香客問我,道長(zhǎng)冯袍,這世上最難降的妖魔是什么匈挖? 我笑而不...
    開封第一講書人閱讀 58,449評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮康愤,結(jié)果婚禮上儡循,老公的妹妹穿的比我還像新娘。我一直安慰自己征冷,他們只是感情好择膝,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著检激,像睡著了一般肴捉。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上叔收,一...
    開封第一講書人閱讀 51,370評(píng)論 1 302
  • 那天齿穗,我揣著相機(jī)與錄音,去河邊找鬼饺律。 笑死窃页,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播脖卖,決...
    沈念sama閱讀 40,193評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼乒省,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了畦木?” 一聲冷哼從身側(cè)響起袖扛,我...
    開封第一講書人閱讀 39,074評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎馋劈,沒想到半個(gè)月后攻锰,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,505評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡妓雾,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評(píng)論 3 335
  • 正文 我和宋清朗相戀三年娶吞,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片械姻。...
    茶點(diǎn)故事閱讀 39,841評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡妒蛇,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出楷拳,到底是詐尸還是另有隱情绣夺,我是刑警寧澤,帶...
    沈念sama閱讀 35,569評(píng)論 5 345
  • 正文 年R本政府宣布欢揖,位于F島的核電站陶耍,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏她混。R本人自食惡果不足惜烈钞,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望坤按。 院中可真熱鬧毯欣,春花似錦、人聲如沸臭脓。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽来累。三九已至砚作,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間佃扼,已是汗流浹背偎巢。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留兼耀,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,962評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像瘤运,于是被迫代替她去往敵國(guó)和親窍霞。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評(píng)論 2 354

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

  • 拆頁十六 今日內(nèi)容來自第一章每個(gè)人都應(yīng)該學(xué)會(huì)講的6個(gè)故事拯坟。 說明:在第一章里有6個(gè)故事但金,我們已經(jīng)圍繞前五個(gè)故事寫了...
    快樂幸福的央央閱讀 92評(píng)論 0 0
  • 易舒寧果味飲料 多種舒緩成分,放松身心 晚安郁季,好夢(mèng)冷溃! 1/3 的時(shí)間在睡眠中度過,孩子有10個(gè)小時(shí)以上在睡眠中度過...
    徐歡曙光閱讀 1,892評(píng)論 0 0
  • 今天早上聽著鬧鐘叫醒我起來,其實(shí)有點(diǎn)不情愿呢年柠,昨天忙了一天身體比較疲勞凿歼,但是呢按著自己的目標(biāo)還是起來了。一會(huì)就好了...
    瓊琚惠閱讀 173評(píng)論 0 0