MVVM模式之基础篇

  • MVVM的定义

 

MVVM模式之基础篇_第1张图片

如上所示,MVVM是Mode-View-ViewMode模式:

Model :负责数据实现和逻辑处理,类似MVP。

View : 对应于Activity和XML,负责View的绘制以及与用户交互,类似MVP。

ViewModel : 创建关联,将model和view绑定起来,如此之后,我们model的更改,通过viewmodel反馈给view,从而自动刷新界面

 

MVVM优点?

  1. 数据一致性,因为MVVM采用ViewMode进行数据的自动更新,数据变化时无需进行各种传递,并且可以做到各个Fragment之间数据共享
  2. 解耦性,将View和Mode解耦,View层真正只负责UI相关的,Mode负责数据相关的,不会造成庞大的控制逻辑,特别逻辑多了会出现超级Activity
  3. 利于单元测试,因为View层和Mode层是解耦的,所以可以单独对View和Mode逻辑进行单元测试

 

MVVM缺点?

  1. 增加调试难度,当界面出现异常时,可能是View层代码问题,也可能是Mode层代码问题,这时候定位问题就会麻烦点
  2. 内存占用多,因为ViewMode的生命周期是一直到Activity彻底销毁的,所以这期间LiveData里面的数据是一直保持的,会造成一定的内存占用

 

  • MVVM具体使用

1、首先我们先定义一个简单的ViewMode,这边会把LiveData放到ViewMode里面进行维护,目的是为了更好区分Mode和LiveData的逻辑,如果把LiveData放到Mode里面,当数据出现问题时我们还得排除是LiveData本身数据通知问题还是Mode本身数据处理问题,所以个人还是比较赞同把LiveData放到ViewMode里面,这样比较好维护

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

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

另外上面的loadUsers方法本身就是调用Mode层获取数据,这个数据可以是数据库相关、网络相关、文件相关等,这边就不具体写了

 

2、定义View层,即Activity和layout,View层会持有ViewMode对象,是单向持有,ViewMode不能包含任何View相关的控件,也不需要关心任何View层逻辑

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

        });

    }

}

上面就是一个简单的MVVM结构了,是不是感觉Activity一下子简洁了很多,测试时可以更换View复用ViewMode,也可以更换ViewMode复用View,这样设计单元测试用例就简洁多了,不过在设计的时候不要太多关注ViewMode的输入输出,更多关注View层和Mode层的逻辑是否正确

 

  • Fragment之间数据共享
public class SharedViewModel extends ViewModel {

    private final MutableLiveData selected = new MutableLiveData();



    public void select(Item item) {

        selected.setValue(item);

    }



    public LiveData getSelected() {

        return selected;

    }

}



public class MasterFragment extends Fragment {

    private SharedViewModel model;

    public void onActivityCreated() {

        model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);

        itemSelector.setOnClickListener(item -> {

            model.select(item);

        });

    }

}



public class DetailFragment extends LifecycleFragment {

    public void onActivityCreated() {

        SharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);

        model.getSelected().observe(this, { item ->

                // update UI

        });

    }

}

这边涉及到ViewMode的生命周期了,可以看一下下面的代码:

ViewModelStore的定义

public class ViewModelStore {



    private final HashMap mMap = new HashMap<>();



    final void put(String key, ViewModel viewModel) {

        ViewModel oldViewModel = mMap.put(key, viewModel);

        if (oldViewModel != null) {

            oldViewModel.onCleared();

        }

    }



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

    }

}

ViewModelProviders的of方法

@NonNull

@MainThread

public static ViewModelProvider of(@NonNull FragmentActivity activity,

        @Nullable Factory factory) {

    Application application = checkApplication(activity);

    if (factory == null) {

        factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);

    }

    return new ViewModelProvider(activity.getViewModelStore(), factory);

}

ViewModelProviders的get方法

public  T get(@NonNull String key, @NonNull Class 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;

}

FragmentActivity的onDestroy方法

@Override

protected void onDestroy() {

    super.onDestroy();



    if (mViewModelStore != null && !isChangingConfigurations()) {

        mViewModelStore.clear();

    }



    mFragments.dispatchDestroy();

}

然后我们来解释一下为什么可以做到fragment间数据共享:

  1. 首先获取ViewMode对象都是从ViewModelProvider里面的ViewModelStore获取的,而每个Fragment获取ViewModelProvider对象时构造函数传的Activity都是一样的,所以最终获取的是同一个对象
  2. ViewModelProvider里面的ViewModelStore对象是FragmentActivity里面的mViewModelStore变量,所以activity相同情况下mViewModelStore肯定也相同
  3. 因为每个Fragment对应的getActivity是同一个FragmentActivity,所以获取的ViewMode对象也是同一个,这样就能做到数据共享了

 

  • LiveData的生命周期绑定
@MainThread

public void observe(@NonNull LifecycleOwner owner, @NonNull Observer observer) {

    assertMainThread("observe");

    if (owner.getLifecycle().getCurrentState() == DESTROYED) {

        // ignore

        return;

    }

    LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);

    ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);

    if (existing != null && !existing.isAttachedTo(owner)) {

        throw new IllegalArgumentException("Cannot add the same observer"

                + " with different lifecycles");

    }

    if (existing != null) {

        return;

    }

    owner.getLifecycle().addObserver(wrapper);

}

这边是基于Lifecycle来实现生命周期绑定的,lifecycle方面就不多做介绍了,可以看到对livedata对象进行监听时会在当前Activity对应的lifecycle中加入一个观察者,这样livedata就能获取到activity对应的生命周期通知,然后livedata做了几件事:

  1. 只有在activity活动状态下才会进行数据通知,即调用onChanged回调
  2. 每个observer对象都有持有一个通知计数,初始值是-1,只有当observer的计数值小于livedata里面的计数值才会进行通知,这样可以防止多次重复调用
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;

    }

    if (observer.mLastVersion >= mVersion) {

        return;

    }

    observer.mLastVersion = mVersion;

    //noinspection unchecked

    observer.mObserver.onChanged((T) mData);

}

 

你可能感兴趣的:(android开发)