翻譯官方文檔ViewModel

官方文檔鏈接:https://developer.android.google.cn/topic/libraries/architecture/viewmodel.html

1.前言


LiveData雖然解決了數(shù)據(jù)變化的及時反饋,但由于是在組件中創(chuàng)建LiveData對象的,所以當組件生命周期結(jié)束,此對象便會釋放,這將導致哪些問題呢?下面來講一講。

2.使用案例


ViewModel類被設計用來存儲和管理界面相關的數(shù)據(jù),所以屏幕旋轉(zhuǎn)等配置更改前的數(shù)據(jù)可以被保存。像Activity和Fragment等應用組件由安卓系統(tǒng)管理它們的生命周期,基于一些用戶行為或用戶完全無法控制的設備事件來決定銷毀或重建它們。

既然上述對象由操作系統(tǒng)銷毀或重建,它們持有的任何數(shù)據(jù)都有可能丟失。例如,在Activity中顯示用戶數(shù)據(jù)的列表,當界面由于配置更改重建時,新的Activity需要重新獲取這些數(shù)據(jù)。對于簡單的數(shù)據(jù),Activity能通過onSaveInstanceState()方法存儲,再從onCreate()方法的Bundle參數(shù)中恢復數(shù)據(jù),但是這種方法只適用于像界面狀態(tài)這樣的少量數(shù)據(jù),不適用于像用戶列表這樣的大量數(shù)據(jù)。

另一個問題,這些UI Controller(Activity、Fragment等)經(jīng)常需要進行一些異步調(diào)用,可能會消耗一些時間來返回結(jié)果。這就導致必須管理這些調(diào)用,當UI Controller銷毀時清理它們,以避免潛在的內(nèi)存泄漏。這需要大量的維護,并且在配置更改導致重建對象的情況下,會再次發(fā)出相同的調(diào)用,這是浪費資源的。

還有個重要情況,這些UI Controller已經(jīng)需要響應用戶操作和處理操作系統(tǒng)的通信。若還需手動處理自己的資源,會導致類膨脹;即,一個類試圖自己處理整個應用的工作,而不是委派給其它類。這也導致測試更加困難。

將視圖數(shù)據(jù)的持有從UI Controller的邏輯中分離出來會使結(jié)構(gòu)更加簡單和高效。Lifecycles提供了ViewModel類,用來協(xié)助UI Controller,負責給界面準備數(shù)據(jù)。當配置改變時,它會自動持有數(shù)據(jù),這樣能立刻提供有效的數(shù)據(jù)給新的Activity或Fragment實例。拿上面的例子來說,應該由ViewModel負責獲取和持有用戶列表,而不是Activity或Fragment。

public class MyViewModel extends ViewModel {
    private MutableLiveData<List<User>> users;
    public LiveData<List<User>> getUsers() {
        if (users == null) {
            users = new MutableLiveData<List<Users>>();
            loadUsers();
        }
        return users;
    }

    private void loadUsers() {
        // do async operation to fetch users
    }
}

Activity可以通過如下方法訪問列表:

public class MyActivity extends AppCompatActivity {
    public void onCreate(Bundle savedInstanceState) {
        MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);
        model.getUsers().observe(this, users -> {
            // update UI
        });
    }
}

如果MyActivity被重建了,它將會接收到之前MyActivity創(chuàng)建的MyViewModel實例。當持有的Activity正常結(jié)束時,系統(tǒng)調(diào)用ViewModel的onCleared()方法來釋放資源。

因為ViewModel的生命周期比對應的Activity和Fragment實例要長,所以它不應該引用一個View或任何一個可能持有Activity上下文引用的類。如果ViewModel需要Application的上下文(例如,去找到系統(tǒng)服務),它可以繼承AndroidViewModel類,并在構(gòu)造函數(shù)中接收Application(因為Application類繼承自Context類)。

3.在Fragment間共享數(shù)據(jù)


一個Activity中的兩個或多個Fragment需要相互通訊是種常見現(xiàn)象。但這種場景卻不簡單,所有的Fragment需要定義一些描述接口,并且所屬的Activity必須與它們綁定。此外,所有的Fragment都必須面對一種場景,那就是其它的Fragment還沒有創(chuàng)建或不可見。

上面的痛點可以使用ViewModel對象來解決。假設通過Fragment實現(xiàn)master-detail場景,即用戶在一個Fragment的列表中選擇某一項,在另一個Fragment中展示選擇項的內(nèi)容。這些Fragment共享一個擁有Activity作用域的ViewModel解決通信問題。

public class SharedViewModel extends ViewModel {
    private final MutableLiveData<Item> selected = new MutableLiveData<Item>();

    public void select(Item item) {
        selected.setValue(item);
    }

    public LiveData<Item> getSelected() {
        return selected;
    }
}

public class MasterFragment extends Fragment {
    private SharedViewModel model;
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
        itemSelector.setOnClickListener(item -> {
            model.select(item);
        });
    }
}

public class DetailFragment extends LifecycleFragment {
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
        model.getSelected().observe(this, { item ->
           // update UI
        });
    }
}

所有的Fragment在獲取ViewModelProvider對象時,使用getActivity()方法。這意味著它們將會接收到相同的SharedViewModel實例,即作用域為Activity

這樣做的好處有:

  • Activity不需要做任何事情,或者知道關于此次通信的任何事情。
  • 除了SharedViewModel類,F(xiàn)ragment不需要知道彼此相關的細節(jié)。如果其中一個Fragment消失了,其它的仍保持正常工作。
  • 每個Fragment都有自己的生命周期,不會受到其它的生命周期的影響。實際上,界面中一個Fragment替換另一個,對界面工作不會有任何影響。

4.ViewModel的生命周期


ViewModel對象的作用域取決于,通過ViewModelProvider獲取ViewModel對象時,傳入的Lifecycle的生命周期范圍。它將一直呆在內(nèi)存中直到Lifecycle的生命周期永久地結(jié)束了—如果是Activity,當它finished;如果是Fragment,當它detached。

Lifecycle.png

5.ViewModel與SavedInstanceState區(qū)別


當配置更改后,ViewModel提供了一種方便的途徑恢復數(shù)據(jù),但它不支持持久化。若應用被操作系統(tǒng)killed,將丟失數(shù)據(jù)。這時,可以使用系統(tǒng)早就提供的另一套機制,具體可以看這篇文章

例如,用戶將應用放到后臺,幾小時后再切換回前臺,進程那時將被killed,并且安卓系統(tǒng)將從保存的狀態(tài)中恢復Activity。此過程中,開發(fā)者基本不需要做任何事情,因為所有的系統(tǒng)組件(View,Activity,F(xiàn)ragment)使用saved instance state機制保存它們的狀態(tài)。可以在onSaveInstanceState()回調(diào)方法中,給參數(shù)Bundle添加自定義數(shù)據(jù)。

通過onSaveInstanceState()方法存儲的數(shù)據(jù)將保持在系統(tǒng)進程內(nèi)存中,但安卓系統(tǒng)只允許存儲很少的數(shù)據(jù),所以這不是保存應用實際數(shù)據(jù)的好地方。對于不能通過UI組件輕松地展示出來的東西需謹慎使用。

例如,如果有一個展示某國家信息的用戶界面,不應該將Country對象放入saved instance state,應該將countryId放入存儲的狀態(tài)(除非已經(jīng)被View或Fragment的arguments存儲)。實際對象應該保存在數(shù)據(jù)庫,并讓ViewModel能通過保存的countryId檢索到。

6.總結(jié)


到此,AAC最常用的功能基本介紹完了。至于Room Persistence LibraryPaging Library,前者有大量可替代的庫,而后者不太穩(wěn)定,所以有時間再詳細介紹。由于現(xiàn)在AAC還沒有出穩(wěn)定版,大家可以多關注官網(wǎng)發(fā)布的更新進度,有問題也可以在這里反饋。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,117評論 6 537
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,860評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,128評論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,291評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,025評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,421評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,477評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,642評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,177評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,970評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,157評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,717評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,410評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,821評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,053評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,896評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,157評論 2 375