Retrofit + OkHttp配合使用

因公司项目中的网络框架使用的是volley,而现在流行的框架莫过于Retrofit+OKHttp,作为一名合格的程序员,当然要掌握最新的网络框架了。于是花费时间、精力研究摸索,以下便是自己近期的学习成果。

首先是引入到项目中

implementation 'com.squareup.retrofit2:converter-gson:2.1.0'
implementation 'com.squareup.okhttp3:logging-interceptor:3.2.0'
implementation 'com.squareup.okhttp3:okhttp:3.7.0'
implementation 'com.squareup.retrofit2:retrofit:2.1.0'

接下来创建请求
Retrofit的网络请求和volley差别还是挺大的,Retrofit的接口请求采用注解的形式,接下来看一下它的Post请求(项目中大部分都是Post)。

首先在项目中创建ApiService 接口,然后在里面定义具体的接口请求

public interface ApiService {
	//@Headers 是动态改变baseUrl的重要组成部分后面的拦截器中会有介绍
	
    @FormUrlEncoded
    @Headers({"url:manage"})
    @POST("client/common/login.php"+ "?")
    Call<PointR> getPointList(@Field("parm") String params);

    @FormUrlEncoded
    @Headers({"url:version"})
    @POST("version.php")
    Call<NewSoftUpdateR> getVersion(@FieldMap() Map<String, String> params);
}

接下来就是创建真正的request,在项目开发中Retrofit不可能用一次就new一次,下面这段代码是简单封装了一下Retrofit和oKHttp的结合使用。



public class RetrofitManager {
    private static final int DEFAULT_TIME_OUT = 15;//超时时间 5s
    private static final int DEFAULT_READ_TIME_OUT = 15;//读取时间
    private static ApiService apiServer;
    private static RetrofitManager sInstance = null;

    //获取RetrofitManager
    @TargetApi(Build.VERSION_CODES.KITKAT)
    public static RetrofitManager get() {
        if (sInstance == null) {
            synchronized (RetrofitManager.class) {
                if (sInstance == null) {
                    sInstance = new RetrofitManager();
                }
            }
        }
        return sInstance;
    }

    public void initRxRetrofit() {
        // 创建 OKHttpClient--
        // 给请求设置log--在控制台打印请求日志
        HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
        interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
        // 设置可以访问HTTPS---(HttpsUtils这个类是从鸿洋的封装的OKHTTP库中拿到的,这样设置是信任所有证书)
        HttpsUtils.SSLParams sslParams = HttpsUtils.getSslSocketFactory(null, null, null);
        OkHttpClient.Builder builder = new OkHttpClient.Builder();
        builder.sslSocketFactory(sslParams.sSLSocketFactory, sslParams.trustManager);
        builder.connectTimeout(DEFAULT_TIME_OUT, TimeUnit.SECONDS);//连接超时时间
        builder.writeTimeout(DEFAULT_READ_TIME_OUT, TimeUnit.SECONDS);//写操作 超时时间
        builder.readTimeout(DEFAULT_READ_TIME_OUT, TimeUnit.SECONDS);//读操作超时时间
        builder.retryOnConnectionFailure(true);//网络请求错误重试
        builder.addInterceptor(interceptor);//添加请求日志
        // 添加公共参数拦截器----以及处理后台返回数据的解密问题
        // 如果项目中涉及到多个BaseURL也可以在拦截器中进行处理
        builder.addInterceptor(new BaseInterceptor());
        // 创建Retrofit
        Retrofit mRetrofit = new Retrofit.Builder()
                .client(builder.build())//设置OkHttpClient
                .addConverterFactory(GsonConverterFactory.create())//添加GSon解析器返回数据自动解析成实体类
                .baseUrl(BuildConfig.API_MANAGE)//添加baseURL不添加的话会报异常
                // http://.....com.cn/   BaseURL必须以  /  结尾
                .build();

        apiServer = mRetrofit.create(ApiService.class);
    }

    /**
     * 获取ApiServer对象
     *
     * @return apiServer
     */
    public static ApiService Api() {
        return apiServer;
    }
}

上面这个类就是需要在MyApplication中初始化。记得在AndroidManifest中定义MyApplication。

接下来就是调用

//httpSig请求参数
RetrofitManager.Api().getPointList(httpSig).
        call.enqueue(new Callback<PointR>() {
            @Override
            public void onResponse(Call<PointR> call, Response<PointR> response) {
                //处理请求成功且数据正常的逻辑
                call.cancel();//关闭请求
            }

            @Override
            public void onFailure(Call<PointR> call, Throwable e) {
                //请求失败---可以定义异常处理类根据异常信息提示用户
                call.cancel();
            }
        });

在项目中肯定会涉及到baseUrl的切换,不太懂后台整那么多域名。。。不过遇到问题还的解决,我在这里采用OkHttp的拦截器去实现切换baseUrl,在上面的RetrofitManager 类中添加了一个拦截器 builder.addInterceptor(new BaseInterceptor());下面就是BaseInterceptor中的逻辑。

public class BaseInterceptor implements Interceptor {
    private int maxRetry = 3;//最大重试次数
    //    延迟
    private long delay = 500;
    //    叠加延迟
    private long increaseDelay = 500;
    private List<String> mUrlList = new ArrayList<>();

    BaseInterceptor() {
        //有的接口数据不需要解密操作 所以增加一个List添加不用加密的接口的HOST来判断
        mUrlList.add(BuildConfig.VERSION_HOST);
        mUrlList.add(BuildConfig.CONFIRM_PAY_HOST);
    }

    @Override
    public Response intercept(@NonNull Chain chain) throws IOException {
        RetryWrapper retryWrapper = proceed(chain);
        while (retryWrapper.isNeedReTry()) {
            retryWrapper.retryNum++;
            try {
                Thread.sleep(delay + (retryWrapper.retryNum - 1) * increaseDelay);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            proceed(chain, retryWrapper.request, retryWrapper);
        }
        return retryWrapper.response == null ? decrypt(chain.proceed(chain.request())) : decrypt(retryWrapper.response);
    }

    //在这个方法里面拦截请求然后获取到原来的baseUrl
    private RetryWrapper proceed(Chain chain) throws IOException {
        Request originalRequest = chain.request();
        RetryWrapper retryWrapper = null;
        //获取老的url
        HttpUrl oldUrl = originalRequest.url();
        //获取originalRequest的创建者builder
        Request.Builder builder = originalRequest.newBuilder();
        //获取头信息的集合如:manage和yzx
        List<String> urlNameList = originalRequest.headers("url");
        if (urlNameList != null && urlNameList.size() > 0) {
            //删除原有配置中的值,就是namesAndValues集合里的值
            builder.removeHeader("url");
            //获取头信息中配置的value,如:manage和yzx
            String urlName = urlNameList.get(0);
            HttpUrl baseURL = null;
            //根据头信息中配置的value,来匹配新的base_url地址
            if ("confirm".equals(urlName)) {
                baseURL = HttpUrl.parse(BuildConfig.CONFIRM_PAY);
            } else if ("version".equals(urlName)) {
                baseURL = HttpUrl.parse(BuildConfig.VERSION);
            }
            //重建新的HttpUrl,需要重新设置的url部分
            if (baseURL != null) {
                HttpUrl newHttpUrl = oldUrl.newBuilder()
                        .scheme(baseURL.scheme())//http协议如:http或者https
                        .host(baseURL.host())//主机地址
                        .port(baseURL.port())//端口
                        .build();
                //获取处理后的新newRequest
                Request newRequest = builder.url(newHttpUrl).build();
                retryWrapper = new RetryWrapper(newRequest, maxRetry);
                proceed(chain, newRequest, retryWrapper);
            } else {
                retryWrapper = new RetryWrapper(originalRequest, maxRetry);
                proceed(chain, originalRequest, retryWrapper);
            }
        }
        return retryWrapper;
    }

    private void proceed(Chain chain, Request request, RetryWrapper retryWrapper) throws IOException {
        try {
            Response response = chain.proceed(request);
            retryWrapper.setResponse(response);
        } catch (SocketException | SocketTimeoutException e) {
            e.printStackTrace();
        }
    }

    static class RetryWrapper {
        volatile int retryNum = 1;
        Request request;
        Response response;
        private int maxRetry;

        RetryWrapper(Request request, int maxRetry) {
            this.request = request;
            this.maxRetry = maxRetry;
        }

        public void setResponse(Response response) {
            this.response = response;
        }

        boolean isSuccessful() {
            return response != null && response.isSuccessful();
        }

        boolean isNeedReTry() {
            return !isSuccessful() && retryNum < maxRetry;
        }

    }

    /**
     * 解密过程
     */
    private Response decrypt(Response response) throws IOException {
        String responseStr = "";
        if (response.isSuccessful()) {
            String oldBody = response.body().string();
            if (!TextUtils.isEmpty(oldBody)) {
                try {
                	//这里根据该请求的Url的Host来判断这个接口是否需要进行解密
                    if (mUrlList.contains(response.request().url().host())) {
                    	//不需要解密
                        responseStr = oldBody;
                    } else {
                   		 //进行解密操作
                        responseStr = new String(EncryptionTools.crypt(Base64.decode(oldBody)));
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            return response.newBuilder().body(ResponseBody.create(null, responseStr)).build();
        } else {
        	//这里这个错误好像是有问题的现在想到好的办法以后再好好研究下怎么处理异常
            throw new IOException("Unexpected code " + response);
        }
    }
}

这里只是Retrofit+OKHttp的简单使用,有兴趣的朋友可以尝试一下,玩Android这个网站有不错的这一类的文章。

写这篇文章来记录自己的学习过程。。刚开始写博客有不对的地方请指正。。谢谢

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值