因为这个系列是属于Android的基础系列,所以并不会涉及到很深的内容层次。作为网络这一个模块而言,我们经历了几个阶段。从最初的HttpClicent(已被废弃)到后面的HttpUrlConnection,当然在HttpUrlConnection的基础上出现了很多的网络框架,诸如:Volley,XUtils等等。但是随着OkHttp的推出,原来的这些网络框架都失去了色彩,OkHttp以其独特的魅力迅速获得了广大开发者的喜爱,今天我们就来看看关于OkHttp的那些事。
OkHttp的基本特性
(1)支持HTTP 2.0,允许所有同一个主机地址的请求共享一个socket链接。
(2)连接池复用减少请求延时,同步避免了资源浪费。
(3)透明的GZIP压缩减少响应数据的大小。
(4)自带缓存机制,可以避免一些重复请求。
(5)多IP使用,当服务断开后,自动切换备用IP地址,重新发起连接。
OkHttp的基本使用
添加依赖
// 网络请求框架
// define a BOM and its version
api(platform("com.squareup.okhttp3:okhttp-bom:4.9.1"))
// define any required OkHttp artifacts without version
api("com.squareup.okhttp3:okhttp")
api("com.squareup.okhttp3:logging-interceptor")
异步get请求
主要的步骤为:
(1)创建OkHttp实例。
(2)构建请求参数 默认为get()请求,可以不写。
(3)构建请求。
(4)发送请求并获取回调(enqueue为异步请求,execute为同步请求(由于会阻塞线程,不建议使用))。
/**
* 普通的Get请求
*/
fun okHttpGet(view: View) {
val url = "http://www.baidu.com"
// 1.创建OkHttp实例
val okHttpClient = OkHttpClient()
// 2.构建请求参数 默认为get()请求,可以不写
val request = Request.Builder().url(url).get().build()
// 3.构建请求
val call = okHttpClient.newCall(request)
// 4.发送请求并获取回调(enqueue为异步请求,execute为同步请求(由于会阻塞线程,不建议使用))
call.enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
Log.i("okHttp", "请求失败${e.printStackTrace()}")
}
override fun onResponse(call: Call, response: Response) {
Log.i(
"okHttp",
response.protocol.toString() + " " + response.code + " " + response.message
)
val headers = response.headers
for (i in 0 until headers.size) {
Log.i("okHttp", headers.name(i) + ":" + headers.value(i))
}
Log.i("okHttp", "onResponse: " + response.body!!.string())
}
})
}
异步post请求
主要的步骤为:
(1)创建OkHttp实例。
(2)构建请求参数 默认为get()请求,可以不写。
(3)构建请求。
(4)发送请求并获取回调(enqueue为异步请求,execute为同步请求(由于会阻塞线程,不建议使用))。
/**
* Post方式提交String
*/
fun okHttpPost(view: View) {
val url = "http://www.5mins-sun.com:8081/user/direct_login"
//1.构建OkHttp实例
val okHttpClient = OkHttpClient()
//2.构建请求参数
val mediaType = "application/json; charset=utf-8".toMediaTypeOrNull()
val map = mutableMapOf<String, Any>()
map["phone"] = "13701659446"
val requestBody = Gson().toJson(map)
val request = Request.Builder().url(url).post(requestBody.toRequestBody(mediaType)).build()
//3.构建请求
val call = okHttpClient.newCall(request)
//4.发送请求
call.enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
Log.i("okHttp", "请求失败${e.printStackTrace()}")
}
override fun onResponse(call: Call, response: Response) {
Log.i(
"okHttp",
response.protocol.toString() + " " + response.code + " " + response.message
)
val headers = response.headers
for (i in 0 until headers.size) {
Log.i("okHttp", headers.name(i) + ":" + headers.value(i))
}
Log.i("okHttp", "onResponse: " + response.body!!.string())
}
})
}
异步post提交流
/**
* Post方式提交流
*/
fun okHttpPostStream(view: View) {
val url = "http://www.5mins-sun.com:8081/user/direct_login"
//1.构建OkHttp实例
val okHttpClient = OkHttpClient()
//2.构建请求参数
val requestBody = object : RequestBody() {
override fun contentType(): MediaType? {
return "application/json; charset=utf-8".toMediaTypeOrNull()
}
override fun writeTo(sink: BufferedSink) {
val map = mutableMapOf<String, Any>()
map["phone"] = "13701659446"
val requestBody = Gson().toJson(map)
sink.writeUtf8(requestBody)
}
}
val request = Request.Builder().url(url).post(requestBody).build()
//3.构建请求
val call = okHttpClient.newCall(request)
//4.发送请求
call.enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
Log.i("okHttp", "请求失败${e.printStackTrace()}")
}
override fun onResponse(call: Call, response: Response) {
Log.i(
"okHttp",
response.protocol.toString() + " " + response.code + " " + response.message
)
val headers = response.headers
for (i in 0 until headers.size) {
Log.i("okHttp", headers.name(i) + ":" + headers.value(i))
}
Log.i("okHttp", "onResponse: " + response.body!!.string())
}
})
}
异步post提交文件
/**
* Post提交文件
*/
fun okHttpPostFile(view: View) {
val url = "http://www.5mins-sun.com:8081/manage/test_save_file_by_stream"
//1.构建OkHttp实例
val okHttpClient = OkHttpClient()
//2.构建请求参数
val file = File(Environment.getExternalStorageDirectory().absolutePath + "/zf.txt")
val mediaType = "application/octet-stream".toMediaTypeOrNull()
val request = Request.Builder().url(url).post(RequestBody.create(mediaType, file)).build()
//3.构建请求
val call = okHttpClient.newCall(request)
//4.发送请求
call.enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
Log.i("okHttp", "请求失败${e.printStackTrace()}")
}
override fun onResponse(call: Call, response: Response) {
Log.i(
"okHttp",
response.protocol.toString() + " " + response.code + " " + response.message
)
val headers = response.headers
for (i in 0 until headers.size) {
Log.i("okHttp", headers.name(i) + ":" + headers.value(i))
}
Log.i("okHttp", "onResponse: " + response.body!!.string())
}
})
}
异步post提交表单
fun okHttpPostForm(view: View) {
val url = "http://www.5mins-sun.com:8081/manage/test_save_file"
//1.构建OkHttp实例
val okHttpClient = OkHttpClient()
//2.构建请求参数
val file = File(Environment.getExternalStorageDirectory().absolutePath + "/zhoufn.txt")
val mediaType = "application/octet-stream".toMediaTypeOrNull()
val fileBody = RequestBody.create(mediaType, file)
val requestBody = MultipartBody.Builder().setType(MultipartBody.FORM)
.addFormDataPart("files", file.name, fileBody).build()
val request = Request.Builder().url(url).post(requestBody).build()
//3.构建请求
val call = okHttpClient.newCall(request)
//4.发送请求
call.enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
Log.i("okHttp", "请求失败${e.printStackTrace()}")
}
override fun onResponse(call: Call, response: Response) {
Log.i(
"okHttp",
response.protocol.toString() + " " + response.code + " " + response.message
)
val headers = response.headers
for (i in 0 until headers.size) {
Log.i("okHttp", headers.name(i) + ":" + headers.value(i))
}
Log.i("okHttp", "onResponse: " + response.body!!.string())
}
})
}
其实仔细观察不难发现,无论是哪种方式交互,其大抵的步骤都是一样的,即:
(1)创建OkHttp实例。
(2)构建请求参数 默认为get()请求,可以不写。
(3)构建请求。
(4)发送请求并获取回调(enqueue为异步请求,execute为同步请求(由于会阻塞线程,不建议使用))。
唯一的区别在于第2步构建请求参数的时候有所不同,当使用get请求方式的时候,无须设置RequestBody,而在使用post请求的时候,必须设置RequestBody,那么,什么是RequestBody呢?
RequestBody
在使用RequestBody的时候,我们一般是通过调用它的onCreate来拿到它的实例,先看下面这张图:
4.jpg
注意看,RequestBody.create()有4个重载的方法,里面的参数都不尽相同,其中第一个参数是MediaType,那么,什么是mediaType呢?
MediaType:表示要传递的数据的MIME类型,即你要传递的是什么类型的东西。比如,需要传递的是json格式的字符串,你就可以设置为application/json; charset=utf-8;再比如,需要传递的是图片文件,可以设置为image/png等等。假设类型设置不对,返回码会返回为415。
接着看,除了MediaType,后面的参数主要有:File文件,String字符串等等。当然,在实际的开发过程中,我们用到最多的也就是这两个。其中File代表的是上传的是文件,String代表的是上传的是字符串。而在上面的例子中,我们使用的是MultipartBody来对文件进行上传,如果我们仅仅只想传递Map集合呢?其实对于这种key-value形式的数据,我们可以按照表单来传递,具体的写法为:
val okHttpClient = OkHttpClient()
val requestBody: RequestBody = FormBody.Builder()
.add("key", "value")
.build()
val request: Request = Builder()
.url("url")
.post(requestBody)
.build()
OkHttp的拦截器
OkHttp的优秀之处还在于其强大的拦截器功能,先上一张图
3.png
有没有觉得这种模式很熟悉,其实这是Java中23种设计模式之一的责任链模式,从上到下链到底,一层嵌套着一层。其中最上层可选的为我们自定义的拦截器,在这里我们可以监听打印自己想要的东西,比如:
(1)给我们所有的网络请求接口添加通用字段
public class HttpHeaderInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request original = chain.request();
int userId = IOFactoryUtil.getIOFactoryUtil().getDefaultHandler().getInt("user_id", 0);
if (userId > 0) {
Request request = original.newBuilder()
.header("userID", String.valueOf(userId))
.build();
return chain.proceed(request);
}
return chain.proceed(original);
}
}
(2)打印我们所有的请求参数
public class HttpLoggingInterceptor implements Interceptor {
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
long startTime = System.currentTimeMillis();
okhttp3.Response response = chain.proceed(chain.request());
long endTime = System.currentTimeMillis();
long duration = endTime - startTime;
okhttp3.MediaType mediaType = response.body().contentType();
String content = response.body().string();
Log.d("NetRequest", "请求地址:| " + request.toString());
printParams(request.body());
Log.d("NetRequest", "请求体返回:| Response:" + content);
Log.d("NetRequest", "----------请求耗时:" + duration + "毫秒----------");
return response.newBuilder().body(okhttp3.ResponseBody.create(mediaType, content)).build();
}
private void printParams(RequestBody body) {
Buffer buffer = new Buffer();
try {
body.writeTo(buffer);
Charset charset = Charset.forName("UTF-8");
MediaType contentType = body.contentType();
if (contentType != null) {
charset = contentType.charset(UTF_8);
}
String params = buffer.readString(charset);
Log.d("NetRequest", "请求参数: | " + params);
} catch (IOException e) {
e.printStackTrace();
}
}
}
上面只是简单的举了两个例子,当然还可以设置更多,具体根据自己的实际需求而定。
除了我们自定义的拦截器,系统还帮我们自动添加了一些拦截器,具体来说:
(1)RetryAndFollowUpInterceptor
处理重定向和错误的拦截器
(2)BridgeInterceptor
添加必要的请求头信息、gzip处理等。
(3)CacheInterceptor
缓存处理
(4)ConnectInterceptor
处理网络连接
(5)CallServerInterceptor
访问服务器
看到这里,真心的不由得佩服OkHttp拦截器的设计之巧妙,通过多层不同作用的拦截器的使用,让各个环节都能各司其职而且职责分明。
注意事项:
(1)OkHttp在全局尽量保持实例,这样所有的请求都可以共享连接池、线程池和配置信息。
(2)OkHttp的默认连接网络时间、读取数据时间、写入数据时间为10s,我们可以手动重新设置时间,具体的方法为:readTimeout、connectTimeout、writeTimeout。
(3)onResponse回调的参数是response,一般情况下,比如我们希望获得返回的字符串,可以通过response.body().string()获取;如果希望获得返回的二进制字节数组,则调用response.body().bytes();如果你想拿到返回的inputStream,则调用response.body().byteStream()。
(4)在okhttp3.Callback的回调方法里面有个参数是Call 这个call可以单独取消相应的请求,随便在onFailure或者onResponse方法内部执行call.cancel()都可以。如果想取消所有的请求,则可以okhttpclient.dispatcher().cancelAll();
网友评论