再见 Retrofit(实战)

上篇简单介绍了 Retrofit 的使用,初探 Retrofit(入门)https://www.jianshu.com/p/8dc7cc4d0339,不了解的可以先看看。这期主要想聊聊具体在项目中的使用。

先来说说实际情况,实际项目中我们的请求返回体一般会有固定格式,例如:

public class SampleResp {
    public int code; //业务码
    public String msg; //提示信息
    public String data; //接口返回数据
}

在 response.code() 为 200 的基础上,在根据 SampleResp.code 来进行业务处理。还有一种返回体格式是少了一层包装的,是直接根据 response.code() 的返回码进行判断的,根据 code 码再去取 response.body() 里的数据,这种格式我觉得其实不太规范,混淆了网络请求的 code 码和业务 code 码。但实际应用上是没问题的,目前项目中两种情况都有在用。
接下来解析返回数据,肯定会有一些是要做通用处理的,例如 token 失效,通用的错误提示等。如果每写一个请求就写一遍这些处理,不光写的烦,后期改动也麻烦。
另外一个需要改进的地方是,我们的请求方法都写在一个 interface 里,每调用一个请求都要用 retrofit 实例去创建 interface 实例,最后发起请求,那这部分也可以简化一下,我想采用单例模式,一次创建多次使用。
下面我们就开始。

RetrofitManager

新建一个管理类,来处理 retrofit,service,以及一些配置。大致的思路是,配置好 retrofit 并生成 service 服务,再根据具体操作去调取服务的相关方法就好了。

public class SampleRetrofitManager {
    private ApiServiceFactory service;

    private static class RetrofitManagerHolder {
        public static SampleRetrofitManager INSTANCE = new SampleRetrofitManager();
    }

    private SampleRetrofitManager() {
        OkHttpClient.Builder httpBuilder = new OkHttpClient.Builder();
        httpBuilder.addInterceptor(new Interceptor() {
            @Override
            public Response intercept(Chain chain) throws IOException {
                //要是需要添加请求头可以在这里
                Request original = chain.request();
                Request.Builder reqBuilder = original.newBuilder()
                        .header("header1", "header1")
                        .method(original.method(), original.body());
                reqBuilder.addHeader("header2", "header2");

                return chain.proceed(reqBuilder.build());
            }
        });
        //添加拦截器,可以将请求,返回的日志打印出来
        httpBuilder.addInterceptor(new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY));
        //时间配置
        httpBuilder.connectTimeout(15, TimeUnit.SECONDS);
        httpBuilder.readTimeout(20, TimeUnit.SECONDS);
        httpBuilder.writeTimeout(20, TimeUnit.SECONDS);
        //这好像还感觉不出怎么用,我看网上这么写,先写着还没测
        httpBuilder.retryOnConnectionFailure(true);
        //这里就是 retrofit 和 service 配置
        Retrofit.Builder reBuilder = new Retrofit.Builder()
                .addConverterFactory(GsonConverterFactory.create());
        Retrofit retrofit = reBuilder.client(httpBuilder.build())
                .baseUrl(BuildConfig.APP_HTTP_URL).build();
        service = retrofit.create(ApiServiceFactory.class);
    }

    public static SampleRetrofitManager getInstance() {
        return RetrofitManagerHolder.INSTANCE;
    }
}

这里遇到一个实际情况,就是服务器地址有可能不止一个,我的处理是再添加一个 service 变量,再配置一下地址就好了,用到服务 A 的就调 service A ,用到服务 B 的就调 service B。
到这里我们实现了用单例模式改进了 service 服务的调用,接着我们对请求返回做下封装,来帮我们做一些统一处理。

FormalCallback
public abstract class FormalCallback<T> implements Callback<SampleResp>{
    private Class<T> responseClass;

    public void setRespClass(Class<T> respClass) {
        responseClass = respClass;
    }

    @Override
    public void onResponse(Call<SampleResp> call, Response<SampleResp> response) {
        if (200 == response.code() && null != response.body()) {
            SampleResp resp = response.body();
            if (201 == resp.code) {
                // token 失效处理
            } else if (200 == resp.code) {
                T body = GsonUtil.getInstance().fromJson(resp.data, responseClass);
                onSuccess(body);
            } else {
                onFail();
            }
        }
    }

    @Override
    public void onFailure(Call<SampleResp> call, Throwable t) {
        onFail();
    }

    public abstract void onSuccess(T model);

    public abstract void onFail();
}

上面是针对有格式化返回体的异步请求的封装,实现了 retrofit 的 Callback 接口,在 onResponse 里对结果做了处理,并通过实现 onSuccess 和 onFail 来进一步处理。在使用的时候就更加集中处理业务结果。最后来说说非格式化返回体的封装。

InFormalCallback
public abstract class InFormalCallback<T> implements Callback<ResponseBody> {
    private Class<T> responseClass;

    public void setRespClass(Class<T> responseClass) {
        this.responseClass = responseClass;
    }

    @Override
    public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
        try {
            if (response.code() == 200 && response.body() != null) {
                T body = GsonUtil.getInstance().fromJson(response.body().string(), responseClass);
                onSuccess(body);
            } else if (response.code() == 201) {
                //token 失效
            } else {
                ErrorResponse error = GsonUtil.getInstance().fromJson(response.errorBody().string(), ErrorResponse.class);
                onFail(error);
            }
        }catch (Exception e) {
            e.printStackTrace();
            onFail(new ErrorResponse());
        }
    }

    @Override
    public void onFailure(Call<ResponseBody> call, Throwable t) {
        onFail(new ErrorResponse());
    }

    public abstract void onSuccess(T model);

    public abstract void onFail(ErrorResponse error);
}

非格式化和格式化的返回体封装相比,只是解析数据上略有不同,其他处理上基本一样。
到这里我们就对请求返回的统一处理就做好了,文章一开始提到的两个需要改进的地方也就处理好了。现在把上面这些用起来是没问题的,但我觉得还是可以再封装一下,于是我又做了一步。

public class SampleServiceCall<T> {
    //格式化返回体的 post 请求方法
    public void formalPost(Call<SampleResp> call, Class<T> responseClass, FormalCallback<T> callback) {
        if (null == call || null == responseClass) {
            return;
        }
        callback.setRespClass(responseClass);
        call.enqueue(callback);
    }
}

其实就是多调了一个自定义方法,为了统一处理,就再用方法包装了一下。我只写了格式化返回体的请求方法,非格式化的类似。为了写代码的时候更加集中到业务上,建议可以对格式化请求体做一下方法封装。

好了,上面这些我觉得是可以用到实际项目中的,这里只用了 retrofit,但大家知道 retrofit 一般都和 rxjava 混在一起,那么下期就来讲讲他们是怎么勾搭在一起的。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,837评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,551评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,417评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,448评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,524评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,554评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,569评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,316评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,766评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,077评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,240评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,912评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,560评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,176评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,425评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,114评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,114评论 2 352

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,988评论 25 707
  • 用两张图告诉你,为什么你的 App 会卡顿? - Android - 掘金 Cover 有什么料? 从这篇文章中你...
    hw1212阅读 12,710评论 2 59
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,647评论 18 139
  • 一只小狗,脏兮兮的样子,从田埂上迈着轻快的小碎步跑来,很快超过了我,向着山顶的亭子跑去。 等我来到这个村子的至高点...
    石海Hi阅读 500评论 8 2
  • 越发觉得写作是有百益而无一害的事情,不管是记录生活,收藏心路历程,还是创造一个世界,它都能在完成你的目的之外带给你...
    糖糖六点阅读 169评论 4 3