Android MVVM+DataBinding结合Dagger2进行开发

前言

在我的前几篇文章中,简单学习了以下内容:

Android DataBinding使用总结 (一) DataBinding的环境配置和基本使用

Android DataBinding使用总结(二) DataBinding的所有基本使用方法

Android DataBinding使用总结(三) DataBinding展示RecyclerView列表

Android DataBinding使用总结(四) DataBinding的多类型列表展示

Android DataBinding使用总结(五)结合MultiType展示多类型列表

今天将前段时间学习的DataBinding结合Dagger2进行MVVM的架构模式进行一个简单的界面开发。

阅读本文需要了解的库:Dagger2、DataBinding的基本使用。

Dagger2作为强大的依赖注入库,可以给我们平时的开发效率带来极大的提升,如果没有了解过的请可以参考我的相关文章,本文不再叙述:

我的相关文章,从基础使用到源码分析:

Android 神兵利器Dagger2使用详解(一)基础使用

Android 神兵利器Dagger2使用详解(二)Module&Component源码分析

Android 神兵利器Dagger2使用详解(三)进阶篇,不可自拔爱上Dagger2

源码:

「GitHub」 Dagger2_Sample源码,配合上述文章学习效果更佳

2017/8/24更新补充:

事实上,Android开发中使用Dagger,开发人员仍然需要面对一些问题。

google工程师们尝试弥补Dagger的问题,于是Dagger2-Android,基于Dagger2,应用于Android开发,由google开发的的拓展库应运而生:

  • 打破Dagger2使用窘境:Dagger-Android详解

其他相关学习资料:

Android Dagger依赖注入框架浅析
http://www.tuicool.com/articles/Nf6Njy
Dagger——Android上的依赖注入框架
https://my.oschina.net/rengwuxian/blog/287892?p=2&temp=1488510429911
Dagger - 快速的依赖注入器-官方文档翻译
http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0213/2478.html
Dagger 2: Step To Step
http://www.jianshu.com/p/7505d92d7748
使用Dagger 2进行依赖注入
http://codethink.me/2015/08/06/dependency-injection-with-dagger-2/
Dagger2 使用详解
http://blog.csdn.net/u013163564/article/details/51847784
详解Dagger2
http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0519/2892.html

一、成果:

很简单的demo,一共实现了两种需求,修改界面数据,同时还有网络请求电影信息(API接口from@豆瓣 ,感谢其提供的开发者平台)。

对于MVVM(Model+View+ViewModel)架构设计及思想,请参考这篇文章:

如何构建Android MVVM应用程序 @Kelin大神

有幸拜读了他的源码,收获颇多。

二、代码展示

0、依赖注入Dagger2相关代码(含Model层):

经常使用Dagger2的朋友们应该很熟悉了,代码就不写上去了,需要提到一点的是,依赖注入的ServiceManager实际上就是Model层,代表着网络请求电影数据:

public class ServiceManager {

    MovieService movieService;

    public ServiceManager() {
        init();
    }

    private void init() {

        OkHttpClient.Builder builder = new OkHttpClient().newBuilder();
        if (Constants.DEBUG) {
            HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
            interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
            builder.addInterceptor(interceptor);
        }
        OkHttpClient client = builder.build();

        movieService = new Retrofit.Builder()
                .baseUrl(Constants.API.BASE_DOUBAN)
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .client(client)
                .build()
                .create(MovieService.class);
    }

    public Observable getMovieInfoTest() {
        return getMovieInfo(String.valueOf(25937854));
    }

    /**
     * 获得对应电影ID的电影信息
     *
     * @param movieId
     * @return
     */
    public Observable getMovieInfo(String movieId) {
        return movieService.getMovieInfo(movieId);
    }
}

因为Dagger2并不是本文的重点,因此如果要看更多源码的话,请参考Github传送门,点我查看更多源码

1、Activity+xml布局文件(xml代表View层)

先看下Activity:

public class A06MvvmActivity extends BaseMvvmActivity<A06ActivityMvvmBinding, A06MvvmViewModel> {

    @Inject
    A06MvvmViewModel mViewModel;

    //依赖注入&&DataBinding绑定
    @Override
    protected void inject() {
        A06Component component = DaggerA06Component.builder()
                .appComponent(ComponentHolder.getComponent())
                .a06Module(new A06Module(this))
                .build();
        component.injectActivity(this);
        component.injectViewModel(mViewModel);

        mBinding.setActivity(this);
        mBinding.setViewModel(mViewModel);
    }

    //设置layout布局
    @Override
    protected int getLayoutRes() {
        return R.layout.a06_activity_mvvm;
    }

}

其中的BaseActivity封装了一些简单的逻辑:

public abstract class BaseMvvmActivity<T extends ViewDataBinding, D extends BaseViewModel> extends AppCompatActivity {

    protected T mBinding;
    protected D mViewModel;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        initDataBinding();
        inject();
    }

    protected void initDataBinding() {
        int layoutId = getLayoutRes();
        mBinding = DataBindingUtil.setContentView(this, layoutId);
    }

    /**
     * 初始化DataBinding 和 Dagger2依赖注入
     */
    protected abstract void inject();

    /**
     * 传入布局文件
     * @return 基类会自动生成对应的DataBinding供导出类使用
     */
    protected abstract int getLayoutRes();

    @Override
    protected void onDestroy() {
        if (mBinding != null)
            mBinding.unbind();

        super.onDestroy();
    }
}

重要的布局文件:


<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>

        <variable
            name="activity"
            type="com.mei_husky.samplemvvm.view.activity.A06MvvmActivity" />

        <variable
            name="viewModel"
            type="com.mei_husky.samplemvvm.viewmodel.A06MvvmViewModel" />

    data>

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


        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@{`学生姓名:` + viewModel.student.get().name}" />

        <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="@{() -> viewModel.changeName()}"
            android:text="点击修改Student姓名" />

        <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:onClick="@{() -> viewModel.getMovieInfo()}"
            android:text="点击请求电影信息" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@{viewModel.movieInfo.get() == null ? `没有电影信息` : viewModel.movieInfo.get().getSummary()}" />

    LinearLayout>

layout>

可以看到,所有界面逻辑的处理,我们都交给了viewModel,Activity实际上并没有染指任何修改UI的代码。

2、viewModel层,直接和xml交互,不再需要Activity

public class A06MvvmViewModel extends BaseViewModel {

    public final ObservableField student = new ObservableField<>();
    public final ObservableField movieInfo = new ObservableField<>();

    @Inject
    A06MvvmActivity activity;
    @Inject
    SharedPreferences sp;
    @Inject
    ServiceManager serviceManager;

    public A06MvvmViewModel() {
        student.set(new Student("qingmei2", 18));
    }

    public void changeName() {
        student.get().name.set(student.get().name.get() + "X");
    }

    /**
     * 网络请求电影数据,直接调用Model层获取数据
     */
    public void getMovieInfo() {
        serviceManager.getMovieInfoTest()
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(movieInfoModel -> movieInfo.set(movieInfoModel));
    }
}

//BaseViewModel中可以添加一些公共代码,在本demo中不涉及
public class BaseViewModel {

}

梳理一下,我们通过将viewModel通过DataBinding和xml布局绑定后,实际上数据的处理和界面的展示都已经「摆脱」了传统的Activity,在viewModel通过Model获得数据后,xml中直接根据Observable的对象观察到变化并立即更新数据,可以说是非常方便,同时一些界面的UI修改不再需要在activity中通过setText,setBackGround,setVisibility等方法去处理了,直接交给xml去处理,代码看起来非常清晰,同时耦合性也比较低。

总结

需求实现,我们可以看到,因为Activity并没有接触UI修改的逻辑,因此相比MVP设计模式,我们M-V-VM之间的耦合性更低,并且对于单元测试来说配合Mockito更加高效便捷,同时也摆脱了MVP中令人望而生畏的接口数量,因此还是有可鉴之处的。

参考文档

DataBinding 谷歌官方文档

如何构建Android MVVM应用程序 @Kelin大神

本文源码地址

Github传送门,点我查看源码

该demo包含本文中所有代码但不仅限于:

使用了 Mvvm+DataBinding 搭建的DemoApp

——DataBinding的入门使用以及引导界面

——Databinding的使用

——RecyclerView中使用dataBinding进行列表展示

——RecyclerView中使用dataBinding展示多类型列表

——MultiType库+DataBinding展示RecyclerView多类型列表

——简单搭建MVVM+DataBinding+Dagger2的界面

你可能感兴趣的:(Android)