Android Architecture Components 之 Lifecycle、LiveData诬辈、ViewModel

本文僅是閱讀Android Architecture Components的個人總結(jié)酵使。

  • Android App開發(fā)的痛點(diǎn)

在開發(fā)AndroidApp時,由于系統(tǒng)的UI Controller(Activity焙糟、Fragment)Component能夠被獨(dú)立啟動并且是無順序的口渔,他們的生命周期不受開發(fā)者的控制,因此當(dāng)我們的數(shù)據(jù)依賴這些組件的生命周期時就會發(fā)生許多問題穿撮。

Android官方指出一個重要的開發(fā)原則是:我們不應(yīng)該把不是處理UI和系統(tǒng)事件的代碼寫在UI Controller中缺脉。

Any code that does not handle a UI or operating system interaction should not be in these classes. Keeping them as lean as possible will allow you to avoid many lifecycle related problems. Don't forget that you don't own those classes, they are just glue classes that embody the contract between the OS and your app.

官方指出第二個重要的原則是drive your UI from a model

Architecture Components就是遵守上面兩個原則的框架,它保證我們app可以很好的響應(yīng)系統(tǒng)組件的生命周期悦穿,不出現(xiàn)混亂的情況攻礼。同時drive your UI from a model

Handling Lifecycles with Lifecycle-Aware Components

這個其實(shí)原理是很簡單的:UI Controller(Activity、Fragment)是一個LifecycleOwner,它的生命周期時可以被觀察的栗柒。我們可以向它們上面注冊LifecycleObserver礁扮,觀察生命周期事件。簡單代碼如下:

創(chuàng)建一個LifecycleObserver:

public class MainPresenter implements LifecycleObserver {

    @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
    void initView() {
        Log.e(TAG, "init View");
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
    void initData() {
        Log.e(TAG, "initData");
    }

}

LifecycleOwner注冊LifecycleObserver:

public class MainActivity extends AppCompatActivity implements MainView {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState)
        getLifecycle().addObserver(new MainPresenter());
    }

note: 在Support Library 26.1.0, AppCompatActivityFragment 都已經(jīng)實(shí)現(xiàn)LifecycleOwner

對于上面這個sample傍衡,可以說簡單的把UI Controller的生命周期相關(guān)方法dispatch給其他組件處理深员,使代碼看起來非常清晰。

LiveData

先看一下使用LiveData編碼的優(yōu)勢:

  • 確保UI狀態(tài)和數(shù)據(jù)狀態(tài)是同步的蛙埂。
  • 沒有內(nèi)存泄漏
  • 不會由于Activity的Stop而引起crash
  • 不需要手動處理組件生命周期
  • 不會由于系統(tǒng)config改變而保持?jǐn)?shù)據(jù),比如屏幕旋轉(zhuǎn)

LiveData是一個包裹數(shù)據(jù)并且可以被觀察的對象遮糖,它可以感知App組件的生命周期(Activity Fragment Service)绣的。這個特性使:LiveData可以在組件活躍的狀態(tài)(在生命周期內(nèi))下更新數(shù)據(jù)

活躍狀態(tài)是指STARTorRESUMED。即LiveData在數(shù)據(jù)發(fā)生改變時只把這個改變事件通知給活躍的觀察者,對于不活躍的觀察者是不會通知的屡江。

在給LiveData注冊Observer時芭概,可以把LifecycleOwnerLiveData組成一個配對關(guān)系。這樣在LifecycleOwner不活躍時惩嘉,Observer會被移除罢洲。這樣就保證像Activity Fragment在觀察LiveData變化時是非常安全的,即在不活躍時文黎,是不會響應(yīng)LiveData的變化的惹苗。

下面看一下如何使用:

public class NameViewModel extends ViewModel {

    // Create a LiveData with a String
    private MutableLiveData<String> mCurrentName;
    
    public MutableLiveData<String> getCurrentName() {
        if (mCurrentName == null) {
            mCurrentName = new MutableLiveData<String>();
        }
        return mCurrentName;
    }
}

MutableLiveData是一個LiveData,是一個可被觀察的對象。官方是推薦把LiveData寫在ViewModel中的:

  • 可以避免Activity和Fragment的臃腫耸峭,這些UI Controller只是響應(yīng)UI的變化桩蓉,而不會持有UI的狀態(tài)
  • 解耦LiveData和UIController
    對于ViewModel將會在下面介紹。

通過observer()方法將LifecycleOwner和LiveData建立聯(lián)系:


public class NameActivity extends AppCompatActivity {

    private NameViewModel mModel;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Get the ViewModel.
        mModel = ViewModelProviders.of(this).get(NameViewModel.class);

        // Create the observer which updates the UI.
        final Observer<String> nameObserver = new Observer<String>() {
            @Override
            public void onChanged(@Nullable final String newName) {
                // Update the UI, in this case, a TextView.
                mNameTextView.setText(newName);
            }
        };
});

        // Observe the LiveData, passing in this activity as the LifecycleOwner and the observer.
        mModel.getCurrentName().observe(this, nameObserver);
    }
}

上面把一個Observer加入到LiveData的觀察者列表劳闹,如果Oberver對應(yīng)的LifeOwner(UI Controller)不活躍了院究,這個Observer將會被移除。

數(shù)據(jù)變化本涕,觸發(fā)活躍的LifeOwner更新UI,即調(diào)用ObserveronChanged()方法业汰。

mButton.setOnClickListener(new OnClickListener() {
    @Override
    public void onClick(View v) {
        String anotherName = "John Doe";
        mModel.getCurrentName().setValue(anotherName);
    }
});

ViewModel

這個類被設(shè)計用來管理和UI相關(guān)的數(shù)據(jù)(即LiveData),它允許數(shù)據(jù)感知系統(tǒng)的配置變化,比如屏幕旋轉(zhuǎn)菩颖。

ViewModel在發(fā)生屏幕旋轉(zhuǎn)事件是蔬胯,這個對象會被保留,如果此時UI Controller銷毀了(Activity)位他,在Activity實(shí)例重建的時候氛濒,這個對象是可以被重新獲得的,即我們可以不用在onSaveInstanceState()保存數(shù)據(jù)鹅髓,然后在onCreate()中恢復(fù)舞竿。

可以這樣獲得一個ViewModel

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 an asynchronous operation to fetch users.
    }
    
    @Override
    protected void onCleared() {
        super.onCleared();
    }
}

public class MyActivity extends AppCompatActivity {
    public void onCreate(Bundle savedInstanceState) {
        // Create a ViewModel the first time the system calls an activity's onCreate() method.
        // Re-created activities receive the same MyViewModel instance created by the first activity.

        MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);
        model.getUsers().observe(this, users -> {
            // update UI
        });
    }
}

ViewModel的生命周期如下:

viewmodel-lifecycle.png

即這個對象作用域直到Activity destroy。當(dāng)框架層面Activity被銷毀時窿冯,ViewModelonCleared方法會被調(diào)用骗奖。

官方建議:ViewModel不應(yīng)該引用任何UI Controller相關(guān)對象。它也不應(yīng)該關(guān)心任何UI Controller的生命周期變化醒串。

如果你需要一個 Application Context执桌,可以使用 AndroidViewModel

在多個Fragment中的應(yīng)用

在上面可以看出一個ViewModel其實(shí)適合LifecycleOwner綁定的,因此Activity下的Fragment可以獲得同一個ViewModel,即可以交流芜赌。

比如下面的代碼:

Notice that both fragments use getActivity() when getting the ViewModelProvider. As a result, both fragments receive the same SharedViewModel instance, which is scoped to the activity.

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 Fragment {
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
        model.getSelected().observe(this, { item ->
           // Update the UI.
        });
    }
}

總結(jié)

通過上面的介紹仰挣,我們可以通過Lifecycle、LiveData缠沈、ViewModel這三個對象寫出一個比較穩(wěn)定的app架構(gòu):代碼邏輯清晰,生命周期管理方便等優(yōu)點(diǎn)膘壶。

Demo地址:https://github.com/SusionSuc/ArchitectureComponentSmale

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末错蝴,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子颓芭,更是在濱河造成了極大的恐慌顷锰,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件亡问,死亡現(xiàn)場離奇詭異官紫,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)州藕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進(jìn)店門束世,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人慎框,你說我怎么就攤上這事良狈。” “怎么了笨枯?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵薪丁,是天一觀的道長。 經(jīng)常有香客問我馅精,道長严嗜,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任洲敢,我火速辦了婚禮漫玄,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘压彭。我一直安慰自己睦优,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布壮不。 她就那樣靜靜地躺著汗盘,像睡著了一般。 火紅的嫁衣襯著肌膚如雪询一。 梳的紋絲不亂的頭發(fā)上隐孽,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天,我揣著相機(jī)與錄音健蕊,去河邊找鬼菱阵。 笑死,一個胖子當(dāng)著我的面吹牛缩功,可吹牛的內(nèi)容都是我干的晴及。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼掂之,長吁一口氣:“原來是場噩夢啊……” “哼抗俄!你這毒婦竟也來了脆丁?” 一聲冷哼從身側(cè)響起世舰,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤动雹,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后跟压,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體胰蝠,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年震蒋,在試婚紗的時候發(fā)現(xiàn)自己被綠了茸塞。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡查剖,死狀恐怖钾虐,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情笋庄,我是刑警寧澤效扫,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站直砂,受9級特大地震影響菌仁,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜静暂,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一济丘、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧洽蛀,春花似錦摹迷、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至颂碘,卻和暖如春异赫,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背头岔。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工塔拳, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人峡竣。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓靠抑,卻偏偏與公主長得像,于是被迫代替她去往敵國和親适掰。 傳聞我的和親對象是個殘疾皇子颂碧,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評論 2 344