MVP+Retrofit+RxJava探索之路
Posted 农佳技
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了MVP+Retrofit+RxJava探索之路相关的知识,希望对你有一定的参考价值。
引言
公司最近在开展资金存管项目,后台将启动新的项目,完全可以很好的规范Api接口,于是就规划把旧的网络框架改造一下。
现在网上很火的当然是 Retrofit+RxJava + OkHttp,功能强大,简单易用。
因此选用这套方案来改造网络库,并与旧网络库中的MVP进行整合。
MVP
MVP(Model View Presenter)其实就是一种项目的整体框架,能让你的代码变得更加简洁,模块职能划分明显,层次清晰,降低耦合度。说起框架大家可能还会想到MVC、MVVM,由于篇幅原因,这里就不讲MVC,MVVM了,大家可以自行Google。
由于Presenter的出现,View(Activity)可以不用直接和Model打交道,View(Activity)只用负责页面的显示和交互,剩下的和Model交互的事情都交给Presenter处理,Presenter通过Model获取到数据后再操控View(Activity)进行展示。
这样,Activity的任务就大大减小了,这便是MVP(Model 指的数据逻辑和实体模型,View指的是Activity,P就是Presenter)框架的工作方式。
Retrofit
在Retrofit官网的上的介绍:
A type-safe HTTP client for android and Java
翻译过来就是:
一个适用于Android和Java的类型安全的HTTP客户端
Retrofit 是一个 RESTful 的 HTTP 网络请求框架的封装。注意这里并没有说它是网络请求框架,主要原因在于网络请求的工作并不是由 Retrofit 来完成的,而是由 OkHttp 完成的。
Retrofit 2.0 开始内置了 OkHttp,前者专注于接口的封装,后者专注于网络请求的高效。
App应用程序通过 Retrofit 请求网络,实际上是使用 Retrofit 接口层封装请求参数、Header、Url 等信息,之后由 OkHttp 完成后续的请求操作,在服务端返回数据之后,OkHttp 将原始的结果交给 Retrofit,Retrofit再根据用户的需求对结果进行解析的过程。
讲到这里,你就会发现所谓 Retrofit,其实就是 Retrofitting OkHttp 了。
RxJava
在GitHub主页上的介绍:
a library for composing asynchronous and event-based programs using observable sequences for the Java VM
翻译过来就是:
一个在 Java VM 上使用可观测的序列来组成异步的、基于事件的程序的库
对于初学者来说太抽象了。简单来说,RxJava 的本质可以压缩为异步这一个词。说到根上,它就是一个实现异步操作的库,而别的都是基于这之上的。
然而同样是异步,为什么不用Android内置的AsyncTask / Handler / XXX / ...,而用它。
一个词:简洁
异步操作很关键的一点是程序的简洁性,因为在调度过程比较复杂的情况下,异步代码经常会既难写也难被读懂。 Android 创造的 AsyncTask 和Handler ,其实都是为了让异步代码更加简洁。RxJava 的优势也是简洁,但它的简洁的与众不同之处在于,随着程序逻辑变得越来越复杂,它依然能够保持简洁,并且RxJava最大特点是链式调用,使异步逻辑结构更清晰明了。
实践
前面讲了那么多,接下来看看这三者是如何配合使用的。打开Android Studio,新建一个项目。
项目结构如下:
在项目的包名下,建了六个主要的文件夹:activity、application、entity、contract、present、model。当然根据项目的需要也可以添加更多其他的文件夹,比如一些工具类,控件类等。假如有fragment,也建立fragment目录。
首先看下model文件下的MainModel,主要用于请求数据:
public class MainModel extends AbstractModel implements MainContract.Model {
@Override
public Observable<Page<List<Product>>> getProduct(int currentPage) {
return HttpManager.createApi(Api.class).getProduct(currentPage).compose(TransformerHelp.schedulerTransformer()).compose(TransformerHelp.responseTransformer());
}
}
这里通过HttpManager调用Api接口中定义的getProduct方法。
HttpManager是一个单例,这里通过一个代理类,实现静态调用。
private static class SingletonHolder {
static final HttpManager INSTANCE = new HttpManager();
}
/**
* 取得单例对象
*
* @return 当前对象
*/
private static HttpManager getInstance() {
return SingletonHolder.INSTANCE;
}
public static <T> T createApi(Class<T> clazz) {
return getInstance().mSendDelegate.createApi(clazz);
}
代理类中最终会生成Retrofit对象,并取得clazz接口类的实例化对象。
public <T> T createApi(Class<T> clazz, Configure configure, String url) {
Retrofit retrofit = new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.baseUrl(url)
.client(configure.get())
.build();
return retrofit.create(clazz);
}
接着来看Api接口类:
public interface Api {
@GET("/mapi/product/search.htm")
Observable<BaseResponse<Page<List<Product>>>> getProduct(@Query("currentPage") int currentPage);
}
最终这里会拼接一个URL然后进行网络请求。这里拼接的URL就是:baseUrl+"/mapi/product/search.htm?currentPage=1。在这个URL中/mapi/product/search.htm就是GET后的值,而?后的currentPage入参就是这个方法的入参。
而这里的baseUrl是提取出来一个公共的相对不变的URL,在上面创建Retorift时初始化的。这样一个完整的URL就拼接好了。在方法的开头可以看到有个GET的注解,说明这个请求是GET方法,当然也可以根据具体需求用POST、PUT、DELETE以及HEAD。
到这里Model层一个简单的网络请求就完成了。好了,再来看一下presenter和view,在前面已经讲过,Presenter是连接 Model 和 View 的桥梁,它也负责操控 View。View则是将Presenter获取到的数据进行展示。
先来看view
interface View extends IView {
void showProducts(List<Product> products);
}
可以看到在里面只定义了一个showProducts,如果presenter请求成功,将向该方法传入请求下来的数据,view拿到这个数据后,就可以进行关于这个数据的展示或其他的一些操作。如果请求失败,就会向这个view传入失败信息,或弹个Toast来提示请求失败,这个失败的处理在公共类中已经处理了。当然还可以根据项目需要来定义一些其他的方法。
接下来我们看看presenter是如何工作的
interface Presenter extends IPresenter<View> {
void getProduct(int currentPage);
}
public interface IPresenter<V extends IView> {
/**
* Set or attach the view to this mPresenter
*/
@UiThread
void attachView(V view);
/**
* Will be called if the view has been destroyed. Typically this method will be invoked from
* <code>Activity.detachView()</code> or <code>Fragment.onDestroyView()</code>
*/
@UiThread
void detachView();
}
在这里也只定义了一个getProduct方法,在父类中定义了一个attachView方法跟detachView方法,用于绑定之前定义的View,并与Activity的生命周期绑定。也就是,如果想把请求下来的数据给哪个View就传入哪个View。
再来看一下MainPresenter具体是怎么实现的:
public class MainPresenter extends AbstractPresenter<MainContract.View, MainContract.Model> implements MainContract.Presenter {
public MainPresenter() {
mModel = new MainModel();
}
@Override
public void getProduct(int currentPage) {
addSubscribe(getModel().getProduct(currentPage).subscribe(LoadingProgressObserver.create(getView(),pager->{
getView().showProducts(pager.data);
})));
}
}
MainPresenter 实现了定义的基础Presenter,在构造方法中生成Model对象。
接下来我们定义了一个方法getProduct,名字和入参都和Model中的方法相同。并调用父类addSubscribe添加一个订阅关系。
/**
* 实现类必须调用此方法注册
* @param subscription
*/
protected void addSubscribe(Subscription subscription) {
if (mCompositeSubscription == null) {
mCompositeSubscription = new CompositeSubscription();
}
mCompositeSubscription.add(subscription);
}
private void unSubscribe() {
if (mCompositeSubscription != null && mCompositeSubscription.hasSubscriptions()) {
mCompositeSubscription.clear();
}
}
在addSubscribe方法中新建了一个mCompositeSubscription 对象用来存放RxJava中的订阅关系的。注意请求完数据要及时清掉这个订阅关系,不然会发生内存泄漏。
所以这里会在detachView中通过调用unSubscribe方法来取消这个订阅关系。
然后在attachView中,把MainView传进去。也就是说会把请求下来的数据交给MainView来处理。
上面已经说过model.getProduct就是调用Api的getProduct方法,而这个方法返回的是一个泛型为BaseResponse<Page<List<Product>>>的Observable,即被观察者,然后通过subscribeOn(Schedulers.io())来定义请求事件发生在io线程,然后通过observeOn(AndroidSchedulers.mainThread())来定义事件在主线程消费,即在主线程进行数据的处理,最后通过subscribe使观察者订阅它。在观察者中有三个方法:onNext、onCompleted、onError。当请求成功话,就会调用onNext,并传入请求返回的BaseResponse<Page<List<Product>>>,当请求结束后会调用onCompleted,最终把请求的数据交给MainView处理就可以了,如果请求失败,那么不会调用onCompleted而调用onError,这样也可以向MainView传递错误消息。
这里是通过调用LoadingProgressObserver来实现的。
public abstract class LoadingProgressObserver<T> extends ErrorObserver<T> {
// 弱引用反正内存泄露
private WeakReference<IView> mView;
public LoadingProgressObserver(IView view) {
mView = new WeakReference<IView>(view);
}
private IView getView() {
return mView == null ? null : mView.get();
}
@Override
public void onStart() {
if (getView() != null) {
getView().startLoading();
}
}
@Override
public void onError(ResponseException ex) {
LogUtil.e(ex);
if (getView() != null) {
getView().showLoadFailure(ex);
}
}
@Override
public void onCompleted() {
if (getView() != null) {
getView().showLoadSuccess();
}
}
}
最后,看一下Activity代码:
public class MainActivity extends AbstractMvpActivity<MainContract.Presenter>implements MainContract.View {
@BindView(R.id.list_btn)
Button mListBtn;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
}
@Override
protected MainContract.Presenter createPresenter() {
return new MainPresenter();
}
@Override
public void showMessage(String message) {
LogUtil.d(“显示失败信息");
}
@Override
public void startLoading() {
LogUtil.d("开始加载");
}
@Override
public void showLoadSuccess() {
LogUtil.d("加载成功");
}
@Override
public void showLoadFailure(ResponseException re) {
LogUtil.d("加载失败="+re.getMessage());
}
@Override
public void showProducts(List<Product> products) {
LogUtil.d("products="+products);
}
@OnClick(R.id.list_btn)
public void listClick(View view) {
getPresenter().getProduct(1);
}
}
先创建了一个MainPresenter对象,然后调用它的onCreate方法进行初始化,接着调用attachView来绑定MainView。MainView的实现也很简单,在showProducts方法中将products打印出来。然后点击按钮的时候就调用MainPresenter中getProduct方法,同时传入必要的入参。这样网络请求就开始了,并调用startLoading,如果请求成功就回调MainPresenter中的showProducts方法,失败就回调showLoadFailure方法,完成就回调showLoadSuccess。当Activity销毁时调用MainPresenter的detachView方法来释放订阅关系,防止内存泄漏。
结语
至此,MVP+Retrofit+RxJava探索基本结束,这里只是使用Retrofit+RxJava实现了一个MVP的快速开发的框架,其目的是展现如何将MVP+Retrofit+RxJava应用在项目中。
对于MVP Retrofit RxJava相关的资料及更详细的语法,大家可以Google一下。
本文作者: 陈赟(农金圈研发团队)
以上是关于MVP+Retrofit+RxJava探索之路的主要内容,如果未能解决你的问题,请参考以下文章
Android 教你一步步搭建MVP+Retrofit+RxJava网络请求框架
RxJava+Retrofit+OkHttp深入浅出-mvp(使用篇)