前言
在我的前几篇文章中,简单学习了以下内容:
Android DataBinding使用总结 (一) DataBinding的环境配置和基本使用
Android DataBinding使用总结(二) DataBinding的所有基本使用方法
Android DataBinding使用总结(三) DataBinding展示RecyclerView列表
Android DataBinding使用总结(四) DataBinding的多类型列表展示
Android DataBinding使用总结(五)结合MultiType展示多类型列表
今天将前段时间学习的DataBinding结合Dagger2进行MVVM的架构模式进行一个简单的界面开发。
阅读本文需要了解的库:Dagger2、DataBinding的基本使用。
Dagger2作为强大的依赖注入库,可以给我们平时的开发效率带来极大的提升,如果没有了解过的请可以参考以下文档,本文不再叙述:
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)架构设计及思想,请参考这篇文章:
有幸拜读了他的源码,收获颇多。
二、代码展示
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<MovieInfoModel> getMovieInfoTest() {
return getMovieInfo(String.valueOf(25937854));
}
/**
* 获得对应电影ID的电影信息
*
* @param movieId
* @return
*/
public Observable<MovieInfoModel> 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();
}
}
重要的布局文件:
<?xml version="1.0" encoding="utf-8"?>
<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> student = new ObservableField<>();
public final ObservableField<MovieInfoModel> 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中令人望而生畏的接口数量,因此还是有可鉴之处的。
参考文档
如何构建Android MVVM应用程序 @Kelin大神
本文源码地址
该demo包含本文中所有代码但不仅限于:
使用了 Mvvm+DataBinding 搭建的DemoApp
——DataBinding的入门使用以及引导界面
——Databinding的使用
——RecyclerView中使用dataBinding进行列表展示
——RecyclerView中使用dataBinding展示多类型列表
——MultiType库+DataBinding展示RecyclerView多类型列表
——简单搭建MVVM+DataBinding+Dagger2的界面