大家好,欢迎来到IT知识分享网。
一、概述
OKHttp是处理网络请求的开源框架,Andorid当前最火热的网络框架,Retrofit的底层也是OKHttp,用于替换HttpUrlConnection和Apache HttpClient(API23 6.0已经移除)。
概况起来说OKHttp是一款优秀HTTP框架,它支持GET和POST请求,支持Http的文件上传和下载,支持加载图片,支持下载文件透明的GZIP压缩,支持响应缓存避免重复的网络请求,支持使用连接池来降低响应延迟的问题。
OKHttp的优点:
1.支持HTTP2/SPDY,这使得对同一个主机发出的所有请求都可以共享相同的套接字连接。
2.如果HTTP2/SPDY不可用OkHttp,会使用连接池来复用连接以提高效率。
3.提供了对 GZIP 的默认支持来降低传输内容的大小
4.提供了对 HTTP 响应的缓存机制,可以避免不必要的网络请求
5.当网络出现问题时,OkHttp会自动重试一个主机的多个 IP 地址
二、基本的使用
2.1、配置工程
(1)首先在清单文件AndroidManifest.xml中添加网络权限
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
(2)在build.gradle文件中添加okhttp依赖:
implementation 'com.squareup.okhttp3:okhttp:4.2.0'
2.2、使用步骤:
(1)创建OkHttpClient实例
(2)通过Builder辅助类构建Request请求对象
(3)OkHttpClient实例回调请求,得到Call对象
(4)同步/异步执行请求,获取Response对象
2.3、方法详解
(1)创建OkHttpClient实例
//方式一:创建OkHttpClient实例,使用默认构造函数,创建默认配置OkHttpClient(官方建议全局只有一个实例) OkHttpClient okHttpClient = new OkHttpClient(); //方式二:通过new OkHttpClient.Builder() 一步步配置一个OkHttpClient实例 OkHttpClient httpClient = new OkHttpClient.Builder().connectTimeout(13, TimeUnit.SECONDS).build(); //方式三:如果要求使用现有的实例,可以通过newBuilder().build()方法进行构造 OkHttpClient client = okHttpClient.newBuilder().build();
使用默认构造函数创建OkHttpClient实例,创建默认配置OkHttpClient(官方建议全局只有一个实例)。也可以通过new OkHttpClient.Builder() 一步步配置一个OkHttpClient实例,如果要求使用现有的实例,可以通过newBuilder().build()方法进行构造。
(2)超时设置
我们在创建OkHttpClient实例的时候也会设置相关的属性,通过.Builder().build()的形式设置,比如超时时间设置:
//1.构建OkHttpClient实例 final OkHttpClient okHttpClient = new OkHttpClient.Builder() .connectTimeout(2, TimeUnit.SECONDS)//链接超时为2秒,单位为秒 .writeTimeout(2, TimeUnit.SECONDS)//写入超时 .readTimeout(2, TimeUnit.SECONDS)//读取超时 .build(); //2.通过Builder辅助类构建请求对象 final Request request = new Request.Builder() .url("http://httpbin.org/delay/10")//URL地址 .build();//构建 //创建线程,在子线程中运行 new Thread(new Runnable() { @Override public void run() { try { //3.通过mOkHttpClient调用请求得到Call final Call call = okHttpClient.newCall(request); //4.执行同步请求,获取响应体Response对象 Response response = call.execute(); Log.e(TAG, "请求(超时)==" + response); } catch (IOException e) { e.printStackTrace(); Log.e(TAG, "请求(超时)==" + e.toString()); } } }).start();
我这里设置了请求10秒后才请求成功,请求超时时间为2秒,读写超时时间为2秒,网络请求是耗时的请求操作,需要另外开子线程运行,抛出了超时异常,测试一下效果:
这里引出了http请求的几个重要的角色,Request是OKHttp的访问请求,Builder是访问辅助类,Response是OKHttp的请求响应
Request(请求):每一个HTTP请求中,都应该包含一个URL,一个GET或POST方法,以及Header和其他参数,还可以包含特定内容类型的数据流。
OKHttp:简单来说,通过OkHttpClient可以发送一个http请求,并且可以读取该请求的响应,它是一个生产Call的工厂。收益于一个共享的响应缓存/线程池/复用的链接等因素,绝大多数应用使用一个OKHttpClient实例,便可满足整个应用的Http请求
(3)HTTP头部的设置和读取
HTTP头部的数据结构是Map<String,List<String>>类型,也就是说,对于每个HTTP的头,可能有多个值,但是大部分的HTTP头只有一个值,只有少部分HTTP头允许多个值,至于name的取值说明请参考:HTTP头部参数说明
OKHTTP的处理方式是:
- header(name, value) 来设置HTTP头的唯一值, 如果请求中已经存在响应的信息那么直接替换掉;
- addHeader(name, value) 来补充新值,如果请求头中已经存在name的name-value, 那么还会继续添加,请求头中便会存在多个name相同value不同的“键值对”;
- header(name, value) 读取唯一值或多个值的最后一个值;
- headers(name) 获取所有值。
Request request = new Request.Builder() .url("https://api.github.com/repos/square/okhttp/issues") .header("User-Agent", "OkHttp Headers.java")//设置唯一值 .addHeader("Server", "application/json; q=0.5")//设置新值 .addHeader("Server", "application/vnd.github.v3+json")//设置新值 .build(); mOkHttpClient.newCall(request).enqueue(new Callback() { @Override public void onFailure(@NotNull Call call, @NotNull IOException e) { Log.e(TAG, "Post请求(HTTP头)异步响应failure==" + e.getMessage()); } @Override public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException { Log.e(TAG, "header:Date==" + response.header("Date")); Log.e(TAG, "header:User-Agent==" + response.header("User-Agent")); Log.e(TAG, "headers:Server==" + response.headers("Server")); Log.e(TAG, "headers:Vary==" + response.headers("Vary")); Log.e(TAG, "Post请求(HTTP头)异步响应Success==" + response.body().string()); } });
在Request请求中通过.Builder().build()的形式设置url(请求地址),也可以设置该请求的头部信息,response的body有很多种输出方法,string()只是其中之一,注意是string()不是toString()。如果是下载文件就是response.body().bytes()。我们来看看打印出来的结果:
(4)GET请求(同步)
new Thread(new Runnable() { @Override public void run() { //通过Builder辅助类构建请求对象 Request request = new Request.Builder() .get()//get请求 .url("https://www.baidu.com")//请求地址 .build();//构建 try { //通过mOkHttpClient调用请求得到Call final Call call = mOkHttpClient.newCall(request); //执行同步请求,获取Response对象 Response response = call.execute(); if (response.isSuccessful()) {//如果请求成功 String string = response.body().string(); Log.e(TAG, "get同步请求success==" + string); //响应体的string()对于小文档来说十分方便高效,但是如果响应体太大(超过1M),应避免使用string()方法, //因为它会把整个文档加载到内存中,对用超多1M的响应body,应该使用流的方式来处理。 //response.body().bytes();//字节数组类型 //response.body().byteStream();//字节流类型 //response.body().charStream();//字符流类型 printHeads(response.headers()); } else { Log.e(TAG, "get同步请求failure=="); } } catch (IOException e) { e.printStackTrace(); } } }).start(); / * 打印请求头信息 * * @param headers 请求头集合 */ private void printHeads(Headers headers) { if (headers == null) return; for (int i = 0; i < headers.size(); i++) { Log.e(TAG, "请求头==" + headers.name(i) + ":" + headers.value(i)); } }
在请求Request中声明为GET请求,设置url请求地址,调用实例mOkHttpClient.new Call(request)调用请求,返回Call,通过同步方法call.execute()同步执行,这里需要开启一个子线程运行。需要手动通过response.isSuccessful()判断请求是否成功,同时通过响应体得到Heander打印了请求头的相关信息,看看得出的结果:
(5)GET请求(异步)
同步和异步不一样的地方是Call对象调用的方法,call.enqueue(callback)实现函数回调成功和失败两方法,异步方法可以不用开启子线程执行。
注意:同步是阻塞式的,串联执行,同步发生在当前线程内。异步是并发式的,会再次创建子线程处理耗时操作。
//通过Builder辅助类构建Request对象,链式编程 Request request = new Request.Builder() .url("https://www.baidu.com") .get() .build(); //异步 mOkHttpClient.newCall(request).enqueue(new Callback() { @Override public void onFailure(@NotNull Call call, @NotNull IOException e) { e.printStackTrace(); Log.e(TAG, "get异步响应失败==" + e.toString()); } @Override public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException { //主线程中更新UI runOnUiThread(new Runnable() { @Override public void run() { //TODO 在主线程中更新UI的操作 } }); Log.e(TAG, "get异步当前线程,线程id==" + Thread.currentThread().getId()); String result = response.body().string(); Log.e(TAG, "get异步响应成功==" + result); printHeads(response.headers()); } }); Log.e(TAG, "主线程,线程id==" + Thread.currentThread().getId());
打印log如下:
onFailure()和onResponse()分别是在请求失败和成功时会调用的方法。这里有个要注意的地方,onFailure()和onResponse()是在异步线程里执行的,所以如果你在Android把更新UI的操作写在这两个方法里面是会报错的,这个时候可以用runOnUiThread这个方法进行更新UI操作。
(6)POST提交String请求(同步)
post请求创建request和get是一样的,只是post请求需要提交一个表单,就是RequestBody。表单的格式有好多种,post请求提交参数需要构建RequestBody对象,post提交的过程需要将提交的内容封装到一个RequestBody中,同时需要设置类型MediaType,MediaType用于描述Http请求和响应体的内容类型,也就是Content-Type,通过.Builder().build()的形式设置URL(请求地址),RequestBody(参数容器)。
//构建RequestBody对象,post提交的过程需要将提交的内容封装到一个RequestBody中 //MediaType用于描述Http请求和响应体的内容类型,也就是Content-Type MediaType mediaType = MediaType.parse("text/plain; charset=utf-8"); RequestBody requestBody = RequestBody.create("提交的内容", mediaType); final Request request = new Request.Builder() .post(requestBody) .url("https://api.github.com/markdown/raw") .build(); new Thread(new Runnable() { @Override public void run() { try { Response response = mOkHttpClient.newCall(request).execute(); if (response.isSuccessful()) { Log.e(TAG, "Post请求String同步响应success==" + response.body().string()); } else { Log.e(TAG, "Post请求String同步响应failure==" + response.body().string()); } } catch (IOException e) { e.printStackTrace(); Log.e(TAG, "Post请求String同步响应failure==" + e.getMessage()); } } }).start();
请求结果如下:
RequestBody的数据格式都要指定Content-Type,常见的有三种:
- application/x-www-form-urlencoded 数据是个普通表单;
- multipart/form-data 数据里有文件;
- application/json 数据是个json。
MediaType mediaType = MediaType.parse("image/png"); RequestBody requestBody = RequestBody.create(xxx.png, mediaType);
改变MediaType 中的内容即可设置不同的内容类型,比如image/png表示便携式网络图形(Portable Network Graphics,PNG)是一种无损压缩的位图图形格式,支持索引、灰度、RGB三种颜色方案以及Alpha通道等特性。
(7)POST提交String请求(异步)
RequestBody requestBody = RequestBody.create("提交内容", MediaType.parse("text/plain; charset=utf-8")); Request request = new Request.Builder() .url("https://api.github.com/markdown/raw") .post(requestBody) .build(); mOkHttpClient.newCall(request).enqueue(new Callback() { @Override public void onFailure(@NotNull Call call, @NotNull IOException e) { Log.e(TAG, "Post请求String异步响应failure==" + e.getMessage()); } @Override public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException { String string = response.body().string(); Log.e(TAG, "Post请求String异步响应success==" + string); } });
异步请求主要是有回调方法,和其他部分和同步差不多,我们来看看效果:
(8)POST提交键值对请求(异步)
请求参数提交键值对需要用到FormBody,FormBody继承自RequestBody,通过.add(“key”, “value”)形式添加:
//提交键值对需要用到FormBody,FormBody继承自RequestBody FormBody formBody = new FormBody.Builder() //添加键值对(通多Key-value的形式添加键值对参数) .add("key", "value") .build(); final Request request = new Request.Builder() .post(formBody) .url("url") .build(); mOkHttpClient.newCall(request).enqueue(new Callback() { @Override public void onFailure(@NotNull Call call, @NotNull IOException e) { Log.e(TAG, "Post请求(键值对)异步响应failure==" + e.getMessage()); } @Override public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException { String result = response.body().string(); Log.e(TAG, "Post请求(键值对)异步响应Success==" + result); } });
(9)POST提交文件请求(异步)
post提交文件,将文件传入RequestBody中即可:
RequestBody requestBody = RequestBody.create(MediaType.parse("text/plain; charset=utf-8"), new File("text.txt")); Request request = new Request.Builder() .post(requestBody) .url("https://api.github.com/markdown/raw") .build(); mOkHttpClient.newCall(request).enqueue(new Callback() { @Override public void onFailure(@NotNull Call call, @NotNull IOException e) { Log.e(TAG, "Post请求(文件)异步响应failure==" + e.getMessage()); } @Override public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException { String result = response.body().string(); Log.e(TAG, "Post请求(文件)异步响应Success==" + result); }
请求效果如下:源码中有例子
(10)POST提交表单请求(异步)
使用FormEncodingBuilder来构建和HTML标签相同效果的请求体,键值对将使用一种HTML兼容形式的URL编码来进行编码
//使用FormEncodingBuilder来构建和HTML标签相同效果的请求体,键值对将使用一种HTML兼容形式的URL编码来进行编码 FormBody formBody = new FormBody.Builder() .add("search", "Jurassic Park") .build(); Request request = new Request.Builder() .url("https://en.wikipedia.org/w/index.php") .post(formBody) .build(); mOkHttpClient.newCall(request).enqueue(new Callback() { @Override public void onFailure(@NotNull Call call, @NotNull IOException e) { Log.e(TAG, "Post请求(表单)异步响应failure==" + e.getMessage()); } @Override public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException { String result = response.body().string(); Log.e(TAG, "Post请求(表单)异步响应Success==" + result); } });
(11)POST提交流请求(同步)
以流的方式Post提交请求体,请求体的内容由流写入产生,这里是流直接写入OKIO的BufferedSink。你的程序可能会使用OutputStream, 你可以使用BufferedSink.outputStream()来获取,这里需要重写RequestBody中的几个方法,将本地数据放入到Http协议的请求体中,然后发送到服务器。
//以流的方式Post提交请求体,请求体的内容由流写入产生,这里是流直接写入OKIO的BufferedSink。 // 你的程序可能会使用OutputStream, 你可以使用BufferedSink.outputStream()来获取 final MediaType mediaType = MediaType.parse("text/x-markdown; charset=utf-8"); //重写RequestBody中的几个方法,将本地数据放入到Http协议的请求体中,然后发送到服务器 final RequestBody requestBody = new RequestBody() { @Nullable @Override public MediaType contentType() { //返回内容类型 return mediaType; } @Override public void writeTo(@NotNull BufferedSink bufferedSink) throws IOException { //输入数据头 bufferedSink.writeUtf8("Numbers\\n"); bufferedSink.writeUtf8("-------\\n"); //构造数据 for (int i = 2; i < 997; i++) { bufferedSink.writeUtf8(String.format(" * %s = %s\n", i, factor(i))); } } }; //构建请求 final Request request = new Request.Builder(). url("https://api.github.com/markdown/raw") .post(requestBody) .build(); //开启线程 new Thread(new Runnable() { @Override public void run() { try { Response response = mOkHttpClient.newCall(request).execute(); if (response.isSuccessful()) { String result = response.body().toString(); Log.e(TAG, "Post请求(流)异步响应Success==" + result); } else { Log.e(TAG, "Post请求(流)异步响应failure==" + response); throw new IOException("Unexpected code " + response); } } catch (IOException e) { e.printStackTrace(); } } }).start(); private String factor(int n) { for (int i = 2; i < n; i++) { int x = n / i; if (x * i == n) { return factor(x) + "x" + i; } } return Integer.toString(n); }
请求结果如下:
(12)POST提交分块请求(异步)
MultipartBuilder可以构建复杂的请求体,与HTML文件上传形式兼容,多块请求头中的每个请求体都是一个亲求体,可以定义自己的请求体,这些请求体可以用来描述这块请求,例如他的Content-Disposition,如果Content-Length和Content-Type可用的话,他们会被自动添加到请求头中。
MediaType mediaType = MediaType.parse("image/png"); String IMGUR_CLIENT_ID = "..."; //构建body MultipartBody multipartBody = new MultipartBody.Builder() .setType(MultipartBody.FORM) .addFormDataPart("title", "Square Logo") .addFormDataPart("image", "logo-square.png", RequestBody.create(mediaType, 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(); //执行请求 mOkHttpClient.newCall(request).enqueue(new Callback() { @Override public void onFailure(@NotNull Call call, @NotNull IOException e) { Log.e(TAG, "Post请求(分块)异步响应failure==" + e.getMessage()); } @Override public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException { String result = response.body().string(); Log.e(TAG, "Post请求(分块)异步响应Success==" + result); } });
请求结果如下:
如果你上传一个文件不是一张图片,但是MediaType.parse(“image/png”)里的”image/png”不知道该填什么,可以参考下这个页面。
源码地址
相关文章:
Retrofit2详解和简单使用
- Retrofit2的介绍和简单使用
OKHttp3的使用和详解
- OKHttp3的用法介绍和解析
OKHttp3源码详解
- 从源码角度解释OKHttp3的关键流程和重要操作
转载:https://blog.csdn.net/m0_/article/details/
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/110406.html








