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

许多不管怎么做、怎么想都没结果的事,要懂得交给时间。有些事无论你怎么努力怎么勉强,时间不够,还是耐心的等待吧。

一:序言

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

二: MVP是什么?

对于一些刚学安卓的朋友们应该还不是太熟悉,我们先来温习一下吧!

这张图可以说是看烂了,这张图对于懂了点MVP的人可以说是把中间几个字去掉,都能一眼看穿。这张图到底是什么意思呢?

举个例子:

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

普通做法:

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

MVP:

在图中有三个模块view(界面),presenter(控制层),model(数据源)。他们在这个需求中需要做什么呢?

view(界面):显示数据

presenter(控制层):1.通知model我要取数据 2.取到了数据再传递给view

model(数据源):访问网络获取数据

它的过程是这样的,

view告诉presenter我要数据

presenter告诉model我要数据

model访问网络得到了数据再通知presenter给你我取到的数据

presenter处理好数据再把数据传递给view

最后view显示出来用户可以观看。

有些人说这不是脱了裤子放屁啊?一点代码能写完的东西为啥分了这么多东西?

这确实有点复杂,在面向对象中有几个原则 单一职责原则,开闭原则,里氏代换原则,依赖倒转原则,接口隔离原则,合成复用原则,迪米特法则。这我就不一一介绍了,自行百度。。普通做法中一个Activity即有访问网络,又有更新界面,第一条单一职责原则就违背了,然而在mvp中view只做和界面相关的事情。

再者一个Activity中如果逻辑太多了。一个Activity几千行代码,逻辑判断,更新界面,查询数据库,访问网络,如果第二个人需要修改,怎么看??

这时候再看看mvp 逻辑在P里面一个类,数据在Model层,界面相关的在V层。清晰明了,也方便单元测试。

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

三: RxJava2+Retrofit2整合

1.玩框架第一步compile :

compile'io.reactivex.rxjava2:rxjava:2.1.1'compile'io.reactivex.rxjava2:rxandroid:2.0.1'compile'com.squareup.retrofit2:retrofit:2.3.0'compile'com.squareup.retrofit2:converter-gson:2.3.0'compile'com.squareup.retrofit2:converter-scalars:2.3.0'compile'com.squareup.retrofit2:adapter-rxjava2:2.3.0'//配合rxjava2compile'com.squareup.okhttp3:logging-interceptor:3.8.1'//拦截器

1

2

3

4

5

6

7

2.创建service

publicinterfaceRequestService{String BASE_URL ="https://news-at.zhihu.com/api/4/";/**    * 测试接口    *    *@return*/@GET("news/latest")    Observable test();}

1

2

3

4

5

6

7

8

9

10

11

12

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

3.封装一个工具类

publicclassRetrofitFactory{//访问超时privatestaticfinallongTIMEOUT =30;// Retrofit是基于OkHttpClient的,可以创建一个OkHttpClient进行一些配置privatestaticOkHttpClient httpClient =newOkHttpClient.Builder()//打印接口信息,方便接口调试.addInterceptor(newHttpLoggingInterceptor(newHttpLoggingInterceptor.Logger() {@Overridepublicvoidlog(String message) {                    Log.e("TAG","log: "+ message);                }            }).setLevel(HttpLoggingInterceptor.Level.BASIC))            .connectTimeout(TIMEOUT, TimeUnit.SECONDS)            .readTimeout(TIMEOUT, TimeUnit.SECONDS)            .build();privatestaticRetrofitService retrofitService =newRetrofit.Builder()            .baseUrl(RetrofitService.BASE_URL)// 添加Gson转换器.addConverterFactory(GsonConverterFactory.create(newGsonBuilder()                    .setLenient()                    .create()            ))// 添加Retrofit到RxJava的转换器.addCallAdapterFactory(RxJava2CallAdapterFactory.create())            .client(httpClient)            .build()            .create(RetrofitService.class);//获得RetrofitService对象publicstaticRetrofitServicegetInstance() {returnretrofitService;    }}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

4.使用

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

RetrofitFactory.getInstance()//获取retrofitService对象.test()//测试接口.subscribeOn(Schedulers.io())                .doOnSubscribe(newConsumer() {@Overridepublicvoidaccept(@NonNull Disposable disposable)throwsException {//将这个请求的Disposable添加进入CompositeDisposable同一管理(在封装的presenter中)addDisposable(disposable);//访问网络显示dialogview.showLoadingDialog("");                    }                })                .map(newFunction>() {@OverridepublicListapply(@NonNull TestBean testBean)throwsException {//转化数据returntestBean.getStories();                    }                })//获得的数据返回主线程去更新界面.observeOn(AndroidSchedulers.mainThread())                .subscribe(newConsumer>() {@Overridepublicvoidaccept(@NonNull List storiesBeen)throwsException {//消失dialogview.dismissLoadingDialog();//设置数据view.setData(storiesBeen);                    }                },newConsumer() {@Overridepublicvoidaccept(@NonNull Throwable throwable)throwsException {                        view.dismissLoadingDialog();                        String exception = ExceptionHelper.handleException(throwable);//打印出错误信息Log.e("TAG","exception: "+ exception);                    }                });

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

好我们来分析一下,

首先先获得一个retrofitService对象

然后调用test接口。

访问网络在子线程

在访问网络的时候显示等待对话框,将这个请求加入CompositeDisposable中(在basePresenter封装了统一管理的方法,调用addDisposable(disposable);最后Activity关闭,取消所有网络请求,防止内存泄漏)

将网络获取的数据转换成你需要的数据

线程卡点结果返回主线程

订阅得到数据更新界面,处理错误信息

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

四: 打造MVP

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

五:分析

好我们来分析一下mvp

1.view需要找presenter拿数据,那么view里面需要一个presenter对象。

2.presenter需要给view数据,那么presenter也需要一个view对象。

3.model层访问网络使用RxJava+retrofit,数据回调给presenter(后面分析)

思考

所有的view里面都需要什么操作呢? 所有的presenter里面都需要什么操作呢?

暂时在我的需求中view和presenter只有如下这么几个功能,当然,如果你还有其他的功能可以再加上去。

publicinterfaceBaseView {//显示dialogvoidshowLoadingDialog(String msg);//取消dialogvoiddismissLoadingDialog();}

1

2

3

4

5

6

7

8

9

publicinterfaceBasePresenter {//默认初始化voidstart();//Activity关闭把view对象置为空voiddetach();//将网络请求的每一个disposable添加进入CompositeDisposable,再退出时候一并注销voidaddDisposable(Disposable subscription);//注销所有请求voidunDisposable();}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

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

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

所以我们先看下代码:

publicabstractclassBaseActivityextendsAppCompatActivityimplementsBaseView{protectedP presenter;publicContext context;    @OverrideprotectedvoidonCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);        context =this;        ActivityManager.getAppInstance().addActivity(this);//将当前activity添加进入管理栈presenter = initPresenter();    }    @OverrideprotectedvoidonDestroy() {        ActivityManager.getAppInstance().removeActivity(this);//将当前activity移除管理栈if(presenter !=null) {            presenter.detach();//在presenter中解绑释放viewpresenter =null;        }super.onDestroy();    }/**

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

*

* @return 相应的presenter

*/publicabstractP initPresenter();    @OverridepublicvoiddismissLoadingDialog() {    }    @OverridepublicvoidshowLoadingDialog(String msg) {    }}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

publicabstractclassBasePresenterImplimplementsBasePresenter{public BasePresenterImpl(V view) {this.view = view;        start();    }protectedV view;//给子类使用view@Overridepublic void detach() {this.view =null;        unDisposable();    }@Overridepublic void start() {    }-----------------------我是分割线--------------------------------//以下下为配合RxJava2+retrofit2使用的//将所有正在处理的Subscription都添加到CompositeSubscription中。统一退出的时候注销观察privateCompositeDisposable mCompositeDisposable;/**    * 将Disposable添加    *    *@paramsubscription    */@Overridepublic void addDisposable(Disposable subscription) {//csb 如果解绑了的话添加 sb 需要新的实例否则绑定时无效的if(mCompositeDisposable ==null|| mCompositeDisposable.isDisposed()) {            mCompositeDisposable =newCompositeDisposable();        }        mCompositeDisposable.add(subscription);    }/**

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

*/@Overridepublic void unDisposable() {if(mCompositeDisposable !=null) {            mCompositeDisposable.dispose();        }    }}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

创建activity中泛型传入相应的view接口,presenter中泛型传入相应的presenter接口

activity中onCreate中初始化presenter,onDestroy中调用detach,将presenter中正在执行的任务取消,将view对象置为空。

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

六:使用

好的接下来我们来使用一下吧

首先我们先来个简单的需求:

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

创建Contact管理接口

首先先思考view需要设置数据所有view中需要一个setData方法

presenter需要去访问网络所以需要一个getData方法。代码如下:

publicinterfaceTestContact{interfaceviewextendsBaseView{/**        * 设置数据        *        *@paramdataList        */void setData(List dataList);    }interfacepresenterextendsBasePresenter{/**

* 获取数据

*/void getData();    }}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

创建Activity和presenter

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

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

代码如下:

publicclassTestActivityextendsBaseActivityimplementsTestContact.view{privateList list =newArrayList<>();//数据privateRecyclerView recyclerView;privateTestAdapter adapter;@Overrideprotectedvoid onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);        setContentView(R.layout.activity_test);        init();        presenter.getData();    }/**

* 初始化界面

*/privatevoid init() {        recyclerView = (RecyclerView) findViewById(R.id.recycleview);        recyclerView.setLayoutManager(newLinearLayoutManager(this));        adapter =newTestAdapter(list);        recyclerView.setAdapter(adapter);    }/**    * 初始化presenter    *    *@return对应的presenter    */@Overridepublic TestContact.presenter initPresenter() {returnnewTestPresenter(this);    }/**    * 设置数据    * 刷新界面    *    *@paramdataList 数据源    */@Overridepublic void setData(List dataList) {        list.addAll(dataList);        adapter.notifyDataSetChanged();    }}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

publicclassTestPresenterextendsBasePresenterImplimplementsTestContact.presenter{publicTestPresenter(TestContact.view view) {        super(view);    }/**

* 获取数据

*/@Overridepublicvoid getData() {        Api.getInstance()                .test()//测试接口.subscribeOn(Schedulers.io())                .doOnSubscribe(newConsumer() {                    @Overridepublicvoid accept(@NonNull Disposable disposable) throwsException{                        addDisposable(disposable);//请求加入管理view.showLoadingDialog("");                    }                })                .map(newFunction>(){@OverridepublicList apply(@NonNull TestBean testBean) throwsException{returntestBean.getStories();//转换数据}                })                .observeOn(AndroidSchedulers.mainThread())                .subscribe(newConsumer>() {                    @Overridepublicvoid accept(@NonNullList storiesBeen) throwsException{                        view.dismissLoadingDialog();                        view.setData(storiesBeen);                    }                },newConsumer() {                    @Overridepublicvoid accept(@NonNull Throwable throwable) throwsException{                        view.dismissLoadingDialog();                        ExceptionHelper.handleException(throwable);                    }                });    }}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

七:分析

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

创建对应的类,实现对应的方法

Activity中只有一个recyclerView初始化它。

在onCreate中调用presenter中的getData()方法

在presenter中使用RxJava2+retrofit2访问网络。获取数据返回给view

view拿到数据更新界面

请接受我双手奉上的源码地址

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

推荐阅读更多精彩内容