Android开发之Retrofit/OkHttp使用

本文介绍了Android开发中常用的网络请求库OkHttp及其优点,包括连接池、GZIP压缩和缓存机制。详细阐述了OkHttp的拦截器机制,包括应用拦截器和网络拦截器的区别。此外,还探讨了Retrofit这一基于OkHttp的RESTful框架,说明了Retrofit如何简化网络请求接口的封装,并提供了使用示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

OkHttp
简介

OkHttp是时下最火的Http请求框架,其官网及源码地址如下:

相比于其他网络请求框架,OkHttp有什么优点呢?官方介绍如下:

  • HTTP/2 support allows all requests to the same host to share a socket.
  • Connection pooling reduces request latency (if HTTP/2 isn’t available).
  • Transparent GZIP shrinks download sizes.
  • Response caching avoids the network completely for repeat requests.

从上面的介绍我们可以得出有下面这些优点:

  • 在Http/2的支持下,如果网络请求的host是同一个时,允许这些请求共用一个socket。
  • 使用连接池减少请求延迟。
  • 透明的GZIP压缩,减少数据流量。
  • 支持响应缓存,避免重复的网络请求。
使用
添加依赖
    implementation 'com.squareup.okhttp3:okhttp:3.11.0'
    implementation 'com.squareup.okhttp3:logging-interceptor:3.10.0'
GET请求
        //创建OkHttpClient对象
        OkHttpClient httpClient = new OkHttpClient();
        //创建Request对象
        Request request = new Request.Builder()
                .url("http://www.baidu.com")
                .addHeader("key", "value")
                .get()
                .build();
        
        //get方法同步请求
        try {
            Response response = httpClient.newCall(request).execute();
            if (response.isSuccessful()){
                String result = response.body().string();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
       
        //get方法异步请求,异步请求的回调方法均在子线程中执行
        httpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                //请求失败 
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                //请求成功
                String result = response.body().string();
            }
        });
POST请求
        //post请求
        MediaType mediaType = MediaType.parse("application/json; charset=utf-8");
        String data = "{key:value}";
        RequestBody requestBody = RequestBody.create(mediaType, data);
        Request request1 = new Request.Builder()
                .url("http://www.baidu.com")
                .post(requestBody)
                .build();
        //post同步请求
        try {
            Response response = httpClient.newCall(request1).execute();
            if (response.isSuccessful()) {
                String result = response.body().string();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        //post异步请求
        httpClient.newCall(request1).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
                //请求失败
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                //请求成功
                String result = response.body().string();
            }
        });

拦截器、超时、缓存机制

OKHttp的拦截器分为应用拦截器(Application interceptors)和网络拦截器(Network Interceptors),下图是官方对拦截器的介绍。
在这里插入图片描述
两种拦截器的相对优势官方说明如下:
Each interceptor chain has relative merits.
Application interceptors

  • Don’t need to worry about intermediate responses like redirects and retries.
  • Are always invoked once, even if the HTTP response is served from the cache.
  • Observe the application’s original intent. Unconcerned with OkHttp-injected headers like If-None-Match.
  • Permitted to short-circuit and not call Chain.proceed().
  • Permitted to retry and make multiple calls to Chain.proceed().

Network Interceptors

  • Able to operate on intermediate responses like redirects and retries.
  • Not invoked for cached responses that short-circuit the network.
  • Observe the data just as it will be transmitted over the network.
  • Access to the Connection that carries the request.

翻译过来为:

应用拦截器

  • 不用关系redirects和retries的中间过程
  • 永远只会执行一次,不管response是否从缓存中获取
  • 可以监控原始的请求,不关心其它诸如If-None-Match的Header
  • 允许不调用Chain.proceed()
  • 允许重试多次调用Chain.proceed()

网络拦截器

  • 可以操作redirects和retries的过程
  • 不会调用缓存的response
  • 可以监控网络传输交互的数据
  • 可以获取Connection携带的请求信息

它们的区别在于:Application Interceptor拦截应用层与OkHttp之间的网络响应;而Network Interceptor拦截OkHttp与网络层之间的网络请求响应。应用拦截器通过addInterceptor()的方法添加,而网络拦截器通过addNetworkInterceptor()方法添加。

        //创建拦截器
        Interceptor logInterceptor = new Interceptor() {
            @Override
            public Response intercept(Chain chain) throws IOException {
                Request request2 = chain.request();
                Logger logger = Logger.getGlobal();
                long t1 = System.nanoTime();
                logger.info(String.format("Sending request %s on %s%n%s"
                        , request2.url(), chain.connection(), request2.headers()));
                Response response = chain.proceed(request2);
                long t2 = System.nanoTime();
                logger.info(String.format("Received response for %s in %.1fms%n%s",
                        response.request().url(), (t2 - t1) / 1e6d, response.headers()));
                return response;
            }
        };
	    //为OkHttpClient添加拦截器、超时、缓存控制
        Cache cache = new Cache(new File(Environment.getDataDirectory(), "cache"), 10 * 10 * 1024);
        OkHttpClient client = new OkHttpClient.Builder()
                .connectTimeout(10, TimeUnit.SECONDS)//连接超时时间
                .writeTimeout(10, TimeUnit.SECONDS)//写操作超时时间
                .readTimeout(10, TimeUnit.SECONDS)//读操作超时时间
                .cache(cache)//添加缓存
//                .addInterceptor(logInterceptor)//添加应用拦截器
                .addNetworkInterceptor(logInterceptor)//网络拦截器
                .build();

以下是将logInterceptor分别添加成应用拦截器和网络拦截器所得到的打印信息:

//应用拦截器
03-07 08:00:37.760 2550-2606/com.example.study I/global: Sending request http://www.baidu.com/ on null
03-07 08:00:37.850 2550-2606/com.example.study I/global: Received response for http://www.baidu.com/ in 85.0ms
    Server: bfe/1.0.8.18
    Date: Thu, 07 Mar 2019 13:00:36 GMT
    Content-Type: text/html
    Last-Modified: Mon, 23 Jan 2017 13:28:12 GMT
    Transfer-Encoding: chunked
    Connection: Keep-Alive
    Cache-Control: private, no-cache, no-store, proxy-revalidate, no-transform
    Pragma: no-cache
    Set-Cookie: BDORZ=27315; max-age=86400; domain=.baidu.com; path=/
//网络拦截器
03-07 08:04:22.471 2812-2849/com.example.study I/global: Sending request http://www.baidu.com/ on Connection{www.baidu.com:80, proxy=DIRECT@ hostAddress=www.baidu.com/183.232.231.172:80 cipherSuite=none protocol=http/1.1}
    Host: www.baidu.com
    Connection: Keep-Alive
    Accept-Encoding: gzip
    User-Agent: okhttp/3.11.0
03-07 08:04:22.511 2812-2849/com.example.study I/global: Received response for http://www.baidu.com/ in 41.8ms
    Cache-Control: private, no-cache, no-store, proxy-revalidate, no-transform
    Connection: Keep-Alive
    Content-Encoding: gzip
    Content-Type: text/html
    Date: Thu, 07 Mar 2019 13:04:22 GMT
    Last-Modified: Mon, 23 Jan 2017 13:28:12 GMT
    Pragma: no-cache
    Server: bfe/1.0.8.18
    Set-Cookie: BDORZ=27315; max-age=86400; domain=.baidu.com; path=/
    Transfer-Encoding: chunked

对比两个打印信息发现Sending Request最后的on后面跟的connection应用拦截器为null,而网络拦截器不为null,这说明应用拦截器是先拦截后发送请求的,而网络拦截器则是先发送请求后拦截信息的。

需要注意的是,OkHttp请求返回的response.body().string()方法只能调用一次,若调用两次将会抛出异常。除了使用response.body().string()获取String数据外,还可以通过response.body().bytes()和response.body().byteStream()分别获取字节数组和InputStream输入流。关于OkHttp的更多用法可以到官方的wiki中进一步的了解。

Retrofit

Retrofit是一个基于OkHttp的RESTFUL网络加载框架,它的网络请求工作是通过OkHttp完成的,而Retrofit仅负责网络请求接口的封装。

使用
添加依赖
 implementation 'com.squareup.retrofit2:retrofit:2.1.0'//retrofit 
 implementation 'com.google.code.gson:gson:2.6.2'//Gson 库 
 //下面两个是RxJava 和RxAndroid 
 implementation 'io.reactivex:rxjava:1.1.0' 
 implementation 'io.reactivex:rxandroid:1.1.0'  
 implementation 'com.squareup.retrofit2:converter-gson:2.1.0'//转换器,请求结果转换成Model 
 implementation 'com.squareup.retrofit2:adapter-rxjava:2.1.0'//配合Rxjava 使用
创建请求接口
public interface ApiService {

    //使用GET方法请求数据,配合@Query和@QueryMap使用
    @GET("top250")
    Call<DataBean> getTop250(@Query("start") int start, @Query("count") int count);

    //使用POST方法发送form-encoded请求数据,@FormUrlEncoded可以配合@Filed和@FieldMap使用
    @POST("top250")
    @FormUrlEncoded
    Call<DataBean> getTop250ByPost(@Field("start") int start, @Field("count") int count);

    //使用POST方法发送自定义数据类型请求数据
    @POST("top250")
    Call<DataBean> getTop250ByPostBody(@Body DataBean data);
}
使用Retrofit请求数据
        String baseUrl = "https://api.douban.com/v2/movie/";
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(baseUrl)
                .addConverterFactory(GsonConverterFactory.create())
                .build();

        ApiService apiService = retrofit.create(ApiService.class);
        Call<DataBean> top250 = apiService.getTop250(1, 10);
        //同步请求
        try {
            Response<DataBean> execute = top250.execute();
            if (execute.isSuccessful()) {
                DataBean result = execute.body();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        //异步请求
        top250.enqueue(new Callback<DataBean>() {
            @Override
            public void onResponse(Call<DataBean> call, Response<DataBean> response) {
                //方法在主线程中回调
                DataBean result = response.body();
            }

            @Override
            public void onFailure(Call<DataBean> call, Throwable t) {
                //方法在主线程中回调
            }
        });

Retrofit的请求方法与请求参数均在请求接口中声明好了,在使用过程中,只需要通过Retrofit的实例获取到请求接口类的代理对象,即可通过代理对象进行网络请求,请求也分为同步和异步两种方式。另外,Retrofit底层默认使用OkHttp进行网络请求,我们可以配置自己的OkHttp各项参数,通过clinet()方法将Retrofit的OkHttpClient设置为我们自己的OkHttpClient。

参考资料:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值