Retrofit 2 使用方法,一个便捷的网络加载库

Retrofit是什么,Retrofit可以理解为OkHttp的强化版,它底层基于OkHttp,但是又对其做了封装和处理,又使用大量注解对代码进行简化,且支持很多开源库,例如OkHttp+Retrofit+RxJava

使用Retrofit有什么好处呢?看到RxJava就可以知道Retrofit支持同步异步请求,还可以配置不同的HttpClient实现网络请求,而且还有一个非常大的用途就是解耦。在实现网络请求的时候我们很多时候会在一个类中实现非常多的方法,什么传参啊,解析啊,呈现啊,都归拢在一起,以前还感觉洋洋得意,自己的代码都放在一起,查找起来特别方便,但是却写了太多的冗余代码,后来又提取封装,但是耦合性又太大,往往改一处会导致其他方面的问题,而Retrofit很重要的一个方面就是解耦,让方法各行其职。

提到Retrofit就不得不提他的注解,Retrofit注解非常之多

请求方法

@GETGET请求
@POSTPOST请求
@DELETEDELETE请求
@HEADHEAD请求
@OPTIONSOPTIONS请求
@PATCHPATCH请求

传入参数

@Headers添加请求头
@Path替换路径
@Query替代参数值,结合Get请求使用
@FormUrlEncoded表单参数提交
@Field替换参数值,结合Post请求使用
@Url替换路径

这些都是一些常用的注解,还有一些不常用的注解就不介绍了,如果有需要可以直接看源码文档

如何使用

首先我们先新建一个Http请求工具类,用于定义一些通用方法,进行统一管理,因为都写在代码注释里了,我这里不在强调了,主要看代码吧。

public class RetrofitHleperUtil {
    /**
     * 
     */
    private RetrofitHleperUtil retorfitHleperInstance;
    private OkHttpClient okHttpClient;
    private Retrofit mRetrofit;
    private NetWorkRequertLogInterceptor netWorkRequertLogInterceptor;

    /**
     * 写一个单例用于管理RetrofitHleperUtil类,报证只会被创建一次
     *
     * @return
     */
    public RetrofitHleperUtil getRetorfitHleperInstance() {
        if (retorfitHleperInstance == null) {
            retorfitHleperInstance = new RetrofitHleperUtil();
        }
        return retorfitHleperInstance;
    }

    /**
     * OkHttpClient 配置
     *
     * @return
     */
    private OkHttpClient getOkHttpClient() {
        if (okHttpClient == null) {
            okHttpClient = new OkHttpClient.Builder()
                    //设置连接超时时间
                    .connectTimeout(10, TimeUnit.SECONDS)
                    //设置写入超时时间
                    .writeTimeout(10, TimeUnit.SECONDS)
                    //设置读取超时时间
                    .readTimeout(10, TimeUnit.SECONDS)
                    //添加拦截器
                    .addInterceptor(getNetWorkRequertLogInterceptor())
                    .build();
        }
        return okHttpClient;
    }

    /**
     * 获取拦截器
     * @return
     */
    private NetWorkRequertLogInterceptor getNetWorkRequertLogInterceptor() {
        if (netWorkRequertLogInterceptor == null) {
            netWorkRequertLogInterceptor = new NetWorkRequertLogInterceptor();
        }
        return netWorkRequertLogInterceptor;
    }


    /**
     * 配置Retrofit
     *
     * @return
     */
    public RetroiftInterface init() {
        if (mRetrofit == null) {
            mRetrofit = new Retrofit.Builder()
                    //设置请求路径,结尾必须以'/'结尾,不然会报错
                    .baseUrl("http://api.kuailai.me/")
                    //加入OkHttpClient配置
                    .client(getOkHttpClient())
                    .build();
        }
        return mRetrofit.create(RetroiftInterface.class);
    }

}

这里在OkHttpClient加入了拦截器打印功能,我们看下拦截器的配置,注释写的很详细,就不一一说明了,这里强调一下,使用response.body.string拦截器打印返回数据时一定要记得重新新建一个,不然会报错,因为OkHttp下response.body.string打印一次后会关闭

public class NetWorkRequertLogInterceptor implements Interceptor {

    private static final String TAG = "NetWorkRequertLogInterc";

    @Override
    public Response intercept(Chain chain) {
        /**
         * request 请求体
         * response 返回体
         * mediaType 媒介类型
         * content 返回body
         * stringparam 请求参数
         */
        Request request = null;
        Response response = null;
        MediaType mediaType = null;
        String content = "";
        StringBuffer stringparam;
        try {
            request = chain.request();
            response = chain.proceed(chain.request());

            /**
             * 添加单个头部信息
             *  request.header()
             *  添加多个头部信息
             *   request.headers();
             */
            stringparam = new StringBuffer();
            content = response.body().string();
            if (request.body() instanceof FormBody) {
                FormBody body = (FormBody) request.body();
                for (int i = 0; i < body.size(); i++) {
                    stringparam.append(body.encodedName(i) + ":" + body.encodedValue(i) + ",");
                }
            }
  //打印请求地址,请求参数,返回数据
                Log.d(TAG,  "-----" + "\nurl:" + request.url() + "\nparam:" + stringparam + "\nbody:" + content + "\n-----");
        } catch (Exception e) {
            //返回错误信息
            Log.d(TAG,e.toString());
        }
        /**
         * response.body.string只会调用一次,调用一次后会自动close,所以新建了一个
         */
        return response.newBuilder()
                .body(okhttp3.ResponseBody.create(mediaType, content))
                .build();
    }
}

写完了工具类我们就可以正式开始工作了,首先我们先看GET请求

@GET请求

1.无参GET请求

细心的朋友肯定发现了我在getRetrofit()方法中写了这么一句代码:return mRetrofit.create(RetroiftInterface.class);,但是我并没用多做介绍,现在我说下,大家看到我在这里creat了一下,传入参数是RetroiftInterface,这个RetroiftInterface是干嘛用的呢?RetroiftInterface 就是配置网络请求的地址和参数的接口类。我们看下示例,凡是请求网络,只需要把连接连接地址和参数在这里配置就OK了,这样就完成了部分解耦和代码提取封装工作,不用在各个类去重复写

正式开始网络请求,首先获取RetroiftInterface实列

点击方法请求网络获取数据

  retroiftInterface.getNoParam().enqueue(new Callback<ResponseBody>() {
            @Override
            public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
            }

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

看看我们的拦截器是否又效果,结果正常,因为是无参请求所以param是空的,地址和返回数据也是正常的

我们在onResponse里打印看看是否一致,加入打印语句

2.有参GET请求

有参和无参请求差别并不大,主要差别就是在配置这里和传入参数这里

先看一下配置

 

在这里我们用了一个@Path注解,在前面我们介绍过,@Path是用来替换路径的,这里就将index替换为了传入的页数,然后使用@Query作为参数传入,然后看看如何传入参数的,传入参数和平常调用方法传入没什么区别,直接调用方法传入参数就OK了,代码如下:

点击运行,打印结果成功返回好像并没有什么异常,但是仔细观察一下就会发现我们的传入的参数并没有打印出来,这是为什么呢?因为我们在拦截器中只做了对表单参数进行打印,其他并未做打印操作,如需要,请自行增加判断解决,我没找到方法,?!

 

@POST请求

1.表单请求

表单请求和GET有参请求也没又太大区别,只是加了新的注解而已,使用方法还是一样的,@FofmUrlEncoded和@Field,这两个是针对post表单请求使用的,至于如何使用,这几乎和GET传入有参请求是一样的,直接调用该方法就行了。

 retroiftInterface.postFrom("victory", "123456").enqueue(new Callback<ResponseBody>() {
            @Override
            public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
                try {
                    Log.d(TAG, "onNext: " + response.code());
                    Log.d(TAG, "onNext: " + response.message());
                    Log.d(TAG, "onNext: " + response.body().string());
                } catch (IOException e) {

                }
            }

            @Override
            public void onFailure(Call<ResponseBody> call, Throwable t) {

            }
        });

 

这次我们发现参数正常打印出来了,说明我们的打印参数是没有问题的,但是如何打印其他形式的传入参数还得多多考虑(其实还可以在传入时打印,但是多接口调用时就是不好匹配地址和对应的数据了)。

2.JSON请求

传入JSON数据当参数比传入表单稍微复杂了点,但是也差不多,我们接下来看另一个注解@Headers,在上面我们介绍过了,这个是传入头部信息,其实如果是全部接口都是JSON数据传入的话,我们可以在拦截器里直接设置,而非在配置里设置,先看看如何写接口的。使用@Headers设置头部信息,告诉Retrofit我们要用JSON格式的数据作为参数,@Body是传入数据的类型

使用方法也稍稍有了改变,我们先看代码在介绍。因为玩安卓使用的都是表单传入数据,所以我用我们公司的一个接口进行测试。因为接收参数是Body类型所以我们要进行在代码中进行类型转换一些,这里OkHttp提供了转换方法,使用RequestBody进行类型转换,然后作为参数传入就行了

     JSONObject jsonObject = new JSONObject();
        try {
            jsonObject.put("MethodName", "MachineInfo");
            jsonObject.put("UserAccount", "你的手机号");
            jsonObject.put("PhoneModel", android.os.Build.MODEL);
            jsonObject.put("Manufacturer", android.os.Build.MANUFACTURER);
            jsonObject.put("System", "Android");
            jsonObject.put("SystemVersion", android.os.Build.VERSION.SDK);
            jsonObject.put("AppVersion", "8.1");
        } catch (Exception e) {

        }
        RequestBody requestBody = RequestBody.create(MediaType.parse("application/json"), jsonObject.toString());
        retroiftInterface.postJSON(requestBody)
                .enqueue(new Callback<ResponseBody>() {
                    @Override
                    public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
                        try {
                            Log.d(TAG, "onNext: " + response.code());
                            Log.d(TAG, "onNext: " + response.message());
                            Log.d(TAG, "onNext: " + response.body().string());
                        } catch (IOException e) {

                        }
                    }

                    @Override
                    public void onFailure(Call<ResponseBody> call, Throwable t) {

                    }
                });

 

下载文件

下载文件我们使用@Url动态设置下载文件路径,@Url跟@Path功能差不多,都是替换访问路径,@Url是替换全局的,因为下载文件也是我司的接口,所以就用@Url直接替换了。

这里我写了两个写入方法,一个是不带进度的一个是带进度的,原理相同,我就拿带进度的举例子了

       retroiftInterface.getFileDwon("http://ftpfile.kuailai.me/appversion/download/comingsoonsystemonline.apk")
                .enqueue(new Callback<ResponseBody>() {
                    @Override
                    public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
                        Log.d(TAG, "onNext: " + response.code());
                        Log.d(TAG, "onNext: " + response.message());
                        Log.d(TAG, "onNext: " + response.body().contentLength());
                        File file = new File(Environment.getExternalStorageDirectory().getPath() + "/123456.apk");
                        try {
                            //常用方法
//                            BufferedSource source = response.body().source();
//                            BufferedSink sink = Okio.buffer(Okio.sink(file));
//                            sink.writeAll(source);
//                            sink.flush();
//                            带进度方法
//                          因为都是常用的写入内存方法就不加以说明了,但是提醒一个事情,就是别//                          忘了打开权限
                            OutputStream os =new FileOutputStream(file);
                            InputStream is=response.body().byteStream();
                            int len;
                            long currentLength=0;
                            double con=response.body().contentLength();
                            byte [] buff = new byte[1024];
                            while((len=is.read(buff))!=-1){
                                currentLength+=len;
                                Log.d(TAG, "onNext:len " +len);
                                Log.d(TAG, "onNext:cur " +currentLength);
                                Log.d(TAG, "onNext:con " +con);
                                Log.d(TAG, "onNext:pro " +String.format("%.2f",currentLength/con));
                                os.write(buff,0,len);
                            }
                            os.close();
                            is.close();
                        } catch (Exception e) {
                            Log.e(TAG, "onResponse: " + e.toString());
                        }
                    }

                    @Override
                    public void onFailure(Call<ResponseBody> call, Throwable t) {

                    }
                });

因为是文件所以打印除了会出现乱码情况,单不影响实际使用效果,下面则打印出来了下载的进度。。。到文件夹找一下我们的12345apk文件

上传文件(因为上传文件,手头没有方便的接口,所以就单独举个例子吧)

配置并无什么特殊的,跟正常传参一样

使用的时候稍稍有点复杂,我们先要使用MediaType知道文件类型,然后找到文件,最后使用RequestBody传入,然后就完成了。运行发现并未报错,但是实际操作会如何还需要你自己使用

 MediaType mediaType = MediaType.parse("file/*");
        File file = new File(Environment.getExternalStorageDirectory().getPath() + "/aa.jpg");
        RequestBody requestBody = RequestBody.create(mediaType, file);
        retroiftInterface.postUpLoad("https://www.baidu.com",requestBody)
                .enqueue(new Callback<ResponseBody>() {
                    @Override
                    public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
                        Log.d(TAG, "onNext: " + response.code());
                        Log.d(TAG, "onNext: " + response.message());
                        Log.d(TAG, "onNext: " + response.body().contentLength());
                    }

                    @Override
                    public void onFailure(Call<ResponseBody> call, Throwable t) {

                    }
                });

 

到这里Retrofit差不多也搞一段落了,公布下源码吧

GitHub

CSDN

 

 

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值