100

MVP实战心得---封装Retrofit2.0+RxAndroid+RxBus

 6 years ago
source link: https://juejin.im/post/5a30dc30f265da431e16c111
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Retrofit:

对okhttp的封装,可以更方便的使用okhttp

RxAndroid

响应式编程框架,rxjava的扩展,很爽的链式编程 魅力在于对数据的处理,与线程切换的灵活性. 用来处理异步操作(Lambda表达式不会用.用Lambda表达式代码会更少,但不会的人会看不懂代码.不是很推荐)

RxBus

用RxJava实现的EventBus

说说为什么要配合起来用

Retrofit负责链接网络,请求网络. RxAndroid负责处理请求的结果.异步操作 RxBus可以很方便的进行各组件之间的通信. 我之前是用asynchttpclient做网络请求的,各种代码缩进,if套if,各种回调,惨不忍睹啊. 用了Retrofit+RxAndroid我就彻底放弃asynchttpclient了.

1.RxJava

传送门:RxJava---------这个作为入门学习rxjava非常好

2.Retrofit

这个写点基本的用法吧..

首先看用的包:



arduino

//retrofit2--看名字就知道是啥了 compile 'com.squareup.retrofit2:retrofit:2.1.0' //CallAdapterFactory的Rx依赖包---导这个包才能配合rxAndroid使用 compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0' //ConverterFactory的String依赖包----这个是解析数据的工厂.用来格式化数据的,配置编码啊,gson解析啊. compile 'com.squareup.retrofit2:converter-scalars:2.1.0'

然后是retrofit注解:(使用retrofit,注解是很重要的)



kotlin

方法注解 : 包含@GET、@POST、@PUT、@DELETE、@PATCH、@HEAD、@OPTIONS、@HTTP。 这个不多讲.一般用的就是@GET、@POST,很明显,一个是get请求,一个是post请求

标记注解 : 包含@FormUrlEncoded、@Multipart、@Streaming。
这个得和参数注解一起说
参数注解 : 包含@Query,@QueryMap、@Body、@Field,@FieldMap、@Part,@PartMap。

@Get---------用的参数注解就@Query,@QueryMap,

@Post--------则会用到 @Body、@Field,@FieldMap、@Part,@PartMap。

@Body-------将数据转化成Json,然后post.具体转化根据设置的解析工厂(下面有讲)
---------------------------------------------------分割线----------------------------------------------------------
@Field,@FieldMap------post上传表单.@Field表示单个,@FieldMap表示集合.
需要添加上面的@FormUrlEncoded表示表单提交 ,
对应Content-Type:application/x-www-form-urlencoded
如:
@FormUrlEncoded
@POST("login的url")
Observable<User> login(@Field("name") String name, @FieldMap Map params);
--------------------------------------------------分割线------------------------------------------------------------
@Part,@PartMap----post上传文件/数据.@Part表示单个,@PartMap表示集合.
其中@Part MultipartBody.Part 类型代表文件,@Part("key") RequestBody类型代表参数 
需要添加@Multipart表示支持文件上传的表单,Content-Type: multipart/form-data
@Multipart
@POST("update的url")
Observable<User> update(@Part ("file") MultipartBody.Part file, @Part("key") RequestBody key,@PartMap Map<String,RequestBody> files);
如果参数较少,使用@Part ("file")就可以解决了,如果参数较多,那就需要使用@PartMap了.
其他注解 : @Path、@Header,@Headers、@Url
这几个用处挺大的,这里就不细说了,并不是必用的,我用的不多.

Retrofit 配置代码.

//这个是处理网络请求的log信息的,可以实现Interceptor接口来自定义.
 HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
            @Override
            public void log(String message) {
                HLog.i("RxJava", message);
            }
        });

OkHttpClient client = new OkHttpClient.Builder()
                .addInterceptor(interceptor)
                .build();
Retrofit retrofit = new Retrofit.Builder()
                .client(client)//Retrofit需要配置一个OkHttpClient实例.
                .baseUrl(API_HOST)//需要指定一个baseUrl,一般就是服务器的域名
                .addConverterFactory(FastjsonConverterFactory.create())//这个是数据解析工厂,我用的是fastjson
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())//支持rxJava,在第二个jar包里面
                .build();

下面是完整代码:

/**
* 写成单例模式,因为并不需要多个Retrofit存在.
*/
public class RetrofitUtil {
    /**
     * 服务器地址
     */
    private static final String API_HOST ="你的BaseUrl";
    private RetrofitUtil() {

    }
    public static Retrofit getRetrofit() {
        return Instanace.retrofit;
    }
    //静态内部类,保证单例并在调用getRetrofit方法的时候才去创建.
    private static class Instanace {
        private static final Retrofit retrofit = getInstanace();
        private static Retrofit getInstanace() {
            HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger(){
                @Override
                public void log(String message) {
                    HLog.i("RxJava", message);
                }
            });
            OkHttpClient client = new OkHttpClient.Builder()
                    .addInterceptor(interceptor)
                    .build();
            Reretrofit = new Retrofit.Builder()
                    .client(client)
                    .baseUrl(API_HOST)
                    .addConverterFactory(FastjsonConverterFactory.create())
                    .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                    .build();
            return retrofit;
        }
    }
}

json解析工厂,代码太多,,具体可以见demo

3.RxAndroid

如果没接触的话,可以看前面的Rxjava链接.

(1).首先看Reretrofit+RxAndroid是怎么使用的
@GET("login地址")
Observable<BaseResponse<LoginData>> login(@QueryMap HashMap<String, Object> params);

其实所谓的Reretrofit+RxAndroid就是这么回事.

没有RxAndroid的Reretrofit请求接口是这样写的:

@GET("login地址")
Call<BaseResponse<LoginData>> login(@QueryMap HashMap<String, Object> params);

把Call换成了Observable而已.

(2)写一个接口类
/**
*所有的网络请求都可以写在这个接口类里面.
*/
public interface APIService {
     @GET("login地址")
     Observable<BaseResponse<LoginData>> login(@QueryMap HashMap<String, Object> params);
     ......
}

(3)接口类的实现


csharp

/** * 请求生成类。Retrofit一次生成,并作为单例. */ public class ApiServcieImpl { private ApiServcieImpl() { } public static APIService getInstance() { return createAPIService.apiService; } /** * Retrofit生成接口对象. */ private static class createAPIService { //Retrofit会根据传入的接口类.生成实例对象. private static final APIService apiService = RetrofitUtil.getRetrofit().create(APIService.class); } }

然后就可以通过ApiServcieImpl.getInstance()去调用APIService里面写的接口了. 如:



typescript

ApiServcieImpl.getInstance().login(new HashMap<String, Object>()) //传入参数 .subscribe(new Action1<BaseResponse<LoginData>>() {//简单的回调 @Override public void call(BaseResponse<LoginData> loginDataBaseResponse) { //拿到数据,做处理 } });

有没有发现,设置泛型的是<BaseResponse>,包了一层BaseResponse 这么做是为了请求完成后对返回的数据进行统一处理.

先看BaseResponse:



arduino

public class BaseResponse<T> { private boolean success;//请求是否成功 private int resultCode;//状态吗 private String msg;//返回的提示消息 private T data;//主要内容,因为不知道返回的会是什么类型,所以用泛型来表示 //get set方法就不贴了. }

怎么处理.



swift

/** * @author jlanglang 2016/11/15 16:14 */ public class ModelFilteredFactory { private final static Observable.Transformer transformer = new SimpleTransformer(); /** * 将Observable<BaseResponse<T>>转化Observable<T>,并处理BaseResponse * * @return 返回过滤后的Observable. */ @SuppressWarnings("unchecked") public static <T> Observable<T> compose(Observable<BaseResponse<T>> observable) { return observable.compose(transformer); } /** * 这里就不细讲了,具体可以去看rxjava的使用.这个类的意义就是转换Observable. */ private static class SimpleTransformer<T> implements Observable.Transformer<BaseResponse<T>, T> { //这里对Observable,进行一般的通用设置.不用每次用Observable都去设置线程以及重连设置 @Override public Observable<T> call(Observable<BaseResponse<T>> observable) { return observable.subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .unsubscribeOn(Schedulers.io()) .timeout(5, TimeUnit.SECONDS)//重连间隔时间 .retry(5)//重连次数 .flatMap(new Func1<BaseResponse<T>, Observable<T>>() { @Override public Observable<T> call(BaseResponse<T> tBaseResponse) { return flatResponse(tBaseResponse); } }); } /** * 处理请求结果,BaseResponse * @param response 请求结果 * @return 过滤处理, 返回只有data数据的Observable */ private Observable<T> flatResponse(final BaseResponse<T> response) { return Observable.create(new Observable.OnSubscribe<T>() { @Override public void call(Subscriber<? super T> subscriber) { if (response.isSuccess()) {//请求成功 if (!subscriber.isUnsubscribed()) { subscriber.onNext(response.getData()); } } else {//请求失败 int resultCode = response.getResultCode(); if (!subscriber.isUnsubscribed()) { //这里抛出自定义的一个异常.可以处理服务器返回的错误. subscriber.onError(new APIException(response.getResultCode(), response.getMsg())); } return; } if (!subscriber.isUnsubscribed()) {//请求完成 subscriber.onCompleted(); } } }); } } }

仅仅只有上面那些了么?接着看.



typescript

/** * @author jlanglang 2016/11/14 17:32 * Subscriber,这个是用来处理Observable的结果的. */ public abstract class SimpleSubscriber<T> extends Subscriber<T> { @Override public void onCompleted() {//这个是请求完成时调用.如果走了onError()就不会走这个方法. } @Override public void onError(Throwable e) {//这里通常就处理异常 if (e instanceof APIException) { APIException exception = (APIException) e; ToastUtil.showToast( exception.message); } else if (e instanceof UnknownHostException) { ToastUtil.showToast("请打开网络"); } else if (e instanceof SocketTimeoutException) { ToastUtil.showToast( "请求超时"); } else if (e instanceof ConnectException) { ToastUtil.showToast("连接失败"); } else if (e instanceof HttpException) { ToastUtil.showToast("请求超时"); }else { ToastUtil.showToast("请求失败"); } e.printStackTrace(); } @Override public void onNext(T t) {//这里的是获得了数据,方法意思很明显,下一步干啥 if (t != null) {//这里最好判断一下是否为null. call(t); } else { ToastUtil.showToast("连接失败"); } } /** *因为具体的处理这里无法得知,所以抽象. */ public abstract void call(T t); }

好了,看看现在的具体使用吧:



typescript

ModelFilteredFactory.compose(ApiServcieImpl.getInstance().login(new HashMap<String, Object>())) .subscribe(new SimpleSubscriber<LoginData>() { @Override public void call(LoginData loginData) { } }); 看起来之前用起来差不多,但是却做了很多的处理: 1.对Observable做了通用设置.网络重连次数,线程设置,重连时间. 2.做了对服务器返回结果的统一处理.比如根据resultcode,处理登陆过期啊啥的. 3.判断了data是否为null,不会在call()里面担心loginData是否为null 4.统一处理了请求的各种异常.

5.用到MVP中.

你以为上面那些就完了吗?NO!
如果我们在Presenter中这样调用其实是很不科学的.


javascript

ModelFilteredFactory.compose(ApiServcieImpl.getInstance().login(new HashMap<String, Object>()))

这个转换我们应该放在Modle和ModleImpl中去写


typescript

public class LoginContract{ ....//view接口省略 public interface Model { /** * 获取登陆数据 * @return Observable<LoginData> */ Observable<LoginData> login(HashMap<String, Object> treeMap); } ....//prensent接口省略 } public class LoginModelImpl implements LoginContract.Model { @Override public Observable<LoginData> login(HashMap<String, Object> hashMap) { return ModelFilteredFactory.compose(ApiServcieImpl.getInstance().login(hashMap)); }}

那么我们在presenter中调用就可以这样:


typescript

public class LoginPresenterImpl exdents BasePresenter implements LoginContract.Presenter{ ..... private LoginModelImpl loginModelImpl; public void onCreate(){ loginModelImpl = new LoginModelImpl();//创建modle实例 } public void login(){ //通过modle请求接口 loginModelImpl.login(new HashMap<String, Object>())) .subscribe(new SimpleSubscriber<LoginData>() { @Override public void call(LoginData loginData) { //处理请求的数据,绑定视图 } }); } .... }

6.管理Observable的生命周期,也就是网络请求的生命周期.

Observable是不是很高大上,然而如果你不进行处理,可是会内存泄漏的 RxAndroid也不会自动的根据Activity/frgament的生命周期结束异步请求. 但处理其实很简单.

使用CompositeSubscription

只需要将Observable,异步处理到最后返回的subscribe添加到CompositeSubscription实例里就行了.



typescript

public void login(){ Subscription subscribe = loginModelImpl.login(new HashMap<String, Object>())) .subscribe(new SimpleSubscriber<LoginData>() { @Override public void call(LoginData loginData) { //处理请求的数据,绑定视图 } }); compositeSubscription.add(subscribe);//添加订阅 } //在销毁的时候,结束订阅事件. public void onDestroy() { compositeSubscription.unsubscribe();//结束所有add的subscribe事件 }

那么,实战心得(二)中的BasePresenter就可以进行改进了,具体见:

传送门:实战心得(二)



csharp

/** * @author jlanglang 2016/11/11 15:10 */ public abstract class BasePresenter<T extends BaseView> { protected T mView; protected CompositeSubscription compositeSubscription; /** * 绑定View */ public void onAttch(T view) { this.mView = view; compositeSubscription = new CompositeSubscription (); } /** * 做初始化的操作,需要在V的视图初始化完成之后才能调用 * presenter进行初始化. */ public abstract void onCreate(); /** * 在这里结束异步操作 */ public void onDestroy(){ compositeSubscription.unsubscribe();//结束异步请求. } /** * 在V销毁的时候调用,解除绑定 */ public void onDetach() { mView = null; } /** * 容易被回收掉时保存数据 */ public abstract void onSaveInstanceState(Bundle outState); }

7 RxBus

没什么特别值得提的,用法自行搜索,哈哈,个人在项目中用的也不是很多,某些情况会用一下,但真心好用.

再附上githubdemo地址,时不时更新.

mvpDemo


您的喜欢与回复是我最大的动力-_-


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK