掌握Okhttp (1) : OKhttp的基本使用

HTTP是现代应用常用的一种交换数据和媒体的网络方式,高效地使用HTTP能让资源加载更快,节省带宽。OkHttp是一个高效的HTTP客户端,它有以下默认特性:

支持HTTP/2,允许所有同一个主机地址的请求共享同一个socket连接
连接池减少请求延时
透明的GZIP压缩减少响应数据的大小
缓存响应内容,避免一些完全重复的请求

当网络出现问题的时候OkHttp依然坚守自己的职责,它会自动恢复一般的连接问题,如果你的服务有多个IP地址,当第一个IP请求失败时,OkHttp会交替尝试你配置的其他IP,OkHttp使用现代TLS技术(SNI, ALPN)初始化新的连接,当握手失败时会回退到TLS 1.0。

note: OkHttp 支持 Android 2.3 及以上版本Android平台, 对于 Java, JDK 1.7及以上.

okhttp的GitHub地址如下: 点击跳转

本文demo中使用的接口为 玩安卓 点击跳转 网站提供.

另外,如果你的targetSdkVersion ≥28.还需要注意以下问题:

由于Android P系统限制了明文流量的网络请求,网络请求url要么使用https. 如果使用的只能是http,有2种处理方式:
方法一:
targetSdkVersion 降级回到 27

方法二:
在res目录下新建xml文件夹,文件夹里面创建network_security_config.xml 文件;
文件内容

<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="true" />
</network-security-config>

然后在 AndroidManifest.xml 的application 标签加上

android:networkSecurityConfig="@xml/network_security_config"

如果不进行上述操作,在使用okhttp3请求时,会报如下错误:

CLEARTEXT communication to * not permitted by network 。

一. 基本配置:

1. 依赖:

implementation 'com.squareup.okhttp3:okhttp:3.10.0'

从4.0.0开始,okhttp使用kotlin编码, 因后面涉及okhttp的源码分析,本系列文章若无特殊说明均使用 3.10.0 版本.

2. 权限:

<uses-permission android:name="android.permission.INTERNET" />

最简单常用的网络权限.

如果使用 DiskLruCache,那还得声明写外存的权限.

二.使用

1. 异步GET请求

  • new OkHttpClient;

  • 构造Request对象;

  • 通过前两步中的对象构建Call对象;

  • 通过Call#enqueue(Callback)方法来提交异步请求;

String url = "https://www.wanandroid.com/article/list/1/json";
OkHttpClient okHttpClient = new OkHttpClient();
final Request request = new Request.Builder()
        .url(url)
        .get()//默认就是GET请求,可以不写
        .build();
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
    
    
    @Override
    public void onFailure(Call call, IOException e) {
    
    
        Log.e(TAG, "onFailure: ");
    }

    @Override
    public void onResponse(Call call, Response response) throws IOException {
    
    
        Log.e(TAG, "onResponse: " + response.body().string());
    }
});

2. 同步GET请求

前面几个步骤和异步方式一样,只是最后一部是通过 Call#execute() 来提交请求,注意这种方式会阻塞调用线程,所以在Android中应放在子线程中执行,否则有可能引起ANR异常。

String url = "https://www.wanandroid.com/article/list/1/json";
OkHttpClient okHttpClient = new OkHttpClient();
final Request request = new Request.Builder()
        .url(url)
        .build();
final Call call = okHttpClient.newCall(request);
new Thread(new Runnable() {
    
    
    @Override
    public void run() {
    
    
        try {
    
    
            Response response = call.execute();
            Log.e(TAG, "run: " + response.body().string());
        } catch (IOException e) {
    
    
            e.printStackTrace();
        }
    }
}).start();

3.POST方式提交表单

        OkHttpClient okHttpClient = new OkHttpClient();
        RequestBody requestBody = new FormBody.Builder()
                .add("username", "weibuzudao")
                .add("password", "123456")
                .build();
        Request request = new Request.Builder()
                .url("https://www.wanandroid.com/user/login")
                .post(requestBody)
                .build();

        okHttpClient.newCall(request).enqueue(new Callback() {
    
    
            @Override
            public void onFailure(Call call, IOException e) {
    
    
                Log.e(TAG, "onFailure: " + e.getMessage());
            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
    
    
                Log.e(TAG, "onResponse: " + response.body().string());
            }
        });

提交表单时,使用 RequestBody 的实现类FormBody来描述请求体,它可以携带一些经过编码的 key-value 请求体,键值对存储在下面两个集合中:

 private final List<String> encodedNames;
 private final List<String> encodedValues;

三.拦截器 (interceptor)

OkHttp的拦截器链可谓是其整个框架的精髓,用户可传入的 interceptor 分为两类:
一类是全局的 interceptor,该类 interceptor 在整个拦截器链中最早被调用,通过 OkHttpClient.Builder#addInterceptor(Interceptor) 传入;

另外一类是非网页请求的 interceptor ,这类拦截器只会在非网页请求中被调用,并且是在组装完请求之后,真正发起网络请求前被调用,所有的 interceptor 被保存在 List<Interceptor> interceptors 集合中,按照添加顺序来逐个调用,具体可参考 RealCall#getResponseWithInterceptorChain() 方法。通过 OkHttpClient.Builder#addNetworkInterceptor(Interceptor) 传入;

这里举一个简单的例子,例如有这样一个需求,我要监控App通过 OkHttp 发出的所有原始请求,以及整个请求所耗费的时间,针对这样的需求就可以使用第一类全局的 interceptor 在拦截器链头去做。

OkHttpClient okHttpClient = new OkHttpClient.Builder()
        .addInterceptor(new LoggingInterceptor())
        .build();
        
Request request = new Request.Builder()
        .url("http://www.publicobject.com/helloworld.txt")
        .header("User-Agent", "OkHttp Example")
        .build();
        
okHttpClient.newCall(request).enqueue(new Callback() {
    
    
    @Override
    public void onFailure(Call call, IOException e) {
    
    
        Log.d(TAG, "onFailure: " + e.getMessage());
    }

    @Override
    public void onResponse(Call call, Response response) throws IOException {
    
    
        ResponseBody body = response.body();
        if (body != null) {
    
    
            Log.d(TAG, "onResponse: " + response.body().string());
            body.close();
        }
    }
});
public class LoggingInterceptor implements Interceptor {
    
    
    private static final String TAG = "LoggingInterceptor";
    
    @Override
    public Response intercept(Chain chain) throws IOException {
    
    
        Request request = chain.request();

        long startTime = System.nanoTime();
        Log.d(TAG, String.format("Sending request %s on %s%n%s",
                request.url(), chain.connection(), request.headers()));

        Response response =  chain.proceed(request);

        long endTime = System.nanoTime();
        Log.d(TAG, String.format("Received response for %s in %.1fms%n%s",
                response.request().url(), (endTime - startTime) / 1e6d, response.headers()));

        return response;
    }
}

针对这个请求,打印出来的结果

Sending request http://www.publicobject.com/helloworld.txt on null
User-Agent: OkHttp Example
        
Received response for https://publicobject.com/helloworld.txt in 1265.9ms
Server: nginx/1.10.0 (Ubuntu)
Date: Wed, 28 Mar 2018 08:19:48 GMT
Content-Type: text/plain
Content-Length: 1759
Last-Modified: Tue, 27 May 2014 02:35:47 GMT
Connection: keep-alive
ETag: "5383fa03-6df"
Accept-Ranges: bytes

注意到一点是这个请求做了重定向,原始的 request url 是 http://www.publicobject.com/helloworld.tx,而响应的 request url 是 https://publicobject.com/helloworld.txt,这说明一定发生了重定向,但是做了几次重定向其实我们这里是不知道的,要知道这些的话,可以使用 addNetworkInterceptor()去做。

在这里插入图片描述

四.其他

推荐让 OkHttpClient 保持单例,用同一个 OkHttpClient 实例来执行你的所有请求,因为每一个 OkHttpClient 实例都拥有自己的连接池和线程池,重用这些资源可以减少延时和节省资源,如果为每个请求创建一个 OkHttpClient 实例,显然就是一种资源的浪费。当然,也可以使用如下的方式来创建一个新的 OkHttpClient 实例,它们共享连接池、线程池和配置信息。

 OkHttpClient eagerClient = client.newBuilder()
        .readTimeout(500, TimeUnit.MILLISECONDS)
        .build();
        
Response response = eagerClient.newCall(request).execute();

每一个Call(其实现是RealCall)只能执行一次,否则会报异常,具体参见 RealCall#execute()

猜你喜欢

转载自blog.csdn.net/gaolh89/article/details/104256499
今日推荐