MVVM-live

MVVM-live

Google的项目: 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.

总结:MVVM就包含了ViewModels及Data Binding,所以mvvm‑live = mvvm + LiveData

需要了解的概念有 Lifecycle-aware组件、MVVM、LiveData、ViewModel、Data binding library。

以下是翻译Google的文档 + 源码解释。

Lifecycle-aware组件

lifecycle-aware组件能够对其他组件(比如activity和fragment)发生生命周期改变时做出反应,这样的组件显然很容易使用,轻量且易维护。

举个例子:在UI上显示定位信息

传统做法是:

定义获取位置的接口:
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();
    }
}

这么做的问题:

  1. 需要在生命周期方法里正确去回调接口方法,如果没有正确回调有可能导致内存泄漏。
  2. 并且可能在生命周期方法里有很多类似调用,导致代码很难维护,丑陋。
  3. 甚至有可能myLocationListener.stop()在start()之后调用,导致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组件包能帮助避免这些问题。

下面详细介绍。

Lifecycle

Lifecycle是一个类维护了组件(像activity或fragment)的生命周期信息,并且允许这些信息被观察者获取。内部使用2个enum类Event和State来跟踪与之关联的组件状态:

Event :从关联的组件(activities 或 fragments)里发送过来的,表明组件已经到了哪个生命周期了(如ON_CREATE、ON_START等)

State :Lifecycle对象跟踪的关联组件的状态。

官方event与state关系图

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() {
        //...
    }
}

在外部通过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();
}

只是一个接口,表明实现者有Lifecycle。v4包里的fragemnt和activity都实现了这个,也可以实现自己的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();
            }
        });
    }
}

这个MyLocationListener2就是一个lifecycle-aware的组件。

这么做的好处是:

  1. 让MyLocationListener2自己观察生命周期的变化,不需要外部组件回调,外部组件需需要初始化它就好。
  2. 避免了潜在的泄漏问题
  3. MyLocationListener2非常独立,很好复用。

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.

就是说如果你的library需要跟生命周期有关系,那就实现成lifecycle-aware的组件,这样外部就很容易集成而不用管理它的生命周期。

自定义LifecycleOwner

V4包的Support Library 26.1.0版本及以上的Fragment与Activitie已经实现了LifecycleOwner,也可以自己实现,同样借助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

关键点:

  • Model 代表domain model或者数据访问层。
  • View 代表用户在界面上能看到的UI,如layout,fragment,各种组件。(我认为activity也是,在MVVM框架里,activity被弱化了,只作为view的承载着和生命周期的体现者)。
  • View model 是view的抽象,暴露公开的属性和命令。MVVM有binder。在view model里,binder协调着view和data binder,view model可以被看成是model层数据的一种状态。
  • Binder 把view model里的数据绑定到view上的库。

LiveData

参考google官方解释: architecture-livedata
源码里LiveData的解释很棒!

其实LiveData就是lifecycle-aware组件的增强版,其内部实现了LifecycleObserver。(LifecycleBoundObserver实现的)

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

关键点

  • 是可被观察的data holder
  • 是lifecycle-aware的,就是说它可感知组件(如activities, fragments, services)的生命周期。并且它只更新处于active状态的观察者组件。
  • active状态是指生命周期处于 STARTED 或者 RESUMED 状态,LiveData只通知active的观察者,非active的观察者得不到通知。
  • STARTED的解释RESUMED的解释

优点

  • 确保UI跟数据状态匹配

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.

  • 没有内存泄漏

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

  • 不会因为activity 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.

  • 自动处理由于生命周期改变的影响

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时,observer总是收到最新的数据

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.

  • 对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内共享资源

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 。通过下面的ViewModel也可以做到。

怎么使用LiveData:

  1. 在ViewModel里创建LiveData
public class NameViewModel extends AndroidViewModel {

    // Create a LiveData with a String
    private MutableLiveData<String> curName; // LiveData是data的封装,LiveData通常是放在ViewModel里,通过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()需要再主线程调用
//        curName.postValue("liBo");// postValue()可以在子线程调用
    }
}
  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);// 第一个参数是 LifecycleOwner对象
//        nameViewModel.getCurName().observeForever(nameObserver); // observer is considered to be always active and is therefore always notified about modifications
    }

}

需要明白的几点:

  • 在viewmodel里而不是fragment或activity管理livadata对象,因为(避免fragment或activitydiamante臃肿,避免congiguration change销毁livadata)。解释-1
  • 当更新LiveData里的value时,会触发所有活着的observer(其所关联的LifecycleOwner在active状态)。 解释-2
  • 如果observer从inactione变成active状态,也会受到推送的数据。 解释-3
  • 调用observe(this, nameObserver)就意味着nameObserver跟MvvmTestActivity这个lifecycle owner产生关联,其实是跟activity关联的lifecycle对象LifecycleRegistry关联的。当lifecycle对象不是active状态时,nameObserver 不会接收到更新,当lifecycle对象被销毁时,nameObserver自动从livedata里移除。

解释:

  • 1
    见下面 ' ViewModel为什么能够保活?'
  • 2
LiveData.java

@MainThread
protected void setValue(T value) {
    assertMainThread("setValue");
    mVersion++;   // 记录数据版本,很重要
    mData = value;
    dispatchingValue(null);  // 会调用 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接受的数据版本大于等于目前版本,就不用通知observer了。
        if (observer.mLastVersion >= mVersion) {
            return;
        }
        observer.mLastVersion = mVersion;
        //noinspection unchecked
        observer.mObserver.onChanged((T) mData); // 通知obserser
    }
  • 3
在LiveData.observe()里调用了owner.getLifecycle().addObserver(wrapper) 把obserser关联到了owner的lifecycle。
当owner即activity或fragment state状态发生改变时,会调用LifecycleRegistry.markState()或handleLifecycleEvent(),
再调用sync(),最后会遍历所有关联的observer,并调用其 onStateChanged(),

在LiveData.java
@Override
public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {
    if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
        removeObserver(mObserver);   // 如果lifecycle owner已经是DESTROYED状态,就移除该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个数
    if (wasInactive && mActive) {
        onActive();
    }
    if (LiveData.this.mActiveCount == 0 && !mActive) {
        onInactive();
    }
    if (mActive) {
        dispatchingValue(this); // 上面解释过
    }
}

Transform LiveData

一些高级的用法,待更新

Merge multiple LiveData sources

一些高级的用法,待更新

ViewModel

参考官方 ViewModel
源码里ViewModel的解释很棒!

设计ViewModel的目的是存储及管理UI相关的数据同时关注其生命周期,能保证数据在configuration change(比如切屏)时存活,还有作为fragment,activity与其他的逻辑的通信桥梁。

考虑以下情况:

  • 当configuration change时,activity被销毁并重建,虽然可以通过onSaveInstanceState()保存数据,但是只可以保存少量的可序列化的数据;

  • 还有ui controller(activity、fragment等)需要管理一些异步调用并保证在适当时清理掉 以避免内存泄漏,这些管理工作很麻烦;

  • UI controller应该只是显示数据到UI、相应用户操作、处理系统的交互如处理permission请求。

使用ViewModel可以把数据及处理从Ui controller分离。

怎么使用ViewModel

继承ViewModel,ViewModel的对象可以在configuration change时自动保活,其保存的数据可以立即被下一个activity或fragment实例使用。
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一定不要引用一个view、lifecycle或任何会引用activity context的类。 ViewModel被设计用来独立与view或LifecycleOwner而保活数据的。ViewModel可以维护LifecycleObserver例如LiveData,但是不能observe lifecycle-aware的被观察者例如LiveData。如果需要application context,可以继承AndroidViewModel。

ViewModel的生命周期

ViewModel对象一直存活直到关联的lifecycle永远失效,例如 activity 被finish掉(注意不是onDestroy()被调用)或者fragment被detatched了。生命周期图
验证一下:

public class MainViewModel extends AndroidViewModel {
    private int count = 0; // 只是用于activity更新以在某时做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()*/);
    }

运行结果:

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对象创建了,viewmodel对象不变
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 // 主动去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对象被清
04-03 14:33:57.659 30546-30546/com.lb.legor I/lbtest: onDestroy this=com.lb.legor.MainActivity@390bb73
// 重新进入app,肯定都是新的对象
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个fragment里用getActivity()获取到相同的ViewModel对象。

这样共享ViewModel的好处是:

activity不需要知道fragment之间通信的细节,fragment之间也不需要知道对方,一个fragemnt的存活与否也不影响另一个fragment使用viewmodle。

原理分析

ViewModel架构图

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 (内部类):
            private ActivityLifecycleCallbacks mActivityCallbacks =
                new EmptyActivityLifecycleCallbacks() {
                    @Override
                    public void onActivityDestroyed(Activity activity) {
                        // activity被destroyed了才移除对应的fragment,所以其维护的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(); // 创建时会setRetainInstance(true)让该fragment保活。
            // 创建一个HolderFragment对象,add进activity或者fragment关联的FragmentManager,但不显示,没有UI。
            fragmentManager.beginTransaction().add(holder, HOLDER_TAG).commitAllowingStateLoss();
            return holder;
        }

ViewModelProvider of(@NonNull Fragment fragment)逻辑类似。

ViewModel为什么可以share?

因为通过ViewModelProvider of(FragmentActivity)获得的ViewModelProvider对象里是相同的ViewModelStore对象
从这个对象里用相同的key获取viewmodel就是一个model对象。

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,该用哪个?
ViewModel是不支持任何context的,如果在ViewModel里需要使用context就用AndroidViewModel,因为可以通过getApplication()得到app context,也不会造成内存泄漏。

Data Binding Library

参考 官方解释

Data Binding Library具有高度可扩展性和易用性,是一个支持库,可以在Android 2.1 (API level 7+)以上使用。

优点:

  • 不用再findViewById()了,code也不会出现自动生成的控件标签
  • 可以自动绑定数据
  • 可以优雅的处理事件(如click或自定义方法等)
  • 更多高级用法,如表达式及转换等

使用之前在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文件里通过DataBinding实例或DataBindingUtil来inflate一个layout,之后就可以通过DataBinding实例来操作layout。

写个简单的demo演示下前2个优点,用mvvm-live模式,显示一个person的简单信息:

  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,运行截图

借鉴 & 运用

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,383评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,522评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,852评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,621评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,741评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,929评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,076评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,803评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,265评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,582评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,716评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,395评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,039评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,027评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,488评论 2 361
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,612评论 2 350

推荐阅读更多精彩内容