简单使用
-
OkHttp的github:https://github.com/square/okhttp
-
首先需要添加依赖
implementation("com.squareup.okhttp3:okhttp:4.0.1")
-
添加网络权限
<uses-permission android:name="android.permission.INTERNET" />
-
然后创建OkHttpClient对象
OkHttpClient client = new OkHttpClient(); //若要配置OkHttpClient,则应该通过OkHttpClient的Builder来创建OkHttpClient。 OkHttpClient client = new OkHttpClient.Builder() .connectTimeout(3, TimeUnit.SECONDS) .retryOnConnectionFailure(true) //... .build();
-
接着,创建Request对象
// 上传单个文件 RequestBody requestBody = RequestBody.create("", MediaType.parse("text/plain;charset=utf-8")); // 上传表单 RequestBody requestBody1 = new MultipartBody.Builder() //上传表单必须要有这一句 .setType(MultipartBody.FORM) .addFormDataPart("key", "value") .build(); //FormBody是RequestBody的一个子类,只能用来上传表单内容都为字符串的表单 FormBody formBody = new FormBody.Builder() .add("key", "value") .build(); Request request = new Request.Builder() .url("https://www.google.com") //.get() //.method("post", requestBody) .post(formBody) .addHeader("content-type", "application/json") .build();
-
将Request对象封装为Call对象
Call call = client.newCall(request);
-
决定同步还是异步执行
// 同步执行 call.execute(); // 异步执行 call.enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { Toast.makeText(context, "请求失败", Toast.LENGTH_SHORT).show(); } @Override public void onResponse(Call call, final Response response) throws IOException { String res = response.body().string(); } });
源码分析
整体架构
简介
如图所示OkHttp总体架构大概分为以下几层:
- Interface——接口层:接收网络请求访问
- Protocol层——协议层:处理协议逻辑
- Connection——连接层:管理网络连接,发送新的请求,接受服务器访问
- Cache——缓存层:管理本地缓存
- I/O——I/O层:实际数据读写实现
- Inteceptor——拦截器层:拦截网络请求,插入拦截逻辑
Interface——接口层
接口层用于接收用户的网络请求,发起实际的网络请求。具体见下面分析。
Protocol——协议层
协议层负责处理协议逻辑,OkHttp支持HTTP1/HTTP2/WebSocket三种协议。
Connection——连接层
该层负责网络连接。在连接层有一个连接池(RealConnectionPool),统一管理所有的Socket连接,当用户发起一个网络请求时,OkHttp会先从连接池中查找是否有符合要求的连接,若有则直接通过该连接发送网络请求,否则建立一个新连接发送请求。
RealConnection描述一个物理Socket连接,连接池中维护多个RealConnection对象。由于HTTP2支持多路复用,一个RealConnection支持多个网络访问请求,因此又引入了StreamAllocation来描述一个实际的网络请求开销,一个RealConnection对应一个或多个StreamAllocation,因此StreamAllocation可以看做是RealConnection的计数器,当RealConnection的引用计数变为0,且长时间没有被其他请求重新占用就将被释放。
Cache——缓存层:管理本地缓存
该层负责维护请求缓存,当用户的网络请求在本地已有符合要求的缓存时,OkHttp会直接从缓存中返回结果。见缓存拦截器的分析。
I/O——I/O层:实际数据读写实现
该层负责实际的数据读写。核心类:ExchangeCodec,此类只是一个接口。实现类是Http1ExchangeCodec和Http2ExchangeCodec
Inteceptor——拦截器层:拦截网络访问,插入拦截逻辑
拦截器层提供了一个类AOP接口,方便用户可以切入到各个层面对网络访问进行拦截并执行相关逻辑。具体下面介绍
Interface——接口层
接口层用于接收用户的网络请求,发起实际的网络请求。以下是几个类的介绍:
OkHttpClient:OkHttpClient是OkHttp框架的客户端,用户可以对其进行各种配置,发起网络请求都是通过OkHttpClient来完成的。每个OkHttpClient内部都维护了自己的任务队列,连接池,Cache,拦截器等,因此在一个项目工程中应该用单例模式。
Request:Request用来描述要发起请求。包括请求url、请求方法、请求头、请求体、缓存控制、是否是https请求。
Call:Call对象描述一个实际的网络请求,用户的每一个网络请求都是一个Call对象,一个Call对象只能被执行一次。Call实际是一个接口,实现类是RealCall类,用来描述同步请求。ReaCall类有一个内部类AsyncCall类,该类继承自Runnable接口,因此每一个Call就是一个线程,执行Call的过程就是执行器executeOn方法的过程。RealCall中保存了OkHttpClient、Request对象,以及标记了是否是WebSocket。
Dispatcher:该类是OkHttpClient的任务队列,其内部维护了一个线程池,当收到一个Call时,它负责在线程池中找到空闲的线程并执行其executeOn方法。
OkHttpClient
//不管如何,最后都创建了Builder对象。
//kotlin中的一个类可以有一个主构造函数以及一个或多个次构造函数。主构造函数是类头的一部分,跟在类名之后。
//若主构造函数没有任何注解或者可见性修饰符,可以省略internal关键字
//主构造函数不能包含任何代码,初始化代码可以放到init关键字作为前缀的初始化块中。主构造函数的参数可以在初始化块中使用。
//若有主构造函数,则每个次构造函数需要委托给主构造函数(直接或间接)
open class OkHttpClient internal constructor(
builder: Builder
) : Cloneable, Call.Factory, WebSocket.Factory {
//...
constructor() : this(Builder())
//...
init {
if (builder.sslSocketFactoryOrNull != null || connectionSpecs.none {
it.isTls }) {
this.sslSocketFactoryOrNull = builder.sslSocketFactoryOrNull
this.certificateChainCleaner = builder.certificateChainCleaner
} else {
val trustManager = Platform.get().platformTrustManager()
Platform.get().configureTrustManager(trustManager)
this.sslSocketFactoryOrNull = newSslSocketFactory(trustManager)
this.certificateChainCleaner = CertificateChainCleaner.get(trustManager)
}
if (sslSocketFactoryOrNull != null) {
Platform.get().configureSslSocketFactory(sslSocketFactoryOrNull)
}
this.certificatePinner = builder.certificatePinner
.withCertificateChainCleaner(certificateChainCleaner)
check(null !in (interceptors as List<Interceptor?>)) {
"Null interceptor: $interceptors"
}
check(null !in (networkInterceptors as List<Interceptor?>)) {
"Null network interceptor: $networkInterceptors"
}
}
}
//Builder有两种创建方式:不带参,带OkHttpClient参数的构造方法。
class Builder constructor() {
//kotlin中有四种可见性修饰符:private、protected、internal和public,默认为public。
//public:随处可见
//private:声明它的文件内可见
//internal:模块(一个IDEA模块;一个Maven项目;一个Gradle源集;一次<kotlinc>Ant任务执行所编译的一套文件)内可见
//protected:不适用于顶层声明。
internal var dispatcher: Dispatcher = Dispatcher()//分发器
internal var connectionPool: ConnectionPool = ConnectionPool()//连接池
internal val interceptors: MutableList<Interceptor> = mutableListOf()//拦截器
internal val networkInterceptors: MutableList<Interceptor> = mutableListOf()//网络拦截器
internal var eventListenerFactory: EventListener.Factory = EventListener.NONE.asFactory()//监听器工厂
internal var retryOnConnectionFailure = true//连接失败时是否重试
internal var authenticator: Authenticator = Authenticator.NONE//本地身份验证
internal var followRedirects = true//HTTP是否重定向
internal var followSslRedirects = true//HTTPS是否重定向
internal var cookieJar: CookieJar = CookieJar.NO_COOKIES//Cookie
internal var cache: Cache? = null//缓存
internal var dns: Dns = Dns.SYSTEM//域名解析
internal var proxy: Proxy? = null//代理
internal var proxySelector: ProxySelector = ProxySelector.getDefault() ?: NullProxySelector()//代理选择器
internal var proxyAuthenticator: Authenticator = Authenticator.NONE//代理身份验证
internal var socketFactory: SocketFactory = SocketFactory.getDefault()//socket工厂
internal var sslSocketFactoryOrNull: SSLSocketFactory? = null//ssl socket工厂,用于HTTPS
internal var connectionSpecs: List<ConnectionSpec> = DEFAULT_CONNECTION_SPECS//传输层版本和连接协议。
internal var protocols: List<Protocol> = DEFAULT_PROTOCOLS//支持的协议
internal var hostnameVerifier: HostnameVerifier = OkHostnameVerifier//用于确定主机名
internal var certificatePinner: CertificatePinner = CertificatePinner.DEFAULT//证书链
internal var certificateChainCleaner: CertificateChainCleaner? = null//证书确认
internal var callTimeout = 0//调用超时时间
internal var connectTimeout = 10_000//连接超时时间
internal var readTimeout = 10_000//读取超时时间
internal var writeTimeout = 10_000//写入超时时间
internal var pingInterval = 0//长连接命令间隔
internal constructor(okHttpClient: OkHttpClient) : this() {
this.dispatcher = okHttpClient.dispatcher
this.connectionPool = okHttpClient.connectionPool
this.interceptors += okHttpClient.interceptors
this.networkInterceptors += okHttpClient.networkInterceptors
this.eventListenerFactory = okHttpClient.eventListenerFactory
this.retryOnConnectionFailure = okHttpClient.retryOnConnectionFailure
this.authenticator = okHttpClient.authenticator
this.followRedirects = okHttpClient.followRedirects
this.followSslRedirects = okHttpClient.followSslRedirects
this.cookieJar = okHttpClient.cookieJar
this.cache = okHttpClient.cache
this.dns = okHttpClient.dns
this.proxy = okHttpClient.proxy
this.proxySelector = okHttpClient.proxySelector
this.proxyAuthenticator = okHttpClient.proxyAuthenticator
this.socketFactory = okHttpClient.socketFactory
this.sslSocketFactoryOrNull = okHttpClient.sslSocketFactoryOrNull
this.connectionSpecs = okHttpClient.connectionSpecs
this.protocols = okHttpClient.protocols
this.hostnameVerifier = okHttpClient.hostnameVerifier
this.certificatePinner = okHttpClient.certificatePinner
this.certificateChainCleaner = okHttpClient.certificateChainCleaner
this.callTimeout = okHttpClient.callTimeoutMillis
this.connectTimeout = okHttpClient.connectTimeoutMillis
this.readTimeout = okHttpClient.readTimeoutMillis
this.writeTimeout = okHttpClient.writeTimeoutMillis
this.pingInterval = okHttpClient.pingIntervalMillis
}
}
Request
Request request = new Request.Builder()
.url("https://wwww.baidu.com")
.get()
.build();
//Request唯一的构造函数
class Request internal constructor(
@get:JvmName("url")