(三)安卓框架搭建之MVP+Retrofit+RxJava基础
来源:互联网 发布:用相对坐标编程图例 编辑:程序博客网 时间:2024/05/18 02:58
上一篇,算是完成了准备工作,那么这篇就来说说MVP和RxJava的封装了。首先看看接口返回数据的格式:
{"code" : 1,"message" : "请求成功!" ,"data" : { "name": "张三", "age": 3 }}
code、message、data标准的三大门神。一般是以这种格式返回数据。数据格式的统一利于封装,以此数据格式为准的实体基类如下
在dataframework内新建包model和BaseResponseBean类。
package com.example.burro.demo.dataframework.model;/**基类 泛型T为实体数据 * Created by ex.zhong on 2017/9/23. */public class BaseResponseBean<T> { private int code; private String message; private T data; public int getCode() { return code; } public void setCode(int code) { this.code = code; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public T getData() { return data; } public void setData(T data) { this.data = data; }
由于后面的demo用到豆瓣的API,很遗憾它的格式并非上述标准的格式。在项目中返回的数据用了继承父类方式,而非上面的泛型方式,为了区分。新的父类名字我改为BaseResultBean
,上面的标准格式基类我仍旧保存到demo中,如果更换的话,那也是分分钟的事情。后面案例和讲解也将使用BaseResultBean,
其内容如下:
package com.example.burro.demo.dataframework.model;/**返回数据父类。子类可继承 * Created by ex.zhong on 2017/9/23. */public class BaseResultBean { protected int code; protected String msg; public BaseResultBean() { } public BaseResultBean(int code, String msg) { this.code = code; this.msg = msg; } public int getCode() { return code; } public void setCode(int code) { this.code = code; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; }}
当然,项目中要根据实际数据为准来调整调整字段、结构等。没有必要过于纠结数据格式问题,换汤不换药,道理是一样的。
下面进行MVP相关内容的讲解!
依赖包引入:
项目的build.gradle增加如下appframework
/*rx-android-java*/ rxjava : 'io.reactivex:rxjava:1.1.0', rxandroid : 'io.reactivex:rxandroid:1.1.0', retrofit : 'com.squareup.retrofit2:retrofit:2.0.2', converter_gson : 'com.squareup.retrofit2:converter-gson:2.0.2', adapter_rxjava : 'com.squareup.retrofit2:adapter-rxjava:2.0.2', //compile 'com.google.code.gson:gson:2.6.2' logging_interceptor : 'com.squareup.okhttp3:logging-interceptor:3.3.0', spots_dialog : 'com.github.d-max:spots-dialog:0.7@aar',
dataframework的build.gradle增加如下
compile deps.rxjava compile deps.rxandroid compile deps.retrofit compile deps.converter_gson compile deps.adapter_rxjava compile deps.logging_interceptor compile deps.spots_dialog compile deps.annotation
BaseView
写之前需要在appframework
下新建包mvp
,mvp下新建三个包contract
,presenter
,view
在view下新建接口BaseView
,Baseview接口内的方法是页面内【Activity或者Fragment】需要执行的通用方法。这里先定义一个 showError(BaseResultBean resultBean);
返回正确情况有很多种,在实现类中增加,若错误,我们要统一处理。所以showError(BaseResultBean resultBean)方法是全局共有的。
package com.example.burro.demo.appframework.mvp.view;import com.example.burro.demo.dataframework.model.BaseResultBean;/**View接口 * Created by ex.zhong on 2017/9/23. */public interface BaseView { void showError(BaseResultBean resultBean);}
BasePresenter
Presenter和View创建类似,
在presenter下新建IPresenter
,IPresenter attachView(T view); void detachView();两个方法是全局共有的
package com.example.burro.demo.appframework.mvp.presenter;import com.example.burro.demo.appframework.mvp.view.BaseView;/**Presenter接口 * 注:在创建presenter时绑定,在页面destroy()时解绑。 * Created by ex.zhong on 2017/9/23. */public interface IPresenter<T extends BaseView> { void attachView(T view); void detachView();}
因为几乎每个Presenter实现类里都要处理绑定和解绑事件,所以我们要把这个处理过程提取出来,此处写一个基类BasePresenter统一管理,在presenter下新建BasePresenter
实现IPresenter
package com.example.burro.demo.appframework.mvp.presenter;import com.example.burro.demo.appframework.mvp.view.BaseView;/** * Presenter基类。目的是统一处理绑定和解绑 * Created by ex.zhong on 2017/9/23. */public class BasePresenter<T extends BaseView> implements IPresenter<T> { protected T mView; @Override public void attachView(T mView) { mView = mView; } @Override public void detachView() { mView = null; } // public boolean isViewAttached() { // return mView != null; // } // public void checkViewAttached() { // if (!isViewAttached()) throw new // MvpViewNotAttachedException(); // }![Uploading 04_766476.png . . .] // public static class //MvpViewNotAttachedException extends //RuntimeException { // public MvpViewNotAttachedException() { // super("Please call //Presenter.attachView(MvpView) before" + // " requesting data to the //Presenter"); // } // }}
【备注:这里的checkViewAttached(),在rxJava未引入之前使用。目的是判断页面是否还存在,若不存在则不执行。rxJava中对此作了处理。只需调用解绑方法即可。在此处稍作提及,后面我会直接删掉此内容】
稍后会写一个测试类TestActivty结合豆瓣的API。来详解mvp的使用。在此之前先来封装一下BaseActivity,因为一般都是在BaseActivity中进行Presenter和View的初始化绑定
BaseActivity
在appframework新建ui包,包内新建BaseActivity抽象类
类中都是基本的要素,且注释较为详细,容易理解。其中有个别方法是为了和后面内容对接,直接贴代码:
package com.example.burro.demo.appframework.ui;import android.app.Activity;import android.os.Bundle;import android.support.annotation.Nullable;import android.support.v7.app.AppCompatActivity;import android.support.v7.widget.Toolbar;import android.view.MenuItem;import android.view.View;import com.example.burro.demo.appframework.BaseApplication;import com.example.burro.demo.appframework.mvp.presenter.BasePresenter;import com.example.burro.demo.appframework.mvp.view.BaseView;import butterknife.ButterKnife;import butterknife.Unbinder;/** * BaseActivity Activity基类 * butterKnife的绑定 初始方法的设定 presentet和view的绑定 * Created by ex.zhong on 2017/9/23. */public abstract class BaseActivity<T extends BasePresenter> extends AppCompatActivity implements BaseView,Toolbar.OnMenuItemClickListener { protected T mPresenter; protected Activity mContext; private Unbinder mUnbinder; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(initLayoutInflater()); mUnbinder = ButterKnife.bind(this); mContext = this; createPresenter(); if (mPresenter != null) mPresenter.attachView(this); BaseApplication.getInstance().addActivity(this); initParams(); initViews(); } protected abstract int initLayoutInflater(); //初始化布局 protected abstract void initParams(); //初始化参数 protected abstract void initViews(); //初始化控件 protected abstract void createPresenter(); //创建presenter /** * @param toolbar toolbar 控件 * @param title 标题 */ protected void setToolBar(Toolbar toolbar, String title) { if (toolbar != null) { if (title != null) toolbar.setTitle(title); setSupportActionBar(toolbar); toolbar.setOnMenuItemClickListener(this); getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setDisplayShowHomeEnabled(true); toolbar.setNavigationOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { onBackPressed(); } }); } } //toolbar右侧menu点击事件 @Override public boolean onMenuItemClick(MenuItem item) { return false; } //统一处理错误信息 public void handleError(BaseResultBean errResult) { if (errResult == null) return; if (this == null) return; //可以分门别类的处理 错误消息,如session过期,跳转到登录页面。其他情况提示即可 ToastUtils.showToast(mContext, errResult.getMsg()); } @Override protected void onDestroy() { if (mPresenter != null) mPresenter.detachView(); if (mUnbinder != null) mUnbinder.unbind(); super.onDestroy(); }}
在biz新建测试类
新建内容如下 biz/test/view/TestActivity、biz/test/TestContract、biz/test/TestPresenterImpl
1.包内新建TestActivity继承BaseActivity.TestActivity
package com.example.burro.demo.appbiz.test.view;import com.example.burro.demo.appbiz.R;import com.example.burro.demo.appbiz.R2;import com.example.burro.demo.appbiz.test.TestContract;import com.example.burro.demo.appbiz.test.TestPresenterImpl;import com.example.burro.demo.appframework.ui.BaseActivity;import com.example.burro.demo.appframework.util.LogUtils;import com.example.burro.demo.databiz.model.test.MovieListBean;import com.example.burro.demo.dataframework.model.BaseResultBean;import butterknife.OnClick;/**测试页面 * Created by ex.zhong on 2017/9/23. */public class TestActivity extends BaseActivity<TestPresenterImpl> implements TestContract.View{ @Override protected int initLayoutInflater() { return R.layout.activity_test; } @Override protected void initParams() { } @Override protected void initViews() { } @Override protected void createPresenter() { mPresenter = new TestPresenterImpl(); } @Override public void showError(BaseResultBean resultBean) { //错误处理 handleError(resultBean); } @Override public void setMovieListData(MovieListBean bean) { LogUtils.i("TAG",bean==null?"":bean.toString()); } @OnClick(R2.id.btnTest) public void getMovieListData(){ mPresenter.getMovieListData(1,15); }}
这里的showError(),是BasePresenter中的回调方法,用来统一处理错误情况,若页面有RecycalView并正在刷新的情况,也可在此处结束刷新。因为每个页面都会showError(),所以我们需要在BaseActivity里增加统一处理的方法handleError(resultBean),内容如下:
//统一处理错误信息 public void handleError(BaseResultBean errResult) { if (errResult == null) return; if (this == null) return; //可以分门别类的处理 错误消息,如session过期,跳转到登录页面。其他情况提示即可 ToastUtils.showToast(mContext, errResult.getMsg()); }
值得强调的是,增加错误结果统一处理很有必要,也很少有人注意这点,我们后面网络请求错误结果的返回也会与此对接。此处默认是给出Toast提示信息,当然还有很多其他操作,正如注释所说:如果errResult的code是session过期的标识,那么我们给出提示的同时也会跳转至登录页面等等。
2.TestContract:
Contract:d单词意思为契约、协议。TestContract即协议类,定制mvp各层接口和实现方法。说白了,就是把v层和p层需要实现的方法统一在一块,方便管理,也起到了解耦作用。
package com.example.burro.demo.appbiz.test;import com.example.burro.demo.appframework.mvp.presenter.IPresenter;import com.example.burro.demo.appframework.mvp.view.BaseView;import com.example.burro.demo.databiz.model.test.MovieListBean;/**协议类,定制mvp各层接口和实现方法 * Contract:d单词意思为契约 协议 * 接口View内 定义实现view内所需方法 * 接口Presenter 定义实现presenter内所需的方法 * Created by ex.zhong on 2017/9/23. */public class TestContract { public interface View extends BaseView { void setMovieListData(MovieListBean bean); } public interface Presenter extends IPresenter<View> { void getMovieListData(int start,int count); }}
3.TestPresenterImpl:
TestPresenterImpl继承自BasePresenter,初级版本如下:
package com.example.burro.demo.appbiz.test;import com.example.burro.demo.appframework.mvp.presenter.BasePresenter;import com.example.burro.demo.appbiz.test.TestContract.*;import com.example.burro.demo.appframework.util.LogUtils;import com.example.burro.demo.appframework.util.StringUtils;import com.example.burro.demo.databiz.model.test.MovieListBean;import com.example.burro.demo.databiz.service.ApiService;import com.example.burro.demo.dataframework.http.HttpConfig;import java.util.HashMap;import retrofit2.Retrofit;import retrofit2.adapter.rxjava.RxJavaCallAdapterFactory;import retrofit2.converter.gson.GsonConverterFactory;import rx.Subscriber;import rx.android.schedulers.AndroidSchedulers;import rx.schedulers.Schedulers;/**测试presenter * Created by ex.zhong on 2017/9/23. */public class TestPresenterImpl extends BasePresenter<View> implements Presenter { @Override public void getMovieListData(int start, int count) { Retrofit retrofit = new Retrofit.Builder() .baseUrl(HttpConfig.BASE_URL) .addConverterFactory(GsonConverterFactory.create()) .addCallAdapterFactory(RxJavaCallAdapterFactory.create()) .build(); ApiService movieService = retrofit.create(ApiService.class); HashMap<String,String> map=new HashMap<>(); map.put("start", StringUtils.getString(start)); map.put("count",StringUtils.getString(count)); movieService.getMovieListData(map) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Subscriber<MovieListBean>() { @Override public void onStart() { //请求开始 LogUtils.i("TestPresenterImpl","onStart()"); } @Override public void onCompleted() { // //请求完成 LogUtils.i("TestPresenterImpl","onCompleted()"); } @Override public void onError(Throwable e) { // //请求异常 LogUtils.i("TestPresenterImpl","onError()"); } @Override public void onNext(MovieListBean movieListBean) { // //请求OK,执行 LogUtils.i("TestPresenterImpl","onNext()"); mView.setMovieListData(movieListBean); } }); }}
文中用到的豆瓣电影TOP250的URL为:http://api.douban.com/v2/movie/top250?start=1&count=15
其他几个主要辅助的类或资源分别为如下:
布局文件activity_test.xml:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.example.burro.demo.appbiz.test.view.TestActivity"> <android.support.design.widget.AppBarLayout android:id="@+id/appBarLayout" android:layout_width="match_parent" android:layout_height="wrap_content" android:theme="@style/AppTheme.AppBarOverlay"> <android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" app:popupTheme="@style/AppTheme.PopupOverlay" /> </android.support.design.widget.AppBarLayout> <Button android:id="@+id/btnTest" android:layout_below="@id/appBarLayout" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="请求数据" /></RelativeLayout>
其中AppTheme.PopupOver,AppTheme.AppBarOverlay等是toolbar相关的样式资源,请到demo中查看,此处不一一列出
ApiService接口类 databiz/service/ApiService:
package com.example.burro.demo.databiz.service;import com.example.burro.demo.databiz.model.test.MovieListBean;import java.util.HashMap;import java.util.Map;import retrofit2.http.GET;import retrofit2.http.Path;import retrofit2.http.Query;import retrofit2.http.QueryMap;import rx.Observable;/** * 存放访问网络的方法 * Created by ex.zhong on 2017/9/24. */public interface ApiService { public static final String URL_MOVIELIST="/v2/movie/top250"; //豆瓣电影top250 @GET(URL_MOVIELIST) Observable<MovieListBean> getMovieListData(@QueryMap HashMap<String,String> count);}
网络配置类 dataframework/http/HttpConfig :
package com.example.burro.demo.dataframework.http;/** * Created by ex.zhong on 2017/9/24. *放置网络相关配置数据,如IP/端口等 */public class HttpConfig { public final static String BASE_URL="http://api.douban.com";}
电影列表实体类 databiz/model/test/MovieListBean :
package com.example.burro.demo.databiz.model.test;import com.example.burro.demo.dataframework.model.BaseResultBean;import java.util.List;/**豆瓣电影列表 * Created by ex.zhong on 2017/9/24. */public class MovieListBean extends BaseResultBean{ public List<SubjectsBean> subjects; public static class SubjectsBean { /** * rating : {"max":10,"average":9.6,"stars":"50","min":0} * genres : ["犯罪","剧情"] * title : 肖申克的救赎 * casts : [{"alt":"https://movie.douban.com/celebrity/1054521/","avatars":{"small":"https://img3.doubanio.com/img/celebrity/small/17525.jpg","large":"https://img3.doubanio.com/img/celebrity/large/17525.jpg","medium":"https://img3.doubanio.com/img/celebrity/medium/17525.jpg"},"name":"蒂姆·罗宾斯","id":"1054521"},{"alt":"https://movie.douban.com/celebrity/1054534/","avatars":{"small":"https://img3.doubanio.com/img/celebrity/small/34642.jpg","large":"https://img3.doubanio.com/img/celebrity/large/34642.jpg","medium":"https://img3.doubanio.com/img/celebrity/medium/34642.jpg"},"name":"摩根·弗里曼","id":"1054534"},{"alt":"https://movie.douban.com/celebrity/1041179/","avatars":{"small":"https://img1.doubanio.com/img/celebrity/small/5837.jpg","large":"https://img1.doubanio.com/img/celebrity/large/5837.jpg","medium":"https://img1.doubanio.com/img/celebrity/medium/5837.jpg"},"name":"鲍勃·冈顿","id":"1041179"}] * collect_count : 1107705 * original_title : The Shawshank Redemption * subtype : movie * directors : [{"alt":"https://movie.douban.com/celebrity/1047973/","avatars":{"small":"https://img3.doubanio.com/img/celebrity/small/230.jpg","large":"https://img3.doubanio.com/img/celebrity/large/230.jpg","medium":"https://img3.doubanio.com/img/celebrity/medium/230.jpg"},"name":"弗兰克·德拉邦特","id":"1047973"}] * year : 1994 * images : {"small":"https://img3.doubanio.com/view/movie_poster_cover/ipst/public/p480747492.webp","large":"https://img3.doubanio.com/view/movie_poster_cover/lpst/public/p480747492.webp","medium":"https://img3.doubanio.com/view/movie_poster_cover/spst/public/p480747492.webp"} * alt : https://movie.douban.com/subject/1292052/ * id : 1292052 */ public RatingBean rating; public String title; public int collect_count; public String original_title; public String subtype; public String year; public ImagesBean images; public String alt; public String id; public List<String> genres; public List<CastsBean> casts; public List<DirectorsBean> directors; public static class RatingBean { /** * max : 10 * average : 9.6 * stars : 50 * min : 0 */ public int max; public double average; public String stars; public int min; } public static class ImagesBean { /** * small : https://img3.doubanio.com/view/movie_poster_cover/ipst/public/p480747492.webp * large : https://img3.doubanio.com/view/movie_poster_cover/lpst/public/p480747492.webp * medium : https://img3.doubanio.com/view/movie_poster_cover/spst/public/p480747492.webp */ public String small; public String large; public String medium; } public static class CastsBean { /** * alt : https://movie.douban.com/celebrity/1054521/ * avatars : {"small":"https://img3.doubanio.com/img/celebrity/small/17525.jpg","large":"https://img3.doubanio.com/img/celebrity/large/17525.jpg","medium":"https://img3.doubanio.com/img/celebrity/medium/17525.jpg"} * name : 蒂姆·罗宾斯 * id : 1054521 */ public String alt; public AvatarsBean avatars; public String name; public String id; public static class AvatarsBean { /** * small : https://img3.doubanio.com/img/celebrity/small/17525.jpg * large : https://img3.doubanio.com/img/celebrity/large/17525.jpg * medium : https://img3.doubanio.com/img/celebrity/medium/17525.jpg */ public String small; public String large; public String medium; } } public static class DirectorsBean { /** * alt : https://movie.douban.com/celebrity/1047973/ * avatars : {"small":"https://img3.doubanio.com/img/celebrity/small/230.jpg","large":"https://img3.doubanio.com/img/celebrity/large/230.jpg","medium":"https://img3.doubanio.com/img/celebrity/medium/230.jpg"} * name : 弗兰克·德拉邦特 * id : 1047973 */ public String alt; public AvatarsBeanX avatars; public String name; public String id; public static class AvatarsBeanX { /** * small : https://img3.doubanio.com/img/celebrity/small/230.jpg * large : https://img3.doubanio.com/img/celebrity/large/230.jpg * medium : https://img3.doubanio.com/img/celebrity/medium/230.jpg */ public String small; public String large; public String medium; } } }}
点击请求数据按钮。获取到返回的数据如下:
![列表
当然,TestPresenterImpl中的内容是重点,其中getMovieListData()方法里的内容是retrofit和rxjava最基本的用法!想必大家多少都见过。我们再来看下rxjava相关的代码,其实它主要做了三个事情。统一管理主线程、工作线程、请求返回后的回调处理!引入rxjava之前,三者都是自己管理。所以说,它的引入极大的简化了我们的工作。
下一篇将讲述优化封装
相关链接
(四)安卓框架搭建之MVP+Retrofit+RxJava优化
github源码地址
- (三)安卓框架搭建之MVP+Retrofit+RxJava基础
- (四)安卓框架搭建之MVP+Retrofit+RxJava优化
- android MVP + dagger2 + Retrofit + Rxjava+okhttp android基础项目框架搭建(2)--之MVP引入
- android MVP + dagger2 + Retrofit + Rxjava+okhttp android基础项目框架搭建(1)--之Dagger2引入
- 从0开始搭建rxjava+retrofit+mvp+dagger2整合基础框架(rxjava+retrofit网络层搭建)
- 安卓开发框架(MVP+主流框架+基类+工具类)--- Retrofit+RxJava
- android MVP + dagger2 + Retrofit + Rxjava+okhttp android基础项目框架搭建(3)--完美实现
- 一步步搭建Retrofit+RxJava+MVP网络请求框架(一)
- 一步步搭建Retrofit+RxJava+MVP网络请求框架(一)
- Rxjava + retrofit + dagger2 + mvp搭建Android框架
- 搭建MVP+Retrofit+RxJava框架详解
- Retrofit+RXJava+MVP的框架搭建
- RxJava+Retrofit+MVP框架
- Android Mvp快速搭建框架MVP+Dagger2+Retrofit+Rxjava
- Android 一步步搭建MVP+Retrofit+RxJava网络请求框架
- Android 搭建MVP+Retrofit+RxJava网络请求框架
- MVP+Retrofit+RxJava+Dagger框架
- 【BaseFragment】基于MVP+Dagger2+Retrofit+Rxjava框架之BaseFragment
- 广度优先搜索--迷宫问题(poj 3984)
- python3.7连接mysql
- 计算机视觉
- Maven学习总结(42)——Maven多模块构建中常用的参数
- 我为什么写博客
- (三)安卓框架搭建之MVP+Retrofit+RxJava基础
- 017-101-Symmetric Tree 判断树是否对称
- Java笔记5
- HDU-3123 GCC(水题)
- Qt学习之调试
- C语言常见错误
- 关于$_SERVER中的PHP_SELF、REQUEST_URI以及SCRIPT_NAME的区别
- apache和tomcat有什么不同,为什么要整合apache 和tomcat?
- 最大公约数-辗转相除法