简单MVP+RxJava2+Retrofit2 搭建一个属于自己的开发框架

一:序言

2016年安卓热门词汇MVP,RxJava,Retrofit。时隔一年这些框架依然是很常用的,现在来把这几个关键词整合起来,搭建一个快速开发框架。。。

二: MVP是什么?

对于一些刚学安卓的朋友们应该还不是太熟悉,我们先来温习一下吧! 
这里写图片描述 
这张图可以说是看烂了,这张图对于懂了点MVP的人可以说是把中间几个字去掉,都能一眼看穿。这张图到底是什么意思呢?


举个例子:

需求:需要点击一个按钮通过访问网络获取一条数据展示在页面上

普通做法: 
一个Activity中写一个方法访问网络获取数据,点击按钮调用它,然后获取数据完成了再拿到对应的控件设置数据,完事了。。。

MVP: 
在图中有三个模块view(界面),presenter(控制层),model(数据源)。他们在这个需求中需要做什么呢? 
view(界面):显示数据 
presenter(控制层):1.通知model我要取数据 2.取到了数据再传递给view 
model(数据源):访问网络获取数据

它的过程是这样的,

  1. view告诉presenter我要数据
  2. presenter告诉model我要数据
  3. model访问网络得到了数据再通知presenter给你我取到的数据
  4. presenter 处理好数据 再把数据传递给view
  5. 最后view显示出来用户可以观看。

有些人说这不是脱了裤子放屁啊?一点代码能写完的东西为啥分了这么多东西? 
这确实有点复杂,在面向对象中有几个原则 单一职责原则,开闭原则,里氏代换原则,依赖倒转原则,接口隔离原则,合成复用原则,迪米特法则。这我就不一一介绍了,自行百度。。普通做法中一个Activity即有访问网络,又有更新界面,第一条单一职责原则就违背了,然而在mvp中view只做和界面相关的事情。 
再者一个Activity中如果逻辑太多了。一个Activity几千行代码,逻辑判断,更新界面,查询数据库,访问网络,如果第二个人需要修改,怎么看?? 
这时候再看看mvp 逻辑在P里面一个类,数据在Model层,界面相关的在V层。清晰明了,也方便单元测试。

程序猿如果不最求代码质量,那和咸鱼有什么区别?

三: RxJava2+Retrofit2整合

1.玩框架第一步compile :

 
  1. compile 'io.reactivex.rxjava2:rxjava:2.1.1'

  2. compile 'io.reactivex.rxjava2:rxandroid:2.0.1'

  3. compile 'com.squareup.retrofit2:retrofit:2.3.0'

  4. compile 'com.squareup.retrofit2:converter-gson:2.3.0'

  5. compile 'com.squareup.retrofit2:converter-scalars:2.3.0'

  6. compile 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'//配合rxjava2

  7. compile 'com.squareup.okhttp3:logging-interceptor:3.8.1'//拦截器

2.创建service

 
  1. public interface RetrofitService {

  2.  
  3. String BASE_URL = "https://news-at.zhihu.com/api/4/";

  4.  
  5. /**

  6. * 测试接口

  7. *

  8. * @return

  9. */

  10. @GET("news/latest")

  11. Observable<TestBean> test();

  12. }

单独使用retrofit是返回call,配合RxJava这里我们返回Observable

3.封装一个工具类

 
  1. public class RetrofitFactory {

  2.  
  3. //访问超时

  4. private static final long TIMEOUT = 30;

  5.  
  6. // Retrofit是基于OkHttpClient的,可以创建一个OkHttpClient进行一些配置

  7. private static OkHttpClient httpClient = new OkHttpClient.Builder()

  8. //打印接口信息,方便接口调试

  9. .addInterceptor(new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {

  10. @Override

  11. public void log(String message) {

  12. Log.e("TAG", "log: " + message);

  13. }

  14. }).setLevel(HttpLoggingInterceptor.Level.BASIC))

  15. .connectTimeout(TIMEOUT, TimeUnit.SECONDS)

  16. .readTimeout(TIMEOUT, TimeUnit.SECONDS)

  17. .build();

  18.  
  19. private static RetrofitService retrofitService = new Retrofit.Builder()

  20. .baseUrl(RetrofitService.BASE_URL)

  21. // 添加Gson转换器

  22. .addConverterFactory(GsonConverterFactory.create(new GsonBuilder()

  23. .setLenient()

  24. .create()

  25. ))

  26. // 添加Retrofit到RxJava的转换器

  27. .addCallAdapterFactory(RxJava2CallAdapterFactory.create())

  28. .client(httpClient)

  29. .build()

  30. .create(RetrofitService.class);

  31.  
  32. //获得RetrofitService对象

  33. public static RetrofitService getInstance() {

  34. return retrofitService;

  35. }

  36. }

4.使用

我们整合好了,最后我们看下怎么使用吧!访问个网络获取一个数据

 
  1. RetrofitFactory.getInstance()//获取retrofitService对象

  2. .test()//测试接口

  3. .subscribeOn(Schedulers.io())

  4. .doOnSubscribe(new Consumer<Disposable>() {

  5. @Override

  6. public void accept(@NonNull Disposable disposable) throws Exception {

  7. //将这个请求的Disposable添加进入CompositeDisposable同一管理(在封装的presenter中)

  8. addDisposable(disposable);

  9. //访问网络显示dialog

  10. view.showLoadingDialog("");

  11. }

  12. })

  13. .map(new Function<TestBean, List<TestBean.StoriesBean>>() {

  14. @Override

  15. public List<TestBean.StoriesBean> apply(@NonNull TestBean testBean) throws Exception {

  16. //转化数据

  17. return testBean.getStories();

  18. }

  19. })

  20. //获得的数据返回主线程去更新界面

  21. .observeOn(AndroidSchedulers.mainThread())

  22. .subscribe(new Consumer<List<TestBean.StoriesBean>>() {

  23. @Override

  24. public void accept(@NonNull List<TestBean.StoriesBean> storiesBeen) throws Exception {

  25. //消失dialog

  26. view.dismissLoadingDialog();

  27. //设置数据

  28. view.setData(storiesBeen);

  29. }

  30. }, new Consumer<Throwable>() {

  31. @Override

  32. public void accept(@NonNull Throwable throwable) throws Exception {

  33. view.dismissLoadingDialog();

  34. String exception = ExceptionHelper.handleException(throwable);

  35. //打印出错误信息

  36. Log.e("TAG", "exception: " + exception);

  37. }

  38. });

好我们来分析一下,

  1. 首先先获得一个retrofitService对象
  2. 然后调用test接口。
  3. 访问网络在子线程
  4. 在访问网络的时候显示等待对话框,将这个请求加入CompositeDisposable中(在basePresenter封装了统一管理的方法,调用addDisposable(disposable);最后Activity关闭,取消所有网络请求,防止内存泄漏)
  5. 将网络获取的数据转换成你需要的数据
  6. 线程卡点结果返回主线程
  7. 订阅得到数据更新界面,处理错误信息

RxJava2+retrofit2就是这么简单封装好了一条线路下来非常清晰。没用过的朋友看下有可能一脸懵逼,不过没关系,你只要拿着我的项目看下就能懂了。


四: 打造MVP

先看下我们的成果里面有什么东西吧!没错 就是下面几个类就ok 
这里写图片描述

五:分析

好我们来分析一下mvp

1.view需要找presenter拿数据,那么view里面需要一个presenter对象。 
2.presenter需要给view数据,那么presenter也需要一个view对象。 
3.model层访问网络使用RxJava+retrofit,数据回调给presenter(后面分析)

思考

所有的view里面都需要什么操作呢? 所有的presenter里面都需要什么操作呢? 
暂时在我的需求中view和presenter只有如下这么几个功能,当然,如果你还有其他的功能可以再加上去。

 
  1. public interface BaseView {

  2.  
  3. //显示dialog

  4. void showLoadingDialog(String msg);

  5.  
  6. //取消dialog

  7. void dismissLoadingDialog();

  8. }

 
  1. public interface BasePresenter {

  2. //默认初始化

  3. void start();

  4.  
  5. //Activity关闭把view对象置为空

  6. void detach();

  7.  
  8. //将网络请求的每一个disposable添加进入CompositeDisposable,再退出时候一并注销

  9. void addDisposable(Disposable subscription);

  10.  
  11. //注销所有请求

  12. void unDisposable();

  13.  
  14. }

五:接下来编写view和presenter的实现类

由于每一个view都对应不同的presenter。当然对应的每个presenter也同样对应一个view。所有我们使用接口和泛型来封装了。

所以我们先看下代码:

 
  1. public abstract class BaseActivity<P extends BasePresenter> extends AppCompatActivity implements BaseView {

  2. protected P presenter;

  3. public Context context;

  4.  
  5. @Override

  6. protected void onCreate(Bundle savedInstanceState) {

  7. super.onCreate(savedInstanceState);

  8. context = this;

  9. ActivityManager.getAppInstance().addActivity(this);//将当前activity添加进入管理栈

  10. presenter = initPresenter();

  11. }

  12.  
  13. @Override

  14. protected void onDestroy() {

  15. ActivityManager.getAppInstance().removeActivity(this);//将当前activity移除管理栈

  16. if (presenter != null) {

  17. presenter.detach();//在presenter中解绑释放view

  18. presenter = null;

  19. }

  20. super.onDestroy();

  21. }

  22.  
  23. /**

  24. * 在子类中初始化对应的presenter

  25. *

  26. * @return 相应的presenter

  27. */

  28. public abstract P initPresenter();

  29.  
  30.  
  31. @Override

  32. public void dismissLoadingDialog() {

  33.  
  34. }

  35.  
  36. @Override

  37. public void showLoadingDialog(String msg) {

  38.  
  39. }

  40. }

  41. public abstract class BasePresenterImpl<V extends BaseView> implements BasePresenter {

  42. public BasePresenterImpl(V view) {

  43. this.view = view;

  44. start();

  45. }

  46.  
  47. protected V view;//给子类使用view

  48.  
  49.  
  50. @Override

  51. public void detach() {

  52. this.view = null;

  53. unDisposable();

  54. }

  55.  
  56. @Override

  57. public void start() {

  58.  
  59. }

  60.  
  61. -----------------------我是分割线--------------------------------

  62.  
  63. //以下下为配合RxJava2+retrofit2使用的

  64.  
  65. //将所有正在处理的Subscription都添加到CompositeSubscription中。统一退出的时候注销观察

  66. private CompositeDisposable mCompositeDisposable;

  67.  
  68. /**

  69. * 将Disposable添加

  70. *

  71. * @param subscription

  72. */

  73. @Override

  74. public void addDisposable(Disposable subscription) {

  75. //csb 如果解绑了的话添加 sb 需要新的实例否则绑定时无效的

  76. if (mCompositeDisposable == null || mCompositeDisposable.isDisposed()) {

  77. mCompositeDisposable = new CompositeDisposable();

  78. }

  79. mCompositeDisposable.add(subscription);

  80. }

  81.  
  82. /**

  83. * 在界面退出等需要解绑观察者的情况下调用此方法统一解绑,防止Rx造成的内存泄漏

  84. */

  85. @Override

  86. public void unDisposable() {

  87. if (mCompositeDisposable != null) {

  88. mCompositeDisposable.dispose();

  89. }

  90. }

  91.  
  92. }

  • 创建activity中泛型传入相应的view接口,presenter中泛型传入相应的presenter接口
  • activity中onCreate中初始化presenter,onDestroy中调用detach,将presenter中正在执行的任务取消,将view对象置为空。

  • presenter中通过构造传递参数。将view的实例传递进入presenter


六:使用

好的接下来我们来使用一下吧 
首先我们先来个简单的需求:

打开一个页面请求网络获取数据,将数据显示在界面上

创建Contact管理接口

首先先思考view需要设置数据所有view中需要一个setData方法 
presenter需要去访问网络所以需要一个getData方法。代码如下:

 
  1. public interface TestContact {

  2. interface view extends BaseView {

  3. /**

  4. * 设置数据

  5. *

  6. * @param dataList

  7. */

  8. void setData(List<TestBean.StoriesBean> dataList);

  9. }

  10.  
  11. interface presenter extends BasePresenter {

  12. /**

  13. * 获取数据

  14. */

  15. void getData();

  16. }

  17. }

创建Activity和presenter

创建一个Activity继承BaseActivity它的泛型对应presenter的接口。实现对应的view接口 
创建一个TestPresenter继承BasePresenterImpl,泛型对应view的接口。并实现对应的presenter接口

代码如下:

 
  1. public class TestActivity extends BaseActivity<TestContact.presenter> implements TestContact.view {

  2.  
  3. private List<TestBean.StoriesBean> list = new ArrayList<>();//数据

  4. private RecyclerView recyclerView;

  5. private TestAdapter adapter;

  6.  
  7. @Override

  8. protected void onCreate(Bundle savedInstanceState) {

  9. super.onCreate(savedInstanceState);

  10. setContentView(R.layout.activity_test);

  11. init();

  12. presenter.getData();

  13. }

  14.  
  15. /**

  16. * 初始化界面

  17. */

  18. private void init() {

  19. recyclerView = (RecyclerView) findViewById(R.id.recycleview);

  20. recyclerView.setLayoutManager(new LinearLayoutManager(this));

  21. adapter = new TestAdapter(list);

  22. recyclerView.setAdapter(adapter);

  23. }

  24.  
  25. /**

  26. * 初始化presenter

  27. *

  28. * @return 对应的presenter

  29. */

  30. @Override

  31. public TestContact.presenter initPresenter() {

  32. return new TestPresenter(this);

  33. }

  34.  
  35. /**

  36. * 设置数据

  37. * 刷新界面

  38. *

  39. * @param dataList 数据源

  40. */

  41. @Override

  42. public void setData(List<TestBean.StoriesBean> dataList) {

  43. list.addAll(dataList);

  44. adapter.notifyDataSetChanged();

  45. }

  46. }

  •  
  1. public class TestPresenter extends BasePresenterImpl<TestContact.view> implements TestContact.presenter {

  2. public TestPresenter(TestContact.view view) {

  3. super(view);

  4. }

  5.  
  6. /**

  7. * 获取数据

  8. */

  9. @Override

  10. public void getData() {

  11. Api.getInstance()

  12. .test()//测试接口

  13. .subscribeOn(Schedulers.io())

  14. .doOnSubscribe(new Consumer<Disposable>() {

  15. @Override

  16. public void accept(@NonNull Disposable disposable) throws Exception {

  17. addDisposable(disposable);//请求加入管理

  18. view.showLoadingDialog("");

  19. }

  20. })

  21. .map(new Function<TestBean, List<TestBean.StoriesBean>>() {

  22. @Override

  23. public List<TestBean.StoriesBean> apply(@NonNull TestBean testBean) throws Exception {

  24. return testBean.getStories();//转换数据

  25. }

  26. })

  27. .observeOn(AndroidSchedulers.mainThread())

  28. .subscribe(new Consumer<List<TestBean.StoriesBean>>() {

  29. @Override

  30. public void accept(@NonNull List<TestBean.StoriesBean> storiesBeen) throws Exception {

  31. view.dismissLoadingDialog();

  32. view.setData(storiesBeen);

  33. }

  34. }, new Consumer<Throwable>() {

  35. @Override

  36. public void accept(@NonNull Throwable throwable) throws Exception {

  37. view.dismissLoadingDialog();

  38. ExceptionHelper.handleException(throwable);

  39. }

  40. });

  41. }

  42. }

七:分析

好了相信大部分朋友看了代码都看懂了,简要的分析一下过程吧

  • 创建对应的类,实现对应的方法
  • Activity中只有一个recyclerView初始化它。
  • 在onCreate中调用presenter中的getData()方法
  • 在presenter中使用RxJava2+retrofit2访问网络。获取数据返回给view
  • view拿到数据更新界面

转载地址:http://blog.csdn.net/a_zhon/article/details/77914012

源码地址:https://github.com/azhon/Mvp-RxJava-Retrofit

猜你喜欢

转载自blog.csdn.net/qq_42618969/article/details/85167655