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

来源:互联网 发布:i see软件 编辑:程序博客网 时间:2024/05/29 19:33

关于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可以共存的

原创粉丝点击