Retrofit+RxJava 完美封装

本来在项目中是使用了Volley 然后封装 封装了一个HttpUtils,
但是由于项目中要引入RxJava,所以就顺便替换一下Volley,使用Retrofit+RxJava。

Volley框架,我这里就 不过多介绍了,大家如果想学习的会可以自己去了解下。
http://blog.csdn.net/ysh06201418/article/details/46443235

首先大家可以参考下其他的Retrofit+RxJava的使用方法。
http://blog.csdn.net/byxyrq/article/details/52672202

如果大家对RxJava还不大了解的话,大家可以看下这篇文章。
http://gank.io/post/560e15be2dca930e00da1083

下面我介绍下我是如何封装Retrofit+RxJava的。
这是我公司的后台返回的json数据格式:

{  
    data:返回的数据,  
    errorCode:200,  
    moreInfo:"错误详情"  
}  

其中'data'中返回的才是我们真正需要的内容,而‘errorCode’不为200,则是后台出错,具体错误信息放在‘moreInfo’中。
而如果直接使用Retrofit+RxJava。如下:

/** 
 * 登录 
 */  
@POST  
@FormUrlEncoded  
Observable<UserVO> login(@Url String url , @FieldMap Map<String,Object>param); 

如果直接使用该接口,那么需要后台直接返回‘UserVO’的json数据。
但是依照前面所说,后台所有的返回数据都是依照

{  
    data:返回的数据,  
    errorCode:200,  
    moreInfo:"错误详情"  
} 

这种格式的数据返回。所以请求这个接口后,其实真正返回的数据应该是如下这样:

{  
    data:{UserVO对象},  
    errorCode:200,  
    moreInfo:"错误详情"  
} 

如上只是一个例子而已,其实所有接口,都是依照这种格式回传,所以在Retrofit+RxJava框架对返回的数据

进行处理之前,需要自己提前处理一下,先判断'errorCode'是否为200,如果为200就是正常请求数据返回,然后

获取到data的数据返回给Retrofit+RxJava框架进行处理。如果不为200,则为错误,根据错误的类型进行相应的处理。

如下:

/** 
    * 统一处理原始数据 
    * 
    * @param originalResponse 
    */  
   private static Response dealResponseData(Response originalResponse) {  
       String jsonString = null;  
       try {  
           BufferedSource bufferedSource = originalResponse.body().source();  
           jsonString = bufferedSource.readString(Charset.forName("utf-8"));  
       } catch (Exception e) {  
           e.printStackTrace();  
       }  
       if (!jsonString.trim().startsWith("{") && !jsonString.trim().startsWith("[")) {  
           return onSuccess(originalResponse, jsonString);  
       }  
       ResponseMessageBean msgBean = ResponseMessageBean.analyseReponse(jsonString);  
       if (msgBean == null) return onSuccess(originalResponse, msgBean.data.toString());  
       if (msgBean != null && (msgBean.errorCode == 200)) {  
           showError = true;  
           if (msgBean.data != null) {  
               return onSuccess(originalResponse, msgBean.data.toString());  
           } else {  
               return originalResponse.newBuilder().body(null).build();  
           }  
       } else {  
           onFailed(msgBean);  
           throw new RuntimeException(msgBean.moreInfo.toString());  
       }  
   }  

请求之后返回一个Response对象,预先处理完成之后返回另一新的Response对象,这样就可以实现刚才的目的了。
ResponseMessageBean其实就是最外面的对象。
onSuccess方法中,其实主要对处理后的数据重新组装成一个新的Response对象

private static Response onSuccess(Response originalResponse, String content) {  
        return originalResponse.newBuilder().  
                body(ResponseBody.create(null, content)).  
                build();  
    } 

方法虽然简单,但是这里要注意一点,不能新建一个新的Response返回,因为你Response里面其实是有多数据的,你就使用原来的Response,
然后把body里面的内容替换下就好了。
其实这里还有一个onFailed方法,主要是后台请求报错了,没有返回200,就根据你自己后台定义的值来判断具体错误,然后具体处理,这里
我就不再具体描述了。

前面描述的是data返回值是一个对象,其实是一个数组也是一样的,只要你在泛型里面写的是List那么就会返回一个List对象,如下:

@POST  
   @FormUrlEncoded  
   Observable<List<UserVO>> userList(@Url String url , @FieldMap Map<String,Object>param);  

这样处理的数据其实是这种格式的

{  
    data:[{},{}],  
    errorCode:200,  
    moreInfo:"错误详情"  
} 

对了,我这里一直没有讲为什么Retrofit+RxJava会对返回的对象进行自动转换,因为我前面已经放了几篇文章,希望大家能先阅读了

一些基础的知识后,再来看这篇文章的具体封装,可能对你的帮助会更大一点。

Retrofit在初始化:

Retrofit retrofit = new Retrofit.Builder().baseUrl(UrlConstant.BASEURL).  
               addConverterFactory(GsonConverterFactory.create()).  
               addCallAdapterFactory(RxJavaCallAdapterFactory.create()).  
               client(mOkHttpClient).  
               build();  

addConverterFactory其实是为了对返回内容的提供一个转换器。
这里官方实现了很多种转换器,如下

Gson: com.squareup.retrofit:converter-gson
Jackson: com.squareup.retrofit:converter-jackson
Moshi: com.squareup.retrofit:converter-moshi
Protobuf: com.squareup.retrofit:converter-protobuf
Wire: com.squareup.retrofit:converter-wire
Simple XML: com.squareup.retrofit:converter-simplexml
一共有这么几种,大家可以根据自己的返回格式和喜好,选择对应的转换器,我这里选择了Gson转换器。记得选择转换器,要引入对应的依赖,这里我就不再过多赘述了。

我这里为什么要讲这个转换器呢?因为在项目的的过程中,我发现了有这样的一个需求,就是返回值中的data不是对象,也不是数组,
如下:

{  
    data:"你好",  
    errorCode:200,  
    moreInfo:"错误详情"  
}  

或者是其他‘boolean’,‘int’等基础类型。就会出错。所以我这里重新对gson转换器做了封装,如下

public class TWGsonResponseBodyConverter<T> implements Converter<ResponseBody, T> {  
  
    private final Gson gson;  
    private final TypeAdapter<T> adapter;  
    private final TypeToken<?> typeToken;  
  
    TWGsonResponseBodyConverter(Gson gson, TypeAdapter<T> adapter, TypeToken<?> typeToken) {  
        this.gson = gson;  
        this.adapter = adapter;  
        this.typeToken = typeToken;  
    }  
  
    @Override  
    public T convert(ResponseBody value) throws IOException {  
        Object obj = checkBasicType(value);  
        if (obj != null) return (T) obj;  
        JsonReader jsonReader = gson.newJsonReader(value.charStream());  
        try {  
            return adapter.read(jsonReader);  
        } finally {  
            value.close();  
        }  
    }  
  
    /** 
     * 如果是基本类型就转化后,返回 
     */  
    private Object checkBasicType(ResponseBody value) throws IOException {  
        String typeName = typeToken.getRawType().getSimpleName();  
        if ("String".equals(typeName)) {  
            return value.string();  
        } else if ("Boolean".equals(typeName)) {  
            return Boolean.parseBoolean(value.string());  
        } else if ("Integer".equals(typeName)) {  
            return Integer.parseInt(value.string());  
        }  
        return null;  
    }  
}  

在方法中判断是否为基础类型,如果为基础类型,或者String 就通过处理后,直接返回,如果不是,则还是通过原来的转换器转换。
这样子就可以适用所有情况了。
在我的项目中,有一个场景,就是需要在请求时,自定义请求头,虽然Retrofit有@Head,但是由于每次请求都需要设置,还是直接封装
在里面比较好,代码如下:

private static Request.Builder createRequestHeader(Request.Builder builder) {  
        builder.header("Content-Type",  
                "application/x-www-form-urlencoded");  
        builder.header("User-Agent", getUserAgent());  
        return builder;  
    }  

其实就是创建一个新的Request返回而已。
然后项目中又说要使用https来访问,所以我又加了下面的代码来支持:

if (mOkHttpClient == null) {  
                    try {  
                        X509TrustManager xtm = new X509TrustManager() {  
                            @Override  
                            public void checkClientTrusted(X509Certificate[] chain, String authType) {  
                            }  
  
                            @Override  
                            public void checkServerTrusted(X509Certificate[] chain, String authType) {  
                            }  
  
                            @Override  
                            public X509Certificate[] getAcceptedIssuers() {  
                                X509Certificate[] x509Certificates = new X509Certificate[0];  
                                return x509Certificates;  
                            }  
                        };  
  
                        SSLContext sslContext = null;  
                        try {  
                            sslContext = SSLContext.getInstance("SSL");  
  
                            sslContext.init(null, new TrustManager[]{xtm}, new SecureRandom());  
  
                        } catch (NoSuchAlgorithmException e) {  
                            e.printStackTrace();  
                        } catch (KeyManagementException e) {  
                            e.printStackTrace();  
                        }  
                        HostnameVerifier DO_NOT_VERIFY = new HostnameVerifier() {  
                            @Override  
                            public boolean verify(String hostname, SSLSession session) {  
                                return true;  
                            }  
                        };  
  
                        // 指定缓存路径,缓存大小100Mb  
                        Cache cache = new Cache(new File(mContext.getCacheDir(), "HttpCache"), 1024 * 1024 * 100);  
                        mOkHttpClient = new OkHttpClient.Builder().  
                                addInterceptor(mRewriteCacheControlInterceptor).  
                                retryOnConnectionFailure(false).  
                                connectTimeout(30, TimeUnit.SECONDS).  
                                sslSocketFactory(sslContext.getSocketFactory()).  
                                hostnameVerifier(DO_NOT_VERIFY).  
                                cache(cache).  
                                build();  
                    } catch (Exception e) {  
                        e.printStackTrace();  
                    }  
                }  
            }  

并且还指定了缓存还有超时时间,重试次数,等等。

以上情况其实已经适用我当前项目的所以场景了。

下面是附上所有代码:

public class RetrofitHelper {  
    private static ServerApi api;  
    private static OkHttpClient mOkHttpClient;  
    private static Context mContext;  
    private static boolean showError = true;  
  
    /** 
     * 启动后初始化 
     */  
    public static void init(Context context) {  
        mContext = context;  
        initOkHttpClient();  
        Retrofit retrofit = new Retrofit.Builder().baseUrl(UrlConstant.BASEURL).  
                addConverterFactory(GsonConverterFactory.create()).  
                addCallAdapterFactory(RxJavaCallAdapterFactory.create()).  
                client(mOkHttpClient).  
                build();  
        api = retrofit.create(ServerApi.class);  
    }  
  
    /** 
     * 重置baseUrl 
     */  
    public static void resetBaseUrl() {  
        Retrofit retrofit = new Retrofit.Builder().baseUrl(UrlConstant.BASEURL).  
                addConverterFactory(TWGsonConverterFactory.create()).  
                addCallAdapterFactory(RxJavaCallAdapterFactory.create()).  
                client(mOkHttpClient).  
                build();  
        api = retrofit.create(ServerApi.class);  
    }  
  
  
    public static ServerApi getApi() {  
        return api;  
    }  
  
    /** 
     * 统一处理原始数据 
     * 
     * @param originalResponse 
     */  
    private static Response dealResponseData(Response originalResponse) {  
        String jsonString = null;  
        try {  
            BufferedSource bufferedSource = originalResponse.body().source();  
            jsonString = bufferedSource.readString(Charset.forName("utf-8"));  
        } catch (Exception e) {  
            e.printStackTrace();  
        }  
        if (!jsonString.trim().startsWith("{") && !jsonString.trim().startsWith("[")) {  
            return onSuccess(originalResponse, jsonString);  
        }  
        ResponseMessageBean msgBean = ResponseMessageBean.analyseReponse(jsonString);  
        if (msgBean == null) return onSuccess(originalResponse, msgBean.data.toString());  
        if (msgBean != null && (msgBean.errorCode == 200)) {  
            showError = true;  
            if (msgBean.data != null) {  
                return onSuccess(originalResponse, msgBean.data.toString());  
            } else {  
                return originalResponse.newBuilder().body(null).build();  
            }  
        } else {  
            onFailed(msgBean);  
            throw new RuntimeException(msgBean.moreInfo.toString());  
        }  
    }  
  
    /** 
     * 初始化okHttp 
     */  
    private static void initOkHttpClient() {  
        if (mOkHttpClient == null) {  
            synchronized (RetrofitHelper.class) {  
                Interceptor mRewriteCacheControlInterceptor = new Interceptor() {  
                    @Override  
                    public Response intercept(Chain chain) throws IOException {  
                        Request request = chain.request();  
                        if (!NetworkUtils.isNetworkConnected(mContext)) {  
                            request = request.newBuilder().cacheControl(CacheControl.FORCE_CACHE).build();  
                        }  
                        /** 
                         * 统一设置请求头 
                         */  
                        Request newRequest = createRequestHeader(request.newBuilder()).build();  
                        Response originalResponse = chain.proceed(newRequest);  
                        //如果是重定向,那么就执行重定向后返回数据。  
                        if (originalResponse.isRedirect()) {  
                            Request redirectRequest = request.newBuilder().url(originalResponse.header("location")).build();  
                            originalResponse = chain.proceed(redirectRequest);  
                        }  
                        originalResponse = dealResponseData(originalResponse);  
                        return originalResponse;  
                    }  
                };  
  
                if (mOkHttpClient == null) {  
                    try {  
                        X509TrustManager xtm = new X509TrustManager() {  
                            @Override  
                            public void checkClientTrusted(X509Certificate[] chain, String authType) {  
                            }  
  
                            @Override  
                            public void checkServerTrusted(X509Certificate[] chain, String authType) {  
                            }  
  
                            @Override  
                            public X509Certificate[] getAcceptedIssuers() {  
                                X509Certificate[] x509Certificates = new X509Certificate[0];  
                                return x509Certificates;  
                            }  
                        };  
  
                        SSLContext sslContext = null;  
                        try {  
                            sslContext = SSLContext.getInstance("SSL");  
  
                            sslContext.init(null, new TrustManager[]{xtm}, new SecureRandom());  
  
                        } catch (NoSuchAlgorithmException e) {  
                            e.printStackTrace();  
                        } catch (KeyManagementException e) {  
                            e.printStackTrace();  
                        }  
                        HostnameVerifier DO_NOT_VERIFY = new HostnameVerifier() {  
                            @Override  
                            public boolean verify(String hostname, SSLSession session) {  
                                return true;  
                            }  
                        };  
  
                        // 指定缓存路径,缓存大小100Mb  
                        Cache cache = new Cache(new File(mContext.getCacheDir(), "HttpCache"), 1024 * 1024 * 100);  
                        mOkHttpClient = new OkHttpClient.Builder().  
                                addInterceptor(mRewriteCacheControlInterceptor).  
                                retryOnConnectionFailure(false).  
                                connectTimeout(30, TimeUnit.SECONDS).  
                                sslSocketFactory(sslContext.getSocketFactory()).  
                                hostnameVerifier(DO_NOT_VERIFY).  
                                cache(cache).  
                                build();  
                    } catch (Exception e) {  
                        e.printStackTrace();  
                    }  
                }  
            }  
        }  
    }  
  
  
    private static Response onSuccess(Response originalResponse, String content) {  
        return originalResponse.newBuilder().  
                body(ResponseBody.create(null, content)).  
                build();  
    }  
  
  
    /** 
     * errorCode 不为200 
     * 
     * @param msgBean 
     */  
    private static void onFailed(ResponseMessageBean msgBean) {  
        String alert = "";  
        if (msgBean == null) {  
            return;  
        }  
        if (msgBean.errorCode != 200) {  
            if (msgBean.errorCode == 401) {  
                Observable.create(new Observable.OnSubscribe<Object>() {  
                    @Override  
                    public void call(Subscriber<? super Object> subscriber) {  
                        DialogUtils.alertUserUnauthorized(mContext);  
                    }  
                }).subscribeOn(AndroidSchedulers.mainThread()).subscribe(new Observer<Object>() {  
                    @Override  
                    public void onCompleted() {  
                    }  
  
                    @Override  
                    public void onError(Throwable e) {  
                    }  
  
                    @Override  
                    public void onNext(Object o) {  
                    }  
                });  
  
                return;  
            } else if (msgBean.errorCode == 301) {  
                alert = "服务器繁忙,请稍后再试";  
            } else if (msgBean.errorCode == 302) {  
                alert = "此次访问已失效哦";  
            } else if (msgBean.errorCode == 403) {  
                alert = "接口临时关闭,请稍后再试";  
            } else if (msgBean.errorCode == 500) {  
                alert = "服务器繁忙,请稍后再试";  
            } else if (msgBean.errorCode == -1) {  
                showError = true;  
                if (msgBean.moreInfo != null) {  
                    alert = msgBean.moreInfo.toString();  
                }  
            } else {  
                return;  
            }  
            final String alertStr = alert;  
  
            Observable.create(new Observable.OnSubscribe<Object>() {  
                @Override  
                public void call(Subscriber<? super Object> subscriber) {  
                    //这里有 window bad token 错误  
                    try {  
                        if (showError && !TextUtils.isEmpty(alertStr)) {  
                            ToastUtils.showToast(mContext, alertStr);  
                            showError = false;  
                        }  
                    } catch (Exception e) {  
                        e.printStackTrace();  
                    }  
                }  
            }).subscribeOn(AndroidSchedulers.mainThread()).subscribe();  
        }  
    }  
  
    /** 
     * 统一处理请求头部数据 
     * 
     * @return 
     */  
  
    private static Request.Builder createRequestHeader(Request.Builder builder) {  
        builder.header("Content-Type",  
                "application/x-www-form-urlencoded");  
        builder.header("User-Agent", getUserAgent());  
        return builder;  
    }  
  
  
    public static String getUserAgent() {  
        UserAgentBean userAgent = new UserAgentBean();  
        userAgent.setDeviceToken(AppUtils.getDeviceId(mContext));  
        userAgent.setClient("Android");  
        userAgent.setChannel("xunbao");  
        if (User.getCurrent() != null) {  
            userAgent.setUserId(User.getCurrent().getId());  
        }  
        PackageInfo info = AppUtils.getPackageInfo(mContext);  
        if (info != null) {  
            userAgent.setBuild(info.versionCode + "");  
            userAgent.setVersion(info.versionName);  
        }  
        userAgent.setScreenSize(  
                DisplayUtils.getScreenSize(mContext));  
        userAgent.setSafeToken(SharePerferenceUtils.getString(mContext,PreferenceConstant.SAFE_TOKEN));  
        return userAgent.toString();  
    }  
  
}  
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,686评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,668评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,160评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,736评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,847评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,043评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,129评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,872评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,318评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,645评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,777评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,861评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,589评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,687评论 2 351

推荐阅读更多精彩内容