前言
该框架的内容是我在工作中自己积累下来的,现分享给大家。由于毕业后我只身一人负责公司的Android项目,都是一些小型的项目,自身能力也着实有限,如有不妥之处,还望大家指正。
我的GitHub主页:https://github.com/BigWolfDean
本框架的GitHub地址:https://github.com/BigWolfDean/Mvp-Rxjava2-Retrofit2-for-Android
说明
本篇文章主要是为了分享框架,不为教学,所以这里关于MVP、RxJava2以及Retrofit2相关的知识不作讲解,如有这几个方面没有任何基础的同学,推荐以下几个链接供大家学习:
以上这些文章都是我看过且学习过的,都是非常值得学习的,没有基础的童鞋们可以先进行学习哦。
项目结构
说了这么多,现在咱们就直奔主题了,先来看一下项目的包结构
从图中可以看出,本工程第一层共分了4个包
- bean 用来存放JavaBean文件,用于Json数据转换
- mvp 用来存放mvp模式下的内容,包含子包 manager、presenter和view
- retrofit 用来存放retrofit相关内容
- rx 用来存放rxJava&RxAndroid相关内容
到这里可能有人会疑惑MVP的结构不应该是Model、View和Presenter吗?为什么这里是manager不是model?
我们先来回顾一下MVP的思想吧。
MVP的基本思想:Presenter负责逻辑的处理,Model提供数据,View负责显示
MVP
DataManager层
Model是用来提供数据的,那我们DataManager是干嘛的呢,我们来看一下代码:
/**
* Created by lang on 2017/12/8.
* 数据管理类
* 用于获取retrofitService的实例及生成并返回对应接口的Observable实例
*/
public class DataManager {
private RetrofitService retrofitService;
public DataManager() {
this.retrofitService = RetrofitFactory.getInstance();
}
/**
* 此处返回 Observable实例,由Presenter层调用
*
* @param apiKey
* @param city
* @param start
* @param count
* @return
*/
public Observable<InTheaterBean> getMovieInTheater(String apiKey, String city, int start, int count) {
return retrofitService.getMovieInTheater(apiKey, city, start, count);
}
}
我们可以看到DataManager中getMovieInTheater方法返回的是Observable实例,那么我们就会发现,这一步操作已然就是对外提供了数据,那么这个DataManager也就是DataModel,这便是传统Model层与RxJava结合的产物。
View层
IBaseView
/**
* Created by lang on 2017/12/8.
* View层的基类,可自行根据项目需要在此指定公用的方法
*/
public interface IBaseView {
}
InTheaterMovieView
/**
* Created by lang on 2018/2/6.
* 继承自IBaseView
* 声明View层方法
*/
public interface InTheaterMovieView extends IBaseView {
//设置从网络上获取到的结果
void setResponse(String response);
//设置错误信息 包含系统返回的错误和开发者自定义的错误
void setError(String error);
}
Presenter层
IPresenter
/**
* Created by lang on 2017/12/8.
* 利用IPresenter来管理业务流程
* onCreate 可用来做初始化操作,如类的实例化等
* onStart 可用来做初始化后的操作,如变量的赋值等
* onPause 根据项目需要决定内容
* onStop 同上
* attachView 绑定继承自IBaseView的接口,用于view层的展示
* attachInComingIntent 绑定从外部传进来的Intent,对Intent相关进行操作
* 推荐使用顺序 onCreate -> onStart -> attachView -> attachingInComingIntent -> onPause -> onStop
* 注意: 使用的过程中一定要根据自己制定的流程来进行操作,否则位置一乱就会发生各种错误
*/
public interface IPresenter {
void onCreate();
void onStart();
void onPause();
void onStop();
// 绑定view
void attachView(IBaseView view);
// 绑定intent
void attachIncomingIntent(Intent intent);
}
InTheaterMoviePresenter
/**
* Created by lang on 2018/2/6.
* 实现IPresenter接口
* 重写对应方法
*/
public class InTheaterMoviePresenter implements IPresenter {
private InTheaterMovieView movieView;
private DataManager dataManager;
@Override
public void onCreate() {
dataManager = new DataManager();
}
@Override
public void onStart() {
}
@Override
public void onPause() {
}
@Override
public void onStop() {
}
@Override
public void attachView(IBaseView view) {
movieView = (InTheaterMovieView) view;
}
@Override
public void attachIncomingIntent(Intent intent) {
}
/**
* 获取豆瓣正在上映的电影信息
*
* @param apiKey
* @param city
* @param start
* @param count
*/
public void getMovieInfo(String apiKey, String city, int start, int count) {
dataManager.getMovieInTheater(apiKey, city, start, count).compose(RxSchedulers.<InTheaterBean>compose()).subscribe(new BaseObserver<InTheaterBean>() {
@Override
protected void onHandleSuccess(InTheaterBean inTheaterBean) {
// 成功的操作
movieView.setResponse(inTheaterBean.toString());
}
@Override
protected void onHandleError(Throwable e) {
// 失败的操作
movieView.setError(e.getMessage());
}
});
}
/**
* 设置错误信息
*
* @param errorInfo
*/
public void setErrorInfo(String errorInfo) {
movieView.setError(errorInfo);
}
}
Retrofit
RetrofitFactory
/**
* Created by lang on 2017/11/21.
* Retrofit工厂类
*/
public class RetrofitFactory {
private static final String BASE_URL = "https://api.douban.com/v2/"; // 根路由
private static final long TIMEOUT = 30; //超时时间,此处单位为秒
// Retrofit是基于OkHttpClient的,可以创建一个OkHttpClient进行一些配置
private static OkHttpClient httpClient = new OkHttpClient.Builder()
// 添加通用的Header
.addInterceptor(new Interceptor() {
@Override
public Response intercept(@NonNull Chain chain) throws IOException {
//此处可根据项目实际需求自行设置header内容
Request.Builder builder = chain.request().newBuilder();
builder.addHeader("key", "value");
return chain.proceed(builder.build());
}
})
/*
这里可以添加一个HttpLoggingInterceptor,因为Retrofit封装好了从Http请求到解析,
出了bug很难找出来问题,添加HttpLoggingInterceptor拦截器方便调试接口
*/
.addInterceptor(new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
@Override
public void log(@NonNull String message) {
Log.e("HttpLoggingInterceptor", message);
}
}).setLevel(HttpLoggingInterceptor.Level.BODY)) //此处级别有NONE,BASIC,HEADERS和BODY
.connectTimeout(TIMEOUT, TimeUnit.SECONDS) //连接超时时间,单位秒
.readTimeout(TIMEOUT, TimeUnit.SECONDS) //连接超时时间,单位秒
.build();
private static RetrofitService retrofitService = new Retrofit.Builder()
.baseUrl(BASE_URL)
// 添加Gson转换器
.addConverterFactory(GsonConverterFactory.create(buildGson()))
// 添加Retrofit到RxJava的转换器
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(httpClient)
.build()
.create(RetrofitService.class);
public static RetrofitService getInstance() {
return retrofitService;
}
private static Gson buildGson() {
return new GsonBuilder()
.serializeNulls()
.setFieldNamingPolicy(FieldNamingPolicy.IDENTITY)
.create();
}
}
RetrofitService
/**
* Created by lang on 2018/02/06.
* RetrofitService接口
*/
public interface RetrofitService {
/**
* @param token 固定值`0b2bdeda43b5688921839c8ecb20399b`
* @param city 所在城市,例如`北京`、`上海`等
* @param start 分页使用,表示第几页
* @param count 分页使用,表示数量
* @return Observable实例
*/
@GET("movie/in_theaters")
Observable<InTheaterBean> getMovieInTheater(@Query("apikey") String token,
@Query("city") String city,
@Query("start") int start,
@Query("count") int count);
}
Rx
BaseObserver
/**
* Created by lang on 2017/11/21.
* Observer基类
*/
public abstract class BaseObserver<T> implements Observer<T> {
private static final String TAG = "BaseObserver";
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(T value) {
onHandleSuccess(value);
}
@Override
public void onError(Throwable e) {
onHandleError(e);
Log.e(TAG, "error:" + e.toString());
}
@Override
public void onComplete() {
Log.d(TAG, "onComplete");
}
protected abstract void onHandleSuccess(T t);
protected abstract void onHandleError(Throwable e);
}
RxSchedulers
/**
* Created by lang on 2017/11/21.
* Rx调度器,指定线程在IO
*/
public class RxSchedulers {
public static <T> ObservableTransformer<T, T> compose() {
return new ObservableTransformer<T, T>() {
@Override
public ObservableSource<T> apply(Observable<T> observable) {
return observable
.subscribeOn(Schedulers.io())
.doOnSubscribe(new Consumer<Disposable>() {
@Override
public void accept(Disposable disposable) throws Exception {
//这里可自行做些操作,如判断网络连接状态等
}
})
.observeOn(AndroidSchedulers.mainThread());
}
};
}
}
如何关联?
以上所贴出的代码,我也就不多做描述了,结合里面的注释看,只要是有基础的童鞋都能看的明白。那么现在的问题是这些东西是如何关联起来的,这里我贴出部分Activity中的代码,具体的代码可以从GitHub上clone哦~
MainActivity重点代码:
(1)
private InTheaterMoviePresenter moviePresenter = new InTheaterMoviePresenter();
(2)
private InTheaterMovieView inTheaterMovieView = new InTheaterMovieView() {
@Override
public void setResponse(String response) {
tvResponse.setText(response);
}
@Override
public void setError(String error) {
tvResponse.setText(error);
}
};
(3)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
moviePresenter.onCreate();
moviePresenter.onStart();
moviePresenter.attachView(inTheaterMovieView);
}
(4)
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.btn_request:
String city = etCity.getText().toString();
String countString = etCount.getText().toString();
String startString = etStart.getText().toString();
String apiKey = etApiKey.getText().toString();
if (!TextUtils.isEmpty(apiKey) && !TextUtils.isEmpty(city)
&& !TextUtils.isEmpty(countString)
&& !TextUtils.isEmpty(startString)) {
moviePresenter.getMovieInfo(apiKey, city, Integer.valueOf(startString), Integer.valueOf(countString));
} else {
moviePresenter.setErrorInfo("输入框不可为空");
}
break;
}
}
代码中主要截取了四块内容
- 实例化InTheaterMoviePresenter
- 声明InTheaterMovieView接口,复写setResponse和setError方法。
- 在onCreate中调用presenter中的方法,将已声明的view进行绑定
- 通过调用presenter中自定义的方法进行相关操作
总结
框架的相关介绍到此也就结束了,具体的内容欢迎大家去GitHub上clone代码,如有不足之处欢迎提Issue,如代码对您有些许的帮助,麻烦帮忙给个Star,谢谢!