用Okhttp已有一段时日,奈何,自我感觉依旧处于多脸懵逼的状态,遂提笔画记一番。
目录
一、添加依赖
二、构建okhttp的实例
三、配置RequestBody请求体
四、配置Request请求
五、配置Call
六、提交(字符串)请求
七、请看网络请求结果,传Log!
八、其实
九、POST提交文件
十、POST提交表单
十一、提交分块请求
十二、完整代码
首先。你得先知道ok的最新版本。也就是去OK的github的老窝看下
https://github.com/square/okhttp
然后,去app/build.gradle 添加相应的依赖
类似这个:
dependencies {
implementation "com.squareup.okhttp3:okhttp:3.14.0"}
嗯。正题,okhttp怎么用?先得弄个实例出来,有三种方式,三种之间的区别已在注释中倾述:
private HttpUtil() {
//第一种.通过配置ok的各种功能,来构建它的实例,就像组装电脑一样
okHttpClient = new OkHttpClient.Builder()
//连接超时
.connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS)
//读取超时
.readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
//写超时
.writeTimeout(WRITE_TIMEOUT, TimeUnit.SECONDS)
//拦截器
.addInterceptor(new LoggingInterceptor()).build();
//第二种.通过现有实例去创建一个实例
okHttpClient=okHttpClient.newBuilder().build();
//第三种.默认构造创建实例
okHttpClient=new OkHttpClient();
//这三种的区别是:稍后更精彩,哈哈哈
}
和上一篇get请求的类似,但除了要配置访问地址外,还要添加点东西,啥呢,它就是你想要上缴给服务器的东西,人称请求体,专业术语是----RequestBody , 只要你按照一定的规则往RequestBody 里丢东西,它就可以帮你合成服务器想要的样子。所以,首先你得搞个RequestBody 出来,然后,将你要给服务器的数据requestContent,以及你的数据类型mediaType是属于文本类型还是其他,RequestBody 的构建代码:
/**
* 1.MediaType对象包含了三种信息:type - 数据基础类型 、subtype - 数据子类型 以及 charset - 字符编码
* (1)text,表示是文本这一大类
* (2)x-markdown是subtype,表示是文本这一大类下的markdown这一小类
* (3)charset=utf-8 则表示采用UTF-8编码
*/
MediaType mediaType=MediaType.parse("text/x-markdown;charset=utf-8");
/**
* 2.要提提交的数据内容
*/
String requestContent="I am kk";
/**
* 3.合成请求体
*/
RequestBody requestBody=RequestBody.create(mediaType,requestContent);
RequestBody 搞出来后,需要配置到 Request 里,同时记得加上要访问的服务器地址,Request 的构建代码:
//要访问的地址
String url="https://api.github.com/markdown/raw";
//构建Request
Request request=new Request.Builder()
.url(url)
//这是刚刚新鲜出炉的请求体
.post(requestBody)
.build();
Call 是个接口,接口里有两方法专门搞网络请求,真正的引用则是它的实现类RealCall ,new Call 只是把RealCall实例给父类接口Call变量,当然,这是后话。看代码解释可以发现。
A call is a request that has been prepared for execution.// Call是一个已准备好执行的请求
构建代码:
Call call=okHttpClient.newCall(request);
Call 的enqueue方法是异步的,所以,你可C以暂且忽略由网络各类情况可能造成的堵塞问题,它内部会处理好的。当然,还有一个execute 方法,但它是同步的,会阻塞主线程,所以,本回打入冷宫。
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
GyLog.d("onFailure getMessage"+e.getMessage());
}
@Override
public void onResponse(Call call, Response response) throws IOException {
GyLog.d(" response.body() "+response.body().string()
+"\n response.code() "+response.code()
+"\n response.message() "+response.message());
Headers headers=response.headers();
for (int i = 0; i < headers.size(); i++) {
GyLog.d(""+headers.name(i)+":"+headers.value(i));
}
}
});
对于下面的一些Http 报文结构,可以参考 HTTP请求/响应报文结构 来进行更深入的了解
2019-04-02 14:03:38.959 31743-31787/com.gc.testhttp D/Kabun:HttpUtil$2.onResponse(L:132): response.body() = I am kk
//响应内容
2019-04-02 14:03:38.959 31743-31787/com.gc.testhttp D/Kabun:HttpUtil$2.onResponse(L:133): response.code() = 200 //响应码
2019-04-02 14:03:38.960 31743-31787/com.gc.testhttp D/Kabun:HttpUtil$2.onResponse(L:134): response.message() = OK //响应信息
2019-04-02 14:03:38.960 31743-31787/com.gc.testhttp D/Kabun:HttpUtil$2.onResponse(L:137): Date:Tue, 02 Apr 2019 06:03:36 GMT //响应时间
2019-04-02 14:03:38.960 31743-31787/com.gc.testhttp D/Kabun:HttpUtil$2.onResponse(L:137): Content-Type:text/html;charset=utf-8 //内容格式;字符编码
2019-04-02 14:03:38.960 31743-31787/com.gc.testhttp D/Kabun:HttpUtil$2.onResponse(L:137): Content-Length:15 //内容长度
2019-04-02 14:03:38.961 31743-31787/com.gc.testhttp D/Kabun:HttpUtil$2.onResponse(L:137): Server:GitHub.com //服务器的类型
2019-04-02 14:03:38.961 31743-31787/com.gc.testhttp D/Kabun:HttpUtil$2.onResponse(L:137): Status:200 OK //状态码
2019-04-02 14:03:38.961 31743-31787/com.gc.testhttp D/Kabun:HttpUtil$2.onResponse(L:137): X-RateLimit-Limit:60 //同一个时间段所允许的请求的最大数目
2019-04-02 14:03:38.961 31743-31787/com.gc.testhttp D/Kabun:HttpUtil$2.onResponse(L:137): X-RateLimit-Remaining:57 //在当前时间段内剩余的请求的数量
2019-04-02 14:03:38.962 31743-31787/com.gc.testhttp D/Kabun:HttpUtil$2.onResponse(L:137): X-RateLimit-Reset:1554188536 //为了得到最大请求数所等待的秒数
2019-04-02 14:03:38.963 31743-31787/com.gc.testhttp D/Kabun:HttpUtil$2.onResponse(L:137): X-CommonMarker-Version:0.18.1
2019-04-02 14:03:38.963 31743-31787/com.gc.testhttp D/Kabun:HttpUtil$2.onResponse(L:137): Access-Control-Expose-Headers:ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type
2019-04-02 14:03:38.963 31743-31787/com.gc.testhttp D/Kabun:HttpUtil$2.onResponse(L:137): Access-Control-Allow-Origin:*
2019-04-02 14:03:38.963 31743-31787/com.gc.testhttp D/Kabun:HttpUtil$2.onResponse(L:137): Strict-Transport-Security:max-age=31536000; includeSubdomains; preload
2019-04-02 14:03:38.964 31743-31787/com.gc.testhttp D/Kabun:HttpUtil$2.onResponse(L:137): X-Frame-Options:deny
2019-04-02 14:03:38.964 31743-31787/com.gc.testhttp D/Kabun:HttpUtil$2.onResponse(L:137): X-Content-Type-Options:nosniff
2019-04-02 14:03:38.965 31743-31787/com.gc.testhttp D/Kabun:HttpUtil$2.onResponse(L:137): X-XSS-Protection:1; mode=block
2019-04-02 14:03:38.965 31743-31787/com.gc.testhttp D/Kabun:HttpUtil$2.onResponse(L:137): Referrer-Policy:origin-when-cross-origin, strict-origin-when-cross-origin
2019-04-02 14:03:38.966 31743-31787/com.gc.testhttp D/Kabun:HttpUtil$2.onResponse(L:137): Content-Security-Policy:default-src 'none'
2019-04-02 14:03:38.966 31743-31787/com.gc.testhttp D/Kabun:HttpUtil$2.onResponse(L:137): Vary:Accept-Encoding
2019-04-02 14:03:38.966 31743-31787/com.gc.testhttp D/Kabun:HttpUtil$2.onResponse(L:137): X-GitHub-Request-Id:A859:0CA7:178F025:1F48D9A:5CA2FB37
以上的提交是属于post请求中的提交 String 字符串的方式,post可以提交的内容还包括以下几种:
找到文件的位置,就能把文件操作对象搞出来,随后,把文件对象放到request里即可。提交文件示例代码:
MediaType mediaType = MediaType.parse("text/x-markdown; charset=utf-8");
OkHttpClient okHttpClient = new OkHttpClient();
//构建指定文件实例
File file = new File("/storage/emulated/0/test.txt");
Request request = new Request.Builder()
.url("https://api.github.com/markdown/raw")
//传入文件实例
.post(RequestBody.create(mediaType, file))
.build();
Call call=okHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
GyLog.d( "onFailure: " + e.getMessage());
}
@Override
public void onResponse(Call call, Response response) throws IOException {
GyLog.d(" response.body() = "+response.body().string());
GyLog.d(" response.code() = "+response.code());
GyLog.d(" response.message() = "+response.message());
}
});
提交文件响应结果 Log:
2019-04-02 16:41:35.111 28136-28181/com.gc.testhttp D/Kabun:HttpUtil$5.onResponse(L:225): response.body() = test 123456
2019-04-02 16:41:35.112 28136-28181/com.gc.testhttp D/Kabun:HttpUtil$5.onResponse(L:226): response.code() = 200
2019-04-02 16:41:35.112 28136-28181/com.gc.testhttp D/Kabun:HttpUtil$5.onResponse(L:227): response.message() = OK
提交表单的话,首先要弄个表单出来,你会发现,少了个MediaType,即之前的内容类型,这个呢是因为FormBody里面已经配置了它,即系封装,关于MediaType的知识可以参考 MediaType
public final class FormBody extends RequestBody {
private static final MediaType CONTENT_TYPE = MediaType.get("application/x-www-form-urlencoded");
表单一般要 key 和 value ,就像 key=“姓名”,value = “小三” ,噢不 ,value = “张三”。然后,你会发现,FormBody实例可以赋值给RequestBody变量,这是因为FormBody是RequestBody的儿子,子类,
OkHttpClient okHttpClient = new OkHttpClient();
//注意:FormBody 类已经内置了静态常量来表示这个body的数据内容类型属于 application/x-www-form-urlencoded
//所以,不用另外设置 MediaType
RequestBody requestBody = new FormBody.Builder()
//类似左边键名,右边键值
.add("search", "Jurassic Park")
.build();
Request request = new Request.Builder()
.url("https://en.wikipedia.org/w/index.php")
.post(requestBody)
.build();
Call call=okHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
GyLog.d( "onFailure: " + e.getMessage());
}
@Override
public void onResponse(Call call, Response response) throws IOException {
GyLog.d(" response.body() = "+response.body().string());
GyLog.d(" response.code() = "+response.code());
GyLog.d(" response.message() = "+response.message());
}
});
请求结果就不贴了,因为返回的结果是一个HTML网页代码。
首先得说下啥是分块,之前的那些都是一个请求块一个请求块那样往服务器丢,这个分块呢,就是,咳咳,就是多几个请求块,堆在一起,一块儿浪。分块的核心东西类是-----MultipartBody,名字就已经透露出一种分块之气,MultipartBody是RequestBody的子类,MultipartBody最爽的地方是,它可以多个请求体一块提交,例如提交多个文件如图片之类的上服务器。
MultipartBody的构建方式有以下几种:
尝试使用上图第二行的方式构建,代码示例:
MultipartBody multipartBody=new MultipartBody.Builder("AaB03x")
//就是设置 MimeType
.setType(MultipartBody.FORM)
.addPart(
Headers.of("Content-Disposition", "form-data; name=\"title\""),
RequestBody.create(null, "Square Logo"))
.addPart(
Headers.of("Content-Disposition", "form-data; name=\"image\""),
RequestBody.create(MEDIA_TYPE_PNG, new File("website/static/logo-square.png")))
.build();
但是,在源码里,归根到底,最后还是调用 addPart(Part part) 的方法,然后形成一个 List
MultipartBody构建完成后,按照以往套路,放进Request 里进行构建:
Request request = new Request.Builder()
.header("Authorization", "Client-ID " + IMGUR_CLIENT_ID)
.url("https://api.imgur.com/3/image")
.post(multipartBody)
.build();
然后再按部就班的进行一系列操作:
Call call=okHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
GyLog.d( "onFailure: " + e.getMessage());
}
@Override
public void onResponse(Call call, Response response) throws IOException {
GyLog.d(" response.body() = "+response.body().string());
GyLog.d(" response.code() = "+response.code());
GyLog.d(" response.message() = "+response.message());
}
});
public class HttpUtil {
private static final long WRITE_TIMEOUT = 30;
private static final long READ_TIMEOUT = 30;
private static final long CONNECT_TIMEOUT = 30;
String url = "http://wwww.baidu.com";
private OkHttpClient okHttpClient;
private HttpUtil() {
//1.通过配置ok的各种功能,来构建它的实例,就像组装电脑一样
okHttpClient = new OkHttpClient.Builder()
//连接超时
.connectTimeout(CONNECT_TIMEOUT, TimeUnit.SECONDS)
//读取超时
.readTimeout(READ_TIMEOUT, TimeUnit.SECONDS)
//写超时
.writeTimeout(WRITE_TIMEOUT, TimeUnit.SECONDS)
//拦截器:
.addInterceptor(new LoggingInterceptor()).build();
// //2.通过现有实例去创建一个实例
// okHttpClient=okHttpClient.newBuilder().build();
// //3.默认构造创建实例
// okHttpClient=new OkHttpClient();
}
public static class Builder {
public static HttpUtil build() {
return new HttpUtil();
}
}
/**
* POST 方式提交String
*/
public void postString(){
/**
* 1.MediaType对象包含了三种信息:type - 数据基础类型 、subtype - 数据子类型 以及 charset - 字符编码
* (1)text,表示是文本这一大类
* (2)x-markdown是subtype,表示是文本这一大类下的markdown这一小类
* (3)charset=utf-8 则表示采用UTF-8编码
*/
MediaType mediaType=MediaType.parse("text/x-markdown;charset=utf-8");
/**
* 2.要提提交的数据内容
*/
String requestContent="I am kk";
/**
* 3.合成请求体
*/
RequestBody requestBody=RequestBody.create(mediaType,requestContent);
//要访问的地址
String url="https://api.github.com/markdown/raw";
Request request=new Request.Builder()
.url(url)
.post(requestBody)
.build();
Call call=okHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
GyLog.d("onFailure getMessage"+e.getMessage());
}
@Override
public void onResponse(Call call, Response response) throws IOException {
GyLog.d(" response.body() = "+response.body().string());
GyLog.d(" response.code() = "+response.code());
GyLog.d(" response.message() = "+response.message());
GyLog.d(" response.protocol() = "+response.protocol());
Headers headers=response.headers();
for (int i = 0; i < headers.size(); i++) {
GyLog.d(""+headers.name(i)+":"+headers.value(i));
}
}
});
}
/**
* 提交流
*/
public void postSink(){
/**
* 1.MediaType对象包含了三种信息:type - 数据基础类型 、subtype - 数据子类型 以及 charset - 字符编码
* (1)text,表示是文本这一大类
* (2)x-markdown是subtype,表示是文本这一大类下的markdown这一小类
* (3)charset=utf-8 则表示采用UTF-8编码
*/
MediaType mediaType=MediaType.parse("text/x-markdown;charset=utf-8");
/**
* 2.要提提交的数据内容
*/
String requestContent="I am kk";
/**
* 3.合成请求体
*/
RequestBody requestBody=new RequestBody() {
@Override
public MediaType contentType() {
return mediaType;
}
@Override
public void writeTo(BufferedSink sink) throws IOException {
sink.writeUtf8(requestContent);
}
};
//要访问的地址
String url="https://api.github.com/markdown/raw";
Request request=new Request.Builder()
.url(url)
.post(requestBody)
.build();
Call call=okHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
GyLog.d("onFailure getMessage"+e.getMessage());
}
@Override
public void onResponse(Call call, Response response) throws IOException {
GyLog.d(" response.body() = "+response.body().string());
GyLog.d(" response.code() = "+response.code());
GyLog.d(" response.message() = "+response.message());
GyLog.d(" response.protocol() = "+response.protocol());
Headers headers=response.headers();
for (int i = 0; i < headers.size(); i++) {
GyLog.d(""+headers.name(i)+":"+headers.value(i));
}
}
});
}
/**
* 提交文件
*/
public void postFile(){
MediaType mediaType = MediaType.parse("text/x-markdown; charset=utf-8");
OkHttpClient okHttpClient = new OkHttpClient();
//构建指定文件实例
File file = new File("/storage/emulated/0/test.txt");
Request request = new Request.Builder()
.url("https://api.github.com/markdown/raw")
//传入文件实例
.post(RequestBody.create(mediaType, file))
.build();
Call call=okHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
GyLog.d( "onFailure: " + e.getMessage());
}
@Override
public void onResponse(Call call, Response response) throws IOException {
GyLog.d(" response.body() = "+response.body().string());
GyLog.d(" response.code() = "+response.code());
GyLog.d(" response.message() = "+response.message());
}
});
}
/**
* 提交表单
*/
public void postFormBody(){
OkHttpClient okHttpClient = new OkHttpClient();
//注意:FormBody 类已经内置了静态常量来表示这个body的数据内容类型属于 application/x-www-form-urlencoded
//所以,不用另外设置 MediaType
RequestBody requestBody = new FormBody.Builder()
//类似左边键名,右边键值
.add("search", "Jurassic Park")
.build();
Request request = new Request.Builder()
.url("https://en.wikipedia.org/w/index.php")
.post(requestBody)
.build();
Call call=okHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
GyLog.d( "onFailure: " + e.getMessage());
}
@Override
public void onResponse(Call call, Response response) throws IOException {
GyLog.d(" response.body() = "+response.body().string());
GyLog.d(" response.code() = "+response.code());
GyLog.d(" response.message() = "+response.message());
}
});
}
/**
* 提交分块
*/
public void postMultiPartBody(){
String IMGUR_CLIENT_ID = "...";
MediaType MEDIA_TYPE_PNG = MediaType.parse("image/png");
OkHttpClient okHttpClient = new OkHttpClient();
MultipartBody multipartBody=new MultipartBody.Builder("AaB03x")
.setType(MultipartBody.FORM)
.addPart(
Headers.of("Content-Disposition", "form-data; name=\"title\""),
RequestBody.create(null, "Square Logo"))
.addPart(
Headers.of("Content-Disposition", "form-data; name=\"image\""),
RequestBody.create(MEDIA_TYPE_PNG, new File("website/static/logo-square.png")))
.build();
Request request = new Request.Builder()
.header("Authorization", "Client-ID " + IMGUR_CLIENT_ID)
.url("https://api.imgur.com/3/image")
.post(multipartBody)
.build();
Call call=okHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
GyLog.d( "onFailure: " + e.getMessage());
}
@Override
public void onResponse(Call call, Response response) throws IOException {
GyLog.d(" response.body() = "+response.body().string());
GyLog.d(" response.code() = "+response.code());
GyLog.d(" response.message() = "+response.message());
}
});
}
}
到目前为止,这是okhttp最基本的POST请求用法,图谱如下:
参考文章: Okhttp3基本使用