给你一个原汁原味的Retrofit2+Rxjava2的请求封装

关于Retrofit和Rxjava的教程往上已经有很多,但大多都是1.x版本,很多方法和类已经改变,而且封装也说的不太多.这里我做了一个简单的封装,可以说一句话代码完成封装也不错.如果你有时间,那么下面的文章会帮助你理解工具类代码的作用.如果你没有时间那么请直接滑到最后下载demo,参照我写的例子直接用到项目里吧.

封装的功能:请求显示进度框; 对返回参数统一处理  cookie保持  缓存策略 公共参数  日志打印

首先说下retrofit这个库,它其实是对okhttp的再次封装,单用okhttp其实也能完成网络请求,但是会比较麻烦.而使用retrofit代码就会十分简化.首先引入类库

implementation 'com.squareup.retrofit2:retrofit:2.3.0'
这是当前的最新版,如果单独使用它的话返回的结果是Call,和Rxjava没有关系,所以要引入rxjava支持

implementation 'com.squareup.retrofit2:converter-gson:2.3.0'
implementation  'com.squareup.retrofit2:adapter-rxjava2:2.3.0'
compile "io.reactivex.rxjava2:rxjava:2.x.y"
compile 'io.reactivex.rxjava2:rxandroid:2.0.0'
因为涉及到线程切换,需要再安卓里切换ui线程,所以要引入rxandroid

最后是okhttp的一些工具库:日志打印等

compile 'com.squareup.okhttp3:logging-interceptor:3.4.1'
compile 'com.squareup.okhttp3:okhttp-urlconnection:3.4.1'

首先是创建ApiService,

public interface ApiService {
    //post请求
    @POST("login/index")
    @FormUrlEncoded
    Flowable<Login> postLogin(@Field("account") String username, @Field("pwd") String pwd, @Field("accessPort") int prot, @Field("version") String version);
    //get请求
    @GET("appcms/cmsunlogin")
    Flowable<CmsEntity> getInfo();
}
基本上除了参数名称和方法名称其他都是固定写法,如果你对retrofit完全不了解,那么可以看看其他简单的教程,这里不多做介绍.请求方法写完之后,开始构建retrofit实例,类似这样

retrofit = new Retrofit.Builder()
        .addConverterFactory(GsonConverterFactory.create(getGsonInstance()))
        .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
        .client(getOkHttpInstance())
        .baseUrl("http://app.ddshenbian.com/").build();
在官方的示例代码中每次请求都要获取一次实例,这意味着每次都要写出这么多重复的代码,不用多说,直接用单利模式放到util里面.变成这样

public static ApiService getApiService() {
    if (retrofit == null) {
        retrofit = new Retrofit.Builder()
                .addConverterFactory(GsonConverterFactory.create(getGsonInstance()))
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .client(getOkHttpInstance())
                .baseUrl("http://app.ddshenbian.com/").build();
    }

    return retrofit.create(ApiService.class);
}
addConverterFactory是添加gson转换支持,如果你不是用json格式,那么请换别的转换器,retrofit官网有支持

addCallAdapterFactory是今天的主角,添加rxjava2支持,他将原来的返回值Call变成了Flowable,也就是被观察者,完美衔接rxjava

client 是添加okhttp,不添加也行,之所以添加是为了对okhttp进行一些列的设置.如下:

/**
     * 实例化okhttp,log打印,添加公共参数,cookie保持,缓存策略,超时和重连
     *
     * @return
     */
    public static OkHttpClient getOkHttpInstance() {
        if (client == null) {
            client = new OkHttpClient.Builder();
            /**
             * log信息拦截器
             */
            //log信息拦截器
            HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor();
            httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
            //设置Debug Log模式
            client.addInterceptor(httpLoggingInterceptor);
            /**
             * 缓存
             */
            final File chachefile = new File(AppAplication.getContext().getExternalCacheDir(), "HttpCache");
            final Cache cache = new Cache(chachefile, 1024 * 1024 * 50);//缓存文件
            Interceptor cacheInterceptor = new Interceptor() {
                @Override
                public Response intercept(Chain chain) throws IOException {
                    Request request = chain.request();
                    if (!ApkUtils.isNetworkAvailable(AppAplication.getContext())) {
                        request = request.newBuilder()
                                .cacheControl(CacheControl.FORCE_CACHE)
                                .build();
                    }
                    Response response = chain.proceed(request);
                    if (ApkUtils.isNetworkAvailable(AppAplication.getContext())) {
                        int maxAge = 0;

                        // 有网络时 设置缓存超时时间0个小时
                        response.newBuilder()
                                .header("Cache-Control", "public, max-age=" + maxAge)
                                .removeHeader("Pragma")// 清除头信息,因为服务器如果不支持,会返回一些干扰信息,不清除下面无法生效
                                .build();
                    } else {
                        //无网络时,设置超时为4                        int maxStale = 60 * 60 * 24 * 28;
                        response.newBuilder()
                                .header("Cache-Control", "public, only-if-cached, max-stale=" + maxStale)
                                .removeHeader("Pragma")
                                .build();
                    }

                    return response;
                }
            };
//            client.cache(cache).addInterceptor(cacheInterceptor);
            /**
             * 公共参数
             */
            Interceptor addQueryParameterInterceptor = new Interceptor() {
                @Override
                public Response intercept(Chain chain) throws IOException {
                    Request originalRequest = chain.request();
                    Request request;
                    String method = originalRequest.method();
                    Headers headers = originalRequest.headers();
                    HttpUrl modifiedUrl = originalRequest.url().newBuilder()
                            // Provide your custom parameter here
                            .addQueryParameter("accessPort", "2")
                            .addQueryParameter("version", "1.0")
                            .build();
                    request = originalRequest.newBuilder().url(modifiedUrl).build();
                    return chain.proceed(request);
                }
            };
            //公共参数
            client.addInterceptor(addQueryParameterInterceptor);
            /**
             * 设置头
             */
            Interceptor headerInterceptor = new Interceptor() {

                @Override
                public Response intercept(Chain chain) throws IOException {
                    Request orignaRequest = chain.request();
                    Request request = orignaRequest.newBuilder()
                            .header("AppType", "TPOS")
                            .header("Content-Type", "application/json")
                            .header("Accept", "application/json")
                            .method(orignaRequest.method(), orignaRequest.body())
                            .build();

                    return chain.proceed(request);
                }
            };
            client.addInterceptor(headerInterceptor);
            /**
             * 设置cookie
             */
            CookieManager cookieManager = new CookieManager();
            cookieManager.setCookiePolicy(CookiePolicy.ACCEPT_ALL);
            client.cookieJar(new JavaNetCookieJar(cookieManager));
            /**
             * 设置超时和重连
             */
            //设置超时
            client.connectTimeout(15, TimeUnit.SECONDS)
                    .readTimeout(20, TimeUnit.SECONDS)
                    .writeTimeout(20, TimeUnit.SECONDS)
                    //错误重连
                    .retryOnConnectionFailure(true);
        }
        return client.build();
    }
上面的代码每一段都有注释,这个就是okhttp的一些设置,跟retrofit没什么关系,不过功能还是很重要的.


最后说一下封装的重点,首先来看一个封装后的请求:

/**
 * 一个基本的请求
 */
private void getinfo() {
    RetrofitUtil.getApiService()
            .getInfo()
            .compose(RetrofitUtil.getComposer())
            .subscribe(login ->
            {
                Log.i(TAG, "login: 接收到结果");
                textview.setText(login.toString());
            });
}
可以看到用RetrofitUtil.getApiService来获取到retrofit实例,然后调用自己写好的请求方法getInfo,getInfo返回Flowable,从此开始进入rajava2,(Flowable就是原来的Obserable),然后下面的一句就是我们的封装了

.compose(RetrofitUtil.getComposer())
通过rajava的compose操作符,它在传入请求结果的同时允许我们用flowabletransformer返回一个新的Flowable,这意味着在最终的subscribe之前我们可以对数据和线程随意处理.最基本来说,如果没有封装的话你每次请求都要增加两句线程指定的代码,这很明显是无用功.另外还有一些对接口返回值的统一处理,比如返回code=-1,就是请求失败,这种情况下很明显不需要我们每次都写一样的代码去处理,我们需要对这种普遍的情况统一处理.

/**
 * 这个方法里可以进行返回信息的统一处理
 * @param <T>
 * @return
 */
public static <T extends BaseBean> FlowableTransformer<T, T> getComposer() {
    FlowableTransformer flowableTransformer = (FlowableTransformer<T, T>) upstream -> upstream
            .subscribeOn(Schedulers.io())
            .observeOn(AndroidSchedulers.mainThread())
            .filter(t -> {
                if (t.code == -99) {
                    //TODO 打开登录页面
                    Log.i(TAG, "test: 打开登录页面");
                    return false;
                }
                if (t.code != 1) {
                    // TODO 返回错误信息
                    Log.i(TAG, "test: code" + t.code);
                    return false;
                }
                //返回flse代表拦截,返回结果不会传递到subScribe中去,true代表不拦截
                return true;
            })
            .doOnSubscribe(subscription -> {
                //  TODO 在这里弹出加载进度dialog
                if (context != null) {
                    context.showDialog(subscription);
                }
            })
            .doOnComplete(() -> {
                if (context != null) {
                   context. dismissDialog();
                }
                Log.i(TAG, "onComplete: ");
            })
            .doOnError(throwable -> {
                        Log.i(TAG, "获取失败: ");
                if (context != null) {
                    context. dismissDialog();
                }
            })
          ;
    return flowableTransformer;
}
解释一下上面的代码:

首先是线程设置:subscribeOn,指定请求网络在io线程,oberveOn,指定观察者也就是回调在主线程

然后是filter:这个操作符的意思是过滤,接收到数据,如果返回false代表拦截,返回true不拦截.BaseBean是所有实体类的父类,他只包含两个字段,code和msg.其中code就是我们拦截判断的标准.比如这里判断code=-99,代表用户token过期,我们需要打开登陆页面,code=-1,代表请求有问题,我们直接用Toast提示失败信息msg.这样就实现了请求结果的统一拦截.

最后是doOnSubscribe,这个方法会在请求开始的时候最先执行,这里最适合弹出加载对话框

doOncomplete和doOnError是在最后执行,区别是一个成功一个失败.如果成功的话就是先执行subscribe最后执行doOncomplete,如果失败就直接执行doOnError.这里加上让加载框消失.

这样,整个封装完成 ,在不破环rajava结构的情况下完成了封装,所以你可看到第二个请求例子:

/**
 * 示范了对返回数据的简单处理
 */
public void get2() {
    ArrayList<String> imageUrlList = new ArrayList<>();
    RetrofitUtil.getApiService(this)//如果需要显示加载对话框就传入当前activity,否则不传
             .getInfo()
            .compose(RetrofitUtil.getComposer())
            //这里可以进行rxjava的各种操作比如返回list的时候我们进行遍历,将所有图片的地址添加到一个集合里面
            //flatMap遍历发射
            .flatMap(cmsEntity -> Flowable.fromIterable(cmsEntity.obj.bannerList))
            //收到之后添加
            .doOnNext(imageVo -> imageUrlList.add(imageVo.imageSrc))
            //最后显示
            .subscribe(image ->textview.setText(imageUrlList.toString()));
}
我们可以通过rajava的各种操作符对返回的数据直接处理,不必再回调里面各种嵌套或者循环.如果你对rxjava的操作符不太了解,可以多看看教程.

demo请自行查看

https://github.com/itwangyu/RetrofitDemo

demo是用studio3.0构建的,并支持了java8.用2.x版本很大可能会构建失败,如果你没有3.0,建议安装一个.3.0和2.x可以共存的

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值