一、
Android中网络请求是必不可少的。OkHttp是网络框架界的大佬了,很多网络框架都基于OkHttp封装。本章主要讲OKHttp的使用,后面会写到用它封装的其他框架的使用。
需在 build.gradle 里面加入依赖
implementation 'com.squareup.okhttp3:okhttp:4.3.1'
二、
由于网络请求是耗时操作,在UI线程中,请求超过5s会导致应用无响应(ANR),所以Android规定了网络请求只能在子线程里面完成。
同步请求和异步请求
在Http中,请求的方式有两种:GET 和 POST。请求分为同步请求和异步请求。GET 和 POST 的差别在于有无请求体。先来看看同步和异步的区别。(以GET请求为例)
① 同步网络请求:需要在子线程中执行,而处理的UI需要回到UI线程中处理。
private fun getSyncOkHttp() {
//子线程
thread {
//请求参数
val request = Request.Builder()
.url("http://www.baidu.com")
.build()
//客户端
val client = OkHttpClient.Builder()
.build()
//调用execute()为同步请求并返回Response对象
response = client.newCall(request).execute()
//判断时候请求成功
response?.isSuccessful?.apply {
if (this) {
runOnUiThread {
text1.text = response?.message
}
}
}
}
}
同步网络请求的标志:execute()。假如得到结果后要对UI进行操作,需要切换到主线程。
② 异步网络请求:不需要在子线程了,但回调方法是执行在子线程中,所以在更新UI时还要跳转到UI线程中,跟同步请求切换线程一样。
private fun getAsyncOkHttp() {
//请求参数
val request = Request.Builder()
.url("http://www.baidu.com")
.build()
//客户端
val client = OkHttpClient.Builder()
.build()
//调用enqueue()为异步请求并回调到子线程
client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
}
//请求成功
override fun onResponse(call: Call, response: Response) {
//切换到UI线程
runOnUiThread {
text1.text = response.message
}
}
})
}
异步网络请求的标志:enqueue()。假如得到结果后要对UI进行操作,需要切换到主线程。
首先,我们要建立一个客户端client,用于发送请求参数request,再把所需要的数据添加到request,如url、请求头header...,然后用client调用newCall方法,把请求参数传进去,最后调用execute()或者enqueue()得到回调结果并做一系列处理。
再来看下POST请求(以异步请求为例):
private fun postAsyncOkHttp() {
//请求体(传输表单类型)
val mBody = FormBody.Builder()
.build()
//请求参数
val request = Request.Builder()
.url("http://www.baidu.com")
.post(mBody)
.build()
//客户端
val client = OkHttpClient.Builder()
.build()
client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
}
override fun onResponse(call: Call, response: Response) {
runOnUiThread {
text1.text = response.message
}
}
})
}
与GET方式不同的是,要在请求参数里面加个请求体,request里面用post()接受一个RequestBody对象,RequestBody是一个抽象类,分别有FormBody和MultipartBody两个子类,上面这个例子使用的是FormBody,用于传输表单类型的参数。MultipartBody则支持多类型的参数传递,例如:在传输表单类型的参数的同时,还是可以传输文件。创建一个MultipartBody对象再调用post()方法就OK了。
在Request.Builder中如果调用post方法则把请求方式设为POST,否则默认为GET。与GET相比,POST需要有请求体接收,其他都与GET相同。
请求头
刚才有提到请求头Header,它是通过Request.Builder对象的相关方法来维护的,如下:
① headers(Headers headers)
② header(String name, String value)
③ addHeader(String name, String value)
④ removeHeader(String name)
addHeader和removeHeader方法,分别是添加和移除header信息。header(String name, String value)这是会重新设置指定name的header信息。headers(Headers headers)则是会移除掉原有的所有header信息,将参数headers的header信息添加到请求中。
注:header和addHeader的区别:指定name如果存在的话,那么header会重新设定name的value,如果不存在,header和addHeader一样是添加头信息。
//请求参数
val request = Request.Builder()
.addHeader("Charset","UTF-8")
.url("http://www.baidu.com")
.build()
超时时间
在网络请求的客户端中,我们可以设定请求的超时时间,包括连接,调用和读写时间,在OKHttpClient.Builder里面设定的,假如不设定,将使用默认的超时设置。
val timeOut:Long = 15 //超时时间
//客户端
val client = OkHttpClient.Builder()
.connectTimeout(timeOut, TimeUnit.SECONDS)
.readTimeout(timeOut, TimeUnit.SECONDS)
.writeTimeout(timeOut, TimeUnit.SECONDS)
.build()
其中第一个参数是超时时间,第二个参数是超时时间单位。
拦截器(Interceptor)
拦截器在OKHttp里面的作用还是非常强大的,能够监视请求,例如在拦截器中设定请求发送和响应的一些信息输出操作,还可以重写请求信息,例如修改url、添加请求头header...
拦截器还分为两种:
① 网络拦截器
② 应用拦截器
首先创建个拦截器继承Interceptor接口,复写Intercept方法。其中,chain可以去到你想要的数据,例如chain.request()能去到当前的请求体,chain.url能去到当前的url...就是说,请求经过一系列操作后来到拦截器,拦截器能得到请求的东西并能对其进行操作,最后调用chain.proceed()方法把响应结果返回出去。
private fun getAsyncOkHttp() {
//请求参数
val request = Request.Builder()
.url("http://www.baidu.com")
.build()
//客户端
val client = OkHttpClient.Builder()
.addInterceptor(MyInterceptor())
.build()
//调用enqueue()为异步请求并回调到子线程
client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
}
//请求成功
override fun onResponse(call: Call, response: Response) {
//切换到UI线程
runOnUiThread {
Log.e("Interceptor",response.url.toString())
}
}
})
}
private class MyInterceptor:Interceptor{
override fun intercept(chain: Interceptor.Chain): Response {
val oldRequest = chain.request()
val newRequest = oldRequest.newBuilder()
.url("http://www.google.com")
.build()
return chain.proceed(newRequest)
}
}
在原来的client中添加了拦截器,拦截器中先去到原来的请求体,得到oldRequest,再调用newBuilder()方法对其进行重构,可以加一些请求头信息,或者更改url...之后把定义好的newRequest通过chain.proceed()得到新的response返回出去。其中,chain.proceed()是真正发生网络请求的地方。运行结果打印出之后设定的url:"http://www.google.com"
注:拦截器可以设置多个,并且拦截器的调用是有顺序的。例如进去A拦截器出来后,再进入B拦截器时,请求体与刚进去A拦截器是有所不同的。
之前还写过一篇用拦截器更改baseUrl的操作的文章,感兴趣的同学可以看看。
三、
OKHttp的使用基本就这些了,如果想更加深入可以看看源码,学习时请求顺序不要乱就好了,记得请求头、超时时间设置及拦截器分别再那些地方加上去,初学者很容易混淆的,包括我 - -!
后面会继续写OKHttp+Retrofit的配合使用。文章有写的不好的地方麻烦大伙提出来,感谢~