MVVM-live

MVVM-live

Google的項(xiàng)目: https://github.com/googlesamples/android-architecture/tree/mastertodo?mvvm

官方解釋:

Uses ViewModels and LiveData from Architecture Components and the Data Binding library with an MVVM architecture.

This version of the app is called todo-mvvm-live, and it uses some Architecture Components like ViewModel, LiveData, and other lifecycle-aware classes. It's based on the todo-mvvm-databinding sample, which uses the Data Binding Library to display data and bind UI elements to actions.

總結(jié):MVVM就包含了ViewModels及Data Binding杨蛋,所以mvvm?live = mvvm + LiveData

需要了解的概念有 Lifecycle-aware組件起便、MVVM、LiveData酥宴、ViewModel、Data binding library您觉。

以下是翻譯Google的文檔 + 源碼解釋拙寡。

Lifecycle-aware組件

lifecycle-aware組件能夠?qū)ζ渌M件(比如activity和fragment)發(fā)生生命周期改變時(shí)做出反應(yīng),這樣的組件顯然很容易使用琳水,輕量且易維護(hù)倒庵。

舉個(gè)例子:在UI上顯示定位信息

傳統(tǒng)做法是:

定義獲取位置的接口:
public class MyLocationListener {
    public MyLocationListener(Context context, LocCallback callback) {
        // ...
    }

    void start() {
        // connect to system location service
    }

    void stop() {
        // disconnect from system location service
    }

    public interface LocCallback {
        void loc(long lat, long lag);
    }
}

在activity里顯示位置信息,并在生命周期里控制接口:
public class MyActivity extends AppCompatActivity {

    MyLocationListener myLocationListener;

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState, @Nullable PersistableBundle persistentState) {
        super.onCreate(savedInstanceState, persistentState);

        myLocationListener = new MyLocationListener(this, new MyLocationListener.LocCallback() {
            @Override
            public void loc(long lat, long lag) {
                // update ui
            }
        });
    }

    @Override
    protected void onStart() {
        super.onStart();
        myLocationListener.start();
    }

    @Override
    protected void onStop() {
        super.onStop();
        myLocationListener.stop();
    }
}

這么做的問(wèn)題:

  1. 需要在生命周期方法里正確去回調(diào)接口方法炫刷,如果沒有正確回調(diào)有可能導(dǎo)致內(nèi)存泄漏擎宝。
  2. 并且可能在生命周期方法里有很多類似調(diào)用,導(dǎo)致代碼很難維護(hù)浑玛,丑陋绍申。
  3. 甚至有可能myLocationListener.stop()在start()之后調(diào)用,導(dǎo)致myLocationListener周期異常顾彰,例如:
@Override
protected void onStart() {
    super.onStart();
    Util.checkUserStatus(result -> {
        // what if this callback is invoked AFTER activity is stopped?
        if (result) {
            myLocationListener.start();
        }
    });
}
    
    @Override
    protected void onStop() {
        super.onStop();
        myLocationListener.stop();
    }

android.arch.lifecycle組件包能幫助避免這些問(wèn)題极阅。

下面詳細(xì)介紹。

Lifecycle

Lifecycle是一個(gè)類維護(hù)了組件(像activity或fragment)的生命周期信息涨享,并且允許這些信息被觀察者獲取筋搏。內(nèi)部使用2個(gè)enum類Event和State來(lái)跟蹤與之關(guān)聯(lián)的組件狀態(tài):

Event :從關(guān)聯(lián)的組件(activities 或 fragments)里發(fā)送過(guò)來(lái)的,表明組件已經(jīng)到了哪個(gè)生命周期了(如ON_CREATE厕隧、ON_START等)

State :Lifecycle對(duì)象跟蹤的關(guān)聯(lián)組件的狀態(tài)奔脐。

官方event與state關(guān)系圖

lifecycle_frag_activity圖

定義可以觀察組件生命周期的observer,用annotation方式:

public class MyObserver implements LifecycleObserver {
    @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
    public void connectListener() {
        //...
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
    public void disconnectListener() {
        //...
    }
}

在外部通過(guò)myLifecycleOwner.getLifecycle().addObserver(new MyObserver()) 讓MyObserver觀察組件的生命周期吁讨。

那什么是LifecycleOwner髓迎?

LifecycleOwner

定義:

public interface LifecycleOwner {
    /**
     * Returns the Lifecycle of the provider.
     *
     * @return The lifecycle of the provider.
     */
    @NonNull
    Lifecycle getLifecycle();
}

只是一個(gè)接口,表明實(shí)現(xiàn)者有Lifecycle建丧。v4包里的fragemnt和activity都實(shí)現(xiàn)了這個(gè)排龄,也可以實(shí)現(xiàn)自己的LifecycleOwner。
前面顯示定位信息的例子可以改為:

public class MyLocationListener2 implements LifecycleObserver {
    private boolean enabled = false;
    private Lifecycle lifecycle;
    private WeakReference<LocCallback> callbackWeakReference;  // 弱引用

    public MyLocationListener2(Context context, Lifecycle lifecycle, LocCallback callback) {
       // ...
       callbackWeakReference = new WeakReference<LocCallback>(callback);
    }

    public void enable() {  // 提供enable方法翎朱,更安全
        enabled = true;
        if (lifecycle.getCurrentState().isAtLeast(STARTED)) {
            // connect if not connected
        }
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    void start() {
        if (enabled) {
            // connect
        }
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    void stop() {
        // disconnect if connected
    }

    public interface LocCallback {
        void onLoc(long lat, long lag);
    }
}

public class MyActivity2 extends AppCompatActivity {

    MyLocationListener2 myLocationListener;

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState, @Nullable PersistableBundle persistentState) {
        super.onCreate(savedInstanceState, persistentState);

        myLocationListener = new MyLocationListener2(this, getLifecycle(), new MyLocationListener2.LocCallback() {
            @Override
            public void onLoc(long lat, long lag) {
                // update ui
            }
        });
    }

    @Override
    protected void onStart() {
        super.onStart();
        Util.checkUserStatus(result -> {
            if (result) {
                myLocationListener.enable();
            }
        });
    }
}

這個(gè)MyLocationListener2就是一個(gè)lifecycle-aware的組件橄维。

這么做的好處是:

  1. 讓MyLocationListener2自己觀察生命周期的變化尺铣,不需要外部組件回調(diào),外部組件需需要初始化它就好争舞。
  2. 避免了潛在的泄漏問(wèn)題
  3. MyLocationListener2非常獨(dú)立迄埃,很好復(fù)用。

Google官方的建議:

If a library provides classes that need to work with the Android lifecycle, we recommend that you use lifecycle-aware components. Your library clients can easily integrate those components without manual lifecycle management on the client side.

就是說(shuō)如果你的library需要跟生命周期有關(guān)系兑障,那就實(shí)現(xiàn)成lifecycle-aware的組件侄非,這樣外部就很容易集成而不用管理它的生命周期。

自定義LifecycleOwner

V4包的Support Library 26.1.0版本及以上的Fragment與Activitie已經(jīng)實(shí)現(xiàn)了LifecycleOwner流译,也可以自己實(shí)現(xiàn)逞怨,同樣借助LifecycleRegistry。

public class MyActivity extends Activity implements LifecycleOwner {
    private LifecycleRegistry mLifecycleRegistry;

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

        mLifecycleRegistry = new LifecycleRegistry(this);
        mLifecycleRegistry.markState(Lifecycle.State.CREATED);
    }

    @Override
    public void onStart() {
        super.onStart();
        mLifecycleRegistry.markState(Lifecycle.State.STARTED);
    }

    @NonNull
    @Override
    public Lifecycle getLifecycle() {
        return mLifecycleRegistry;
    }
}

MVVM

維基百科的解釋: mvvm

關(guān)鍵點(diǎn):

  • Model 代表domain model或者數(shù)據(jù)訪問(wèn)層福澡。
  • View 代表用戶在界面上能看到的UI叠赦,如layout,fragment革砸,各種組件除秀。(我認(rèn)為activity也是,在MVVM框架里算利,activity被弱化了册踩,只作為view的承載著和生命周期的體現(xiàn)者)。
  • View model 是view的抽象效拭,暴露公開的屬性和命令暂吉。MVVM有binder。在view model里缎患,binder協(xié)調(diào)著view和data binder慕的,view model可以被看成是model層數(shù)據(jù)的一種狀態(tài)。
  • Binder 把view model里的數(shù)據(jù)綁定到view上的庫(kù)挤渔。

LiveData

參考google官方解釋: architecture-livedata
源碼里L(fēng)iveData的解釋很棒肮街!

其實(shí)LiveData就是lifecycle-aware組件的增強(qiáng)版,其內(nèi)部實(shí)現(xiàn)了LifecycleObserver判导。(LifecycleBoundObserver實(shí)現(xiàn)的)

    @MainThread
    public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer) {
        ... ...
        LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
        ... ...
        owner.getLifecycle().addObserver(wrapper);
    }

關(guān)鍵點(diǎn)

  • 是可被觀察的data holder
  • 是lifecycle-aware的嫉父,就是說(shuō)它可感知組件(如activities, fragments, services)的生命周期。并且它只更新處于active狀態(tài)的觀察者組件骡楼。
  • active狀態(tài)是指生命周期處于 STARTED 或者 RESUMED 狀態(tài)熔号,LiveData只通知active的觀察者,非active的觀察者得不到通知鸟整。
  • STARTED的解釋RESUMED的解釋

優(yōu)點(diǎn)

  • 確保UI跟數(shù)據(jù)狀態(tài)匹配

LiveData notifies Observer objects when the lifecycle state changes. You can consolidate your code to update the UI in these Observer objects朦蕴。LiveData notifies Observer objects when the lifecycle state changes.

  • 沒有內(nèi)存泄漏

Observers are bound to Lifecycle objects and clean up after themselves when their associated lifecycle is destroyed.

  • 不會(huì)因?yàn)閍ctivity stop掉而crash

If the observer's lifecycle is inactive, such as in the case of an activity in the back stack, then it doesn’t receive any LiveData events.

  • 自動(dòng)處理由于生命周期改變的影響

UI components just observe relevant data and don’t stop or resume observation. LiveData automatically manages all of this since it’s aware of the relevant lifecycle status changes while observing.

  • 在active時(shí)篮条,observer總是收到最新的數(shù)據(jù)

If a lifecycle becomes inactive, it receives the latest data upon becoming active again. For example, an activity that was in the background receives the latest data right after it returns to the foreground.

  • 對(duì)configuration change的處理

If an activity or fragment is recreated due to a configuration change, like device rotation, it immediately receives the latest available data.

  • 在app內(nèi)共享資源

You can extend a LiveData object using the singleton pattern to wrap system services so that they can be shared in your app. The LiveData object connects to the system service once, and then any observer that needs the resource can just watch the LiveData object. For more information, see Extend LiveData 弟头。通過(guò)下面的ViewModel也可以做到。

怎么使用LiveData:

  1. 在ViewModel里創(chuàng)建LiveData
public class NameViewModel extends AndroidViewModel {

    // Create a LiveData with a String
    private MutableLiveData<String> curName; // LiveData是data的封裝涉茧,LiveData通常是放在ViewModel里赴恨,通過(guò)getter被獲取

    public NameViewModel(@NonNull Application application) {
        super(application);
    }

    public MutableLiveData<String> getCurName() {
        if (curName == null) {
            curName = new MutableLiveData();
        }
        return curName;
    }

    public void requestFromServer(String params) {
        // make the request

        // set the response data
        curName.setValue("LiBo");// setValue()需要再主線程調(diào)用
//        curName.postValue("liBo");// postValue()可以在子線程調(diào)用
    }
}
  1. 在lifecycle owner的組件(比如activity、fragment)里observe live data
public class MvvmTestActivity extends AppCompatActivity {

    private TextView nameTv;

    private NameViewModel nameViewModel;

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState, @Nullable PersistableBundle persistentState) {
        super.onCreate(savedInstanceState, persistentState);

        // Get the ViewModel.
        nameViewModel = 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 String s) {
                // Update the UI
                nameTv.setText(s);
            }
        };

        // Observe the LiveData, passing in this activity as the LifecycleOwner and the observer.
        nameViewModel.getCurName().observe(this, nameObserver);// 第一個(gè)參數(shù)是 LifecycleOwner對(duì)象
//        nameViewModel.getCurName().observeForever(nameObserver); // observer is considered to be always active and is therefore always notified about modifications
    }

}

需要明白的幾點(diǎn):

  • 在viewmodel里而不是fragment或activity管理livadata對(duì)象伴栓,因?yàn)椋ū苊鈌ragment或activitydiamante臃腫伦连,避免congiguration change銷毀livadata)。解釋-1
  • 當(dāng)更新LiveData里的value時(shí)钳垮,會(huì)觸發(fā)所有活著的observer(其所關(guān)聯(lián)的LifecycleOwner在active狀態(tài))惑淳。 解釋-2
  • 如果observer從inactione變成active狀態(tài),也會(huì)受到推送的數(shù)據(jù)饺窿。 解釋-3
  • 調(diào)用observe(this, nameObserver)就意味著nameObserver跟MvvmTestActivity這個(gè)lifecycle owner產(chǎn)生關(guān)聯(lián)歧焦,其實(shí)是跟activity關(guān)聯(lián)的lifecycle對(duì)象LifecycleRegistry關(guān)聯(lián)的。當(dāng)lifecycle對(duì)象不是active狀態(tài)時(shí)肚医,nameObserver 不會(huì)接收到更新绢馍,當(dāng)lifecycle對(duì)象被銷毀時(shí),nameObserver自動(dòng)從livedata里移除肠套。

解釋:

  • 1
    見下面 ' ViewModel為什么能夠苯⒂浚活?'
  • 2
LiveData.java

@MainThread
protected void setValue(T value) {
    assertMainThread("setValue");
    mVersion++;   // 記錄數(shù)據(jù)版本你稚,很重要
    mData = value;
    dispatchingValue(null);  // 會(huì)調(diào)用 considerNotify()
}

private void considerNotify(ObserverWrapper observer) {
        if (!observer.mActive) {
            return;
        }
        // Check latest state b4 dispatch. Maybe it changed state but we didn't get the event yet.
        //
        // we still first check observer.active to keep it as the entrance for events. So even if
        // the observer moved to an active state, if we've not received that event, we better not
        // notify for a more predictable notification order.
        if (!observer.shouldBeActive()) {
            observer.activeStateChanged(false); 
            return;
        }
        // 如果observer接受的數(shù)據(jù)版本大于等于目前版本舵稠,就不用通知observer了。
        if (observer.mLastVersion >= mVersion) {
            return;
        }
        observer.mLastVersion = mVersion;
        //noinspection unchecked
        observer.mObserver.onChanged((T) mData); // 通知obserser
    }
  • 3
在LiveData.observe()里調(diào)用了owner.getLifecycle().addObserver(wrapper) 把obserser關(guān)聯(lián)到了owner的lifecycle入宦。
當(dāng)owner即activity或fragment state狀態(tài)發(fā)生改變時(shí)哺徊,會(huì)調(diào)用LifecycleRegistry.markState()或handleLifecycleEvent(),
再調(diào)用sync()乾闰,最后會(huì)遍歷所有關(guān)聯(lián)的observer落追,并調(diào)用其 onStateChanged(),

在LiveData.java
@Override
public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
    if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
        removeObserver(mObserver);   // 如果lifecycle owner已經(jīng)是DESTROYED狀態(tài)涯肩,就移除該observer
        return;
    }
    activeStateChanged(shouldBeActive()); // 
}

void activeStateChanged(boolean newActive) {
    if (newActive == mActive) {
        return;
    }
    // immediately set active state, so we'd never dispatch anything to inactive owner
    mActive = newActive;
    boolean wasInactive = LiveData.this.mActiveCount == 0;
    LiveData.this.mActiveCount += mActive ? 1 : -1;  // mActiveCount記錄活著的observer個(gè)數(shù)
    if (wasInactive && mActive) {
        onActive();
    }
    if (LiveData.this.mActiveCount == 0 && !mActive) {
        onInactive();
    }
    if (mActive) {
        dispatchingValue(this); // 上面解釋過(guò)
    }
}

Transform LiveData

一些高級(jí)的用法轿钠,待更新

Merge multiple LiveData sources

一些高級(jí)的用法,待更新

ViewModel

參考官方 ViewModel
源碼里ViewModel的解釋很棒病苗!

設(shè)計(jì)ViewModel的目的是存儲(chǔ)及管理UI相關(guān)的數(shù)據(jù)同時(shí)關(guān)注其生命周期疗垛,能保證數(shù)據(jù)在configuration change(比如切屏)時(shí)存活,還有作為fragment硫朦,activity與其他的邏輯的通信橋梁贷腕。

考慮以下情況:

  • 當(dāng)configuration change時(shí),activity被銷毀并重建,雖然可以通過(guò)onSaveInstanceState()保存數(shù)據(jù)泽裳,但是只可以保存少量的可序列化的數(shù)據(jù)瞒斩;

  • 還有ui controller(activity、fragment等)需要管理一些異步調(diào)用并保證在適當(dāng)時(shí)清理掉 以避免內(nèi)存泄漏涮总,這些管理工作很麻煩胸囱;

  • UI controller應(yīng)該只是顯示數(shù)據(jù)到UI、相應(yīng)用戶操作瀑梗、處理系統(tǒng)的交互如處理permission請(qǐng)求烹笔。

使用ViewModel可以把數(shù)據(jù)及處理從Ui controller分離。

怎么使用ViewModel

繼承ViewModel抛丽,ViewModel的對(duì)象可以在configuration change時(shí)自動(dòng)卑埃活,其保存的數(shù)據(jù)可以立即被下一個(gè)activity或fragment實(shí)例使用铺纽。
public class MyViewModel extends ViewModel {
    private MutableLiveData<List<User>> users;
    public LiveData<List<User>> getUsers() {
        if (users == null) {
            users = new MutableLiveData<List<User>>();
            loadUsers();
        }
        return users;
    }

    private void loadUsers() {
        // Do an asynchronous operation to fetch users.
    }
}

class User {

}

注意:ViewModel一定不要引用一個(gè)view柬帕、lifecycle或任何會(huì)引用activity context的類。 ViewModel被設(shè)計(jì)用來(lái)獨(dú)立與view或LifecycleOwner而苯泼牛活數(shù)據(jù)的陷寝。ViewModel可以維護(hù)LifecycleObserver例如LiveData,但是不能observe lifecycle-aware的被觀察者例如LiveData其馏。如果需要application context凤跑,可以繼承AndroidViewModel。

ViewModel的生命周期

ViewModel對(duì)象一直存活直到關(guān)聯(lián)的lifecycle永遠(yuǎn)失效叛复,例如 activity 被finish掉(注意不是onDestroy()被調(diào)用)或者fragment被detatched了仔引。生命周期圖
驗(yàn)證一下:

public class MainViewModel extends AndroidViewModel {
    private int count = 0; // 只是用于activity更新以在某時(shí)做finish操作

    public MainViewModel(@NonNull Application application) {
        super(application);
    }

    @Override
    protected void onCleared() {
        super.onCleared();
        Log.i("lbtest", "MainViewModel onCleared");
    }

    public void updateCount() {
        count++;
    }

    public int getCount() {
        return count;
    }
}

private MainViewModel obtainViewModel() {
        MainViewModel vm = ViewModelProviders.of(this)
                .get(MainViewModel.class);
        return vm;
    }

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.i("lbtest", "onCreate this="+this+", vm="+obtainViewModel());
    }

    @Override
    public void onResume() {
        super.onResume();
        MainViewModel viewModel = obtainViewModel();
        Log.i("lbtest", "onResume this="+this+", vm="+viewModel);
        viewModel.updateCount();
        if (viewModel.getCount() == 2) {
            Log.i("lbtest", "onResume finish this="+this);
            finish();
        }
    }

    @Override
    public void onPause() {
        super.onPause();
        Log.i("lbtest", "onPause this="+this+", vm="+obtainViewModel());
    }

    @Override
    public void onStop() {
        super.onStop();
        Log.i("lbtest", "onStop this="+this+", vm="+obtainViewModel());
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.i("lbtest", "onDestroy this="+this/*+", vm="+obtainViewModel()*/);
    }

運(yùn)行結(jié)果:

04-03 14:33:50.828 30546-30546/? I/lbtest: onCreate this=com.lb.legor.MainActivity@50deb83, vm=com.lb.legor.MainViewModel@e6d758a
04-03 14:33:50.832 30546-30546/? I/lbtest: onResume this=com.lb.legor.MainActivity@50deb83, vm=com.lb.legor.MainViewModel@e6d758a
// 切屏操作
04-03 14:33:57.558 30546-30546/com.lb.legor I/lbtest: onPause this=com.lb.legor.MainActivity@50deb83, vm=com.lb.legor.MainViewModel@e6d758a
04-03 14:33:57.560 30546-30546/com.lb.legor I/lbtest: onStop this=com.lb.legor.MainActivity@50deb83, vm=com.lb.legor.MainViewModel@e6d758a
04-03 14:33:57.560 30546-30546/com.lb.legor I/lbtest: onDestroy this=com.lb.legor.MainActivity@50deb83
// 新的activity對(duì)象創(chuàng)建了,viewmodel對(duì)象不變
04-03 14:33:57.577 30546-30546/com.lb.legor I/lbtest: onCreate this=com.lb.legor.MainActivity@390bb73, vm=com.lb.legor.MainViewModel@e6d758a
04-03 14:33:57.582 30546-30546/com.lb.legor I/lbtest: onResume this=com.lb.legor.MainActivity@390bb73, vm=com.lb.legor.MainViewModel@e6d758a
04-03 14:33:57.582 30546-30546/com.lb.legor I/lbtest: onResume finish this=com.lb.legor.MainActivity@390bb73 // 主動(dòng)去finish()
04-03 14:33:57.589 30546-30546/com.lb.legor I/lbtest: onPause this=com.lb.legor.MainActivity@390bb73, vm=com.lb.legor.MainViewModel@e6d758a
04-03 14:33:57.658 30546-30546/com.lb.legor I/lbtest: onStop this=com.lb.legor.MainActivity@390bb73, vm=com.lb.legor.MainViewModel@e6d758a
04-03 14:33:57.658 30546-30546/com.lb.legor I/lbtest: MainViewModel onCleared // viewmodel對(duì)象被清
04-03 14:33:57.659 30546-30546/com.lb.legor I/lbtest: onDestroy this=com.lb.legor.MainActivity@390bb73
// 重新進(jìn)入app褐奥,肯定都是新的對(duì)象
04-03 14:36:28.277 30546-30546/com.lb.legor I/lbtest: onCreate this=com.lb.legor.MainActivity@c0db1d, vm=com.lb.legor.MainViewModel@34e80ea
04-03 14:36:28.280 30546-30546/com.lb.legor I/lbtest: onResume this=com.lb.legor.MainActivity@c0db1d, vm=com.lb.legor.MainViewModel@34e80ea

在fragment間共享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 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.
        });
    }
}

注意咖耘,2個(gè)fragment里用getActivity()獲取到相同的ViewModel對(duì)象。

這樣共享ViewModel的好處是:

activity不需要知道fragment之間通信的細(xì)節(jié)撬码,fragment之間也不需要知道對(duì)方听诸,一個(gè)fragemnt的存活與否也不影響另一個(gè)fragment使用viewmodle仰坦。

原理分析

ViewModel架構(gòu)圖

ViewModel為什么能夠保活为黎?

ViewModelProviders.java:
    @MainThread
    public static ViewModelProvider of(@NonNull FragmentActivity activity,
            @NonNull Factory factory) {
        checkApplication(activity);
        return new ViewModelProvider(ViewModelStores.of(activity), factory);
    }
    
ViewModelStores.java:
    @MainThread
    public static ViewModelStore of(@NonNull FragmentActivity activity) {
        return holderFragmentFor(activity).getViewModelStore();
    }
    
HolderFragment.java:
    private ViewModelStore mViewModelStore = new ViewModelStore();
    public HolderFragment() {
        setRetainInstance(true); // Control whether a fragment instance is retained across
        // Activity re-creation (such as from a configuration change).
    }

    .HolderFragmentManager (內(nèi)部類):
            private ActivityLifecycleCallbacks mActivityCallbacks =
                new EmptyActivityLifecycleCallbacks() {
                    @Override
                    public void onActivityDestroyed(Activity activity) {
                        // activity被destroyed了才移除對(duì)應(yīng)的fragment渊鞋,所以其維護(hù)的mViewModelStore也沒了堂污。
                        HolderFragment fragment = mNotCommittedActivityHolders.remove(activity);
                        if (fragment != null) {
                            Log.e(LOG_TAG, "Failed to save a ViewModel for " + activity);
                        }
                    }
                };

        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); // 觀察activity的生命周期
            }
            holder = createHolderFragment(fm);
            mNotCommittedActivityHolders.put(activity, holder);
            return holder;
        }
    
        private static HolderFragment createHolderFragment(FragmentManager fragmentManager) {
            HolderFragment holder = new HolderFragment(); // 創(chuàng)建時(shí)會(huì)setRetainInstance(true)讓該fragment蓖鑫剩活菱属。
            // 創(chuàng)建一個(gè)HolderFragment對(duì)象,add進(jìn)activity或者fragment關(guān)聯(lián)的FragmentManager驼鹅,但不顯示微谓,沒有UI森篷。
            fragmentManager.beginTransaction().add(holder, HOLDER_TAG).commitAllowingStateLoss();
            return holder;
        }

ViewModelProvider of(@NonNull Fragment fragment)邏輯類似。

ViewModel為什么可以share?

因?yàn)橥ㄟ^(guò)ViewModelProvider of(FragmentActivity)獲得的ViewModelProvider對(duì)象里是相同的ViewModelStore對(duì)象
從這個(gè)對(duì)象里用相同的key獲取viewmodel就是一個(gè)model對(duì)象堰酿。

ViewModelProvider.java:
    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;
    }

AndroidViewModel 與ViewModel疾宏,該用哪個(gè)张足?
ViewModel是不支持任何context的触创,如果在ViewModel里需要使用context就用AndroidViewModel,因?yàn)榭梢酝ㄟ^(guò)getApplication()得到app context为牍,也不會(huì)造成內(nèi)存泄漏哼绑。

Data Binding Library

參考 官方解釋

Data Binding Library具有高度可擴(kuò)展性和易用性,是一個(gè)支持庫(kù)碉咆,可以在Android 2.1 (API level 7+)以上使用抖韩。

優(yōu)點(diǎn):

  • 不用再findViewById()了,code也不會(huì)出現(xiàn)自動(dòng)生成的控件標(biāo)簽
  • 可以自動(dòng)綁定數(shù)據(jù)
  • 可以優(yōu)雅的處理事件(如click或自定義方法等)
  • 更多高級(jí)用法疫铜,如表達(dá)式及轉(zhuǎn)換等

使用之前在app module的build.gradle file 里添加

android {
    ....
    dataBinding {
        enabled = true
    }
}

Android Gradle Plugin 3.1.0 Canary 6使用了新的bindding編譯器茂浮,在gradle.properties里添加
android.databinding.enableV2=true 就可以激活它。

使用data binding壳咕,首先從layout文件開始席揽,在java文件里通過(guò)DataBinding實(shí)例或DataBindingUtil來(lái)inflate一個(gè)layout,之后就可以通過(guò)DataBinding實(shí)例來(lái)操作layout谓厘。

寫個(gè)簡(jiǎn)單的demo演示下前2個(gè)優(yōu)點(diǎn)幌羞,用mvvm-live模式,顯示一個(gè)person的簡(jiǎn)單信息:

  1. 主layout:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>
        <import type="com.lb.legor.mvvm_live.databinding.Person"/>
        <variable
            name="person"
            type="Person"/>
    </data>

    <LinearLayout
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <include layout="@layout/simple_textview"
            android:id="@+id/simpleTextView"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textColor="#000000"
            android:text="@{person.id}"/>

        <com.lb.legor.mvvm_live.databinding.DataBindingLayout1
            android:id="@+id/dataBindingLayout1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>

        <com.lb.legor.mvvm_live.databinding.DataBindingLayout2
            android:id="@+id/dataBindingLayout2"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>

        <android.support.v7.widget.RecyclerView
            android:id="@+id/recyclerView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>
    </LinearLayout>
</layout>
  1. 主activity:
public class DataBindingActivity extends FragmentActivity {
    ActivityDatabindingBinding binding;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = DataBindingUtil.setContentView(this, R.layout.activity_databinding);
        observeData();
    }

    @Override
    protected void onResume() {
        super.onResume();
        obtainViewModel().requestAPerson();
    }

    private void observeData() {
        obtainViewModel().getPersonLiveData().observe(this, new Observer<Person>() {
            @Override
            public void onChanged(@Nullable Person person) {
                if (person == null) {
                    return;
                }
                binding.simpleTextView.textView.setText("A person's simple info:");
                binding.setPerson(person);
                binding.dataBindingLayout1.updateData(person.name);
                binding.dataBindingLayout2.updateData(person.basic);
                binding.recyclerView.setLayoutManager(new LinearLayoutManager(DataBindingActivity.this));
                binding.recyclerView.setAdapter(new CompanyAdapter(DataBindingActivity.this, person.companyList));
            }
        });
    }

    private DataBindingViewModel obtainViewModel() {
        return ViewModelProviders.of(this).get(DataBindingViewModel.class);
    }
}
  1. DataBindingLayout1類
public class DataBindingLayout1 extends LinearLayout {
    private LayoutDatabinding1Binding binding;

    public DataBindingLayout1(Context context) {
        this(context, null);
    }

    public DataBindingLayout1(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public DataBindingLayout1(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        // 有2種寫法竟稳,用編譯生成的LayoutDatabinding1Binding或DataBindingUtil
        binding = LayoutDatabinding1Binding.inflate(LayoutInflater.from(context), this, true);
//        binding = DataBindingUtil.inflate(LayoutInflater.from(context), R.layout.layout_databinding_1, this, true);
    }

    public void updateData(Person.Name name) {
        binding.setName(name);
    }
}
  1. DataBindingLayout1類的layout:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
    
    <data>
        <variable
            name="name"
            type="com.lb.legor.mvvm_live.databinding.Person.Name"/>
    </data>
    
    <LinearLayout
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{name.firstName}"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="2dp"
            android:text="@{name.lastName}"/>

    </LinearLayout>
</layout>

DataBindingLayout2類似

  1. CompanyAdapter類:
public class CompanyAdapter extends RecyclerView.Adapter<CompanyAdapter.MyViewHolder> {

    private Context context;
    private List<Person.Company> data;

    public CompanyAdapter(Context context, List<Person.Company> data) {
        this.context = context;
        this.data = data;
    }

    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        SimpleTextviewBinding binding = DataBindingUtil.inflate(LayoutInflater.from(context), R.layout.simple_textview,
                parent, false);
        return new MyViewHolder(binding.getRoot(), binding);
    }

    @Override
    public void onBindViewHolder(MyViewHolder holder, int position) {
        holder.binding.textView.setText(data.get(position).name);
    }

    @Override
    public int getItemCount() {
        return data.size();
    }

    static class MyViewHolder extends RecyclerView.ViewHolder {
        SimpleTextviewBinding binding;

        public MyViewHolder(View itemView, SimpleTextviewBinding binding) {
            super(itemView);
            this.binding = binding;
        }
    }
}
  1. DataBindingViewModel類:
public class DataBindingViewModel extends AndroidViewModel {
    private MutableLiveData<Person> personLiveData = new MutableLiveData<>();

    public DataBindingViewModel(@NonNull Application application) {
        super(application);
    }

    public void requestAPerson() {
        //...
        Person person = new Person();
        person.id = "340811199001011234";
        Person.Name name = new Person.Name();
        name.firstName = "Paul";
        name.lastName = "Li";
        person.name = name;
        Person.Basic basic = new Person.Basic();
        basic.age = 18;
        basic.sex = "Male";
        person.basic = basic;
        List<Person.Company> companyList = new ArrayList<>();
        Person.Company company1 = new Person.Company();
        company1.name = "Rong";
        companyList.add(company1);
        Person.Company company2 = new Person.Company();
        company2.name = "Le";
        companyList.add(company2);
        Person.Company company3 = new Person.Company();
        company3.name = "Sony";
        companyList.add(company3);
        person.companyList = companyList;
        personLiveData.setValue(person);
    }

    public MutableLiveData<Person> getPersonLiveData() {
        return personLiveData;
    }
}

done属桦,運(yùn)行截圖

借鑒 & 運(yùn)用

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市他爸,隨后出現(xiàn)的幾起案子聂宾,更是在濱河造成了極大的恐慌,老刑警劉巖诊笤,帶你破解...
    沈念sama閱讀 216,324評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件系谐,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡盏混,警方通過(guò)查閱死者的電腦和手機(jī)蔚鸥,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)许赃,“玉大人止喷,你說(shuō)我怎么就攤上這事』炝模” “怎么了弹谁?”我有些...
    開封第一講書人閱讀 162,328評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我预愤,道長(zhǎng)沟于,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,147評(píng)論 1 292
  • 正文 為了忘掉前任植康,我火速辦了婚禮旷太,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘销睁。我一直安慰自己供璧,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,160評(píng)論 6 388
  • 文/花漫 我一把揭開白布冻记。 她就那樣靜靜地躺著睡毒,像睡著了一般。 火紅的嫁衣襯著肌膚如雪冗栗。 梳的紋絲不亂的頭發(fā)上演顾,一...
    開封第一講書人閱讀 51,115評(píng)論 1 296
  • 那天,我揣著相機(jī)與錄音隅居,去河邊找鬼钠至。 笑死,一個(gè)胖子當(dāng)著我的面吹牛军浆,可吹牛的內(nèi)容都是我干的棕洋。 我是一名探鬼主播,決...
    沈念sama閱讀 40,025評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼乒融,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼掰盘!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起赞季,我...
    開封第一講書人閱讀 38,867評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤愧捕,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后申钩,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體次绘,經(jīng)...
    沈念sama閱讀 45,307評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,528評(píng)論 2 332
  • 正文 我和宋清朗相戀三年撒遣,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了邮偎。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,688評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡义黎,死狀恐怖禾进,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情廉涕,我是刑警寧澤泻云,帶...
    沈念sama閱讀 35,409評(píng)論 5 343
  • 正文 年R本政府宣布艇拍,位于F島的核電站,受9級(jí)特大地震影響宠纯,放射性物質(zhì)發(fā)生泄漏卸夕。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,001評(píng)論 3 325
  • 文/蒙蒙 一婆瓜、第九天 我趴在偏房一處隱蔽的房頂上張望快集。 院中可真熱鬧,春花似錦勃救、人聲如沸碍讨。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,657評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至宵统,卻和暖如春晕讲,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背马澈。 一陣腳步聲響...
    開封第一講書人閱讀 32,811評(píng)論 1 268
  • 我被黑心中介騙來(lái)泰國(guó)打工瓢省, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人痊班。 一個(gè)月前我還...
    沈念sama閱讀 47,685評(píng)論 2 368
  • 正文 我出身青樓勤婚,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親涤伐。 傳聞我的和親對(duì)象是個(gè)殘疾皇子馒胆,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,573評(píng)論 2 353

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