一、概述

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() {@Overridepublic void run() {try {//3.通过mOkHttpClient调用请求得到Callfinal 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和其他参数,还可以包含特定内容类型的数据流。

Responses(响应):响应则包含一个回复代码(200代表成功,404代表未找到),Header和定制可选的body。
另外可以根据response.code()获取返回的状态码。

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() {@Overridepublic void onFailure(@NotNull Call call, @NotNull IOException e) {Log.e(TAG, "Post请求(HTTP头)异步响应failure==" + e.getMessage());}@Overridepublic 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() {@Overridepublic void run() {//通过Builder辅助类构建请求对象Request request = new Request.Builder().get()//get请求.url("https://www.baidu.com")//请求地址.build();//构建try {//通过mOkHttpClient调用请求得到Callfinal 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() {@Overridepublic void onFailure(@NotNull Call call, @NotNull IOException e) {e.printStackTrace();Log.e(TAG, "get异步响应失败==" + e.toString());}@Overridepublic void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {//主线程中更新UIrunOnUiThread(new Runnable() {@Overridepublic 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-TypeMediaType 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() {@Overridepublic 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() {@Overridepublic void onFailure(@NotNull Call call, @NotNull IOException e) {Log.e(TAG, "Post请求String异步响应failure==" + e.getMessage());}@Overridepublic 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继承自RequestBodyFormBody 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() {@Overridepublic void onFailure(@NotNull Call call, @NotNull IOException e) {Log.e(TAG, "Post请求(键值对)异步响应failure==" + e.getMessage());}@Overridepublic 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() {@Overridepublic void onFailure(@NotNull Call call, @NotNull IOException e) {Log.e(TAG, "Post请求(文件)异步响应failure==" + e.getMessage());}@Overridepublic 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() {@Overridepublic void onFailure(@NotNull Call call, @NotNull IOException e) {Log.e(TAG, "Post请求(表单)异步响应failure==" + e.getMessage());}@Overridepublic 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@Overridepublic MediaType contentType() {//返回内容类型return mediaType;}@Overridepublic 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() {@Overridepublic 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 = "...";//构建bodyMultipartBody 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() {@Overridepublic void onFailure(@NotNull Call call, @NotNull IOException e) {Log.e(TAG, "Post请求(分块)异步响应failure==" + e.getMessage());}@Overridepublic 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_37796683/article/details/101029208

OKHttp3的使用和详解相关推荐

  1. Android OkHttp3简介和使用详解

    一 OKHttp简介 OKHttp是一个处理网络请求的开源项目,Android 当前最火热网络框架,由移动支付Square公司贡献,用于替代HttpUrlConnection和Apache HttpC ...

  2. OkHttp3简介和使用详解

    1 简介 OKHttp是一个当前主流的网络请求的开源框架 Square公司开发,用于替代HttpUrlConnection和Apache HttpClient Android4.4开始,google已 ...

  3. OkHttp3源码详解

    前言:为什么有些人宁愿吃生活的苦也不愿吃学习的苦,大概是因为懒惰吧,学习的苦是需要自己主动去吃的,而生活的苦,你躺着不动它就会来找你了. 一.概述 OKHttp是一个非常优秀的网络请求框架,已经被谷歌 ...

  4. OkHttp3源码详解(五) okhttp连接池复用机制

    1.概述 提高网络性能优化,很重要的一点就是降低延迟和提升响应速度. 通常我们在浏览器中发起请求的时候header部分往往是这样的 keep-alive 就是浏览器和服务端之间保持长连接,这个连接是可 ...

  5. Retrofit2 multpart多文件上传详解

    原文出处:http://www.chenkaihua.com/2016/04/02/retrofit2-upload-multipart-files.html Retrofit2是目前很流行的andr ...

  6. Retrofit 注解参数详解

    转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/121000230 本文出自[赵彦军的博客] 系列文章推荐: Android Flow ...

  7. android OKHttp的基本使用详解

    今天,简单讲讲Android里如何使用OKHttp. Android框架系列: 一.android EventBus的简单使用 二.android Glide简单使用 三.android OKHttp ...

  8. retrofit2使用详解_秒懂Retrofit2之Converter

    >[版权申明]非商业目的注明出处可自由转载 博文地址:https://blog.csdn.net/ShuSheng0007/article/details/89675797 出自:shushe ...

  9. Android OkHttp 全面详解

    Android OkHttp 全面详解 包的导入 基本使用 异步请求 同步请求 build创建 源码跟踪 newCall RealCall.enqueue Dispatcher.enqueue exe ...

最新文章

  1. QPS和并发数,这次给你说清楚
  2. boost::biconnected_components用法的测试程序
  3. PHP之旅4 php 超全局变量
  4. springmvc 结合ajax批量新增
  5. 停止过度设计,开发客户需要的软件
  6. 深入浅出统计学-第一章
  7. spss——主成分分析详解
  8. 80286 与 80386,实模式与保护模式切换编程
  9. android模拟器用户比例,海马玩发布模拟器报告:学生人群与IT从业者占64%
  10. 七牛云异步抓取java_异步第三方资源抓取
  11. 基于翻译模型(Trans系列)的知识表示学习
  12. nodejs生成二维码
  13. 9 mybatis中使用Java8的日期LocalDate、LocalTime、LocalDateTime
  14. 如何查看git账号以及如何切换登录git账号
  15. 1亿数据 redis 内存_Redis10亿数据量只需要100MB内存,为什么这么牛?
  16. html文件无法复制粘贴,无法复制粘贴的解决办法
  17. ARM ADDS和ADCS的区别
  18. 【实用小工具】如何用Python语音合成,以及文字转语音~
  19. 从零开始:Python学习成长路线
  20. [Win10] depends.exe(即Dependency Walker)运行很慢解决办法

热门文章

  1. 设计模式-结构型模式 适配器模式adapter
  2. ArcGIS中如何从中国行政边界矢量数据中获取部分省市区县乡的矢量数据
  3. 隔板法详解(各种方法)
  4. android手机控制电视,[转载]android万能遥控器之二--用手机遥控电视
  5. ubuntu 输入法、邮箱迁移、词典、CHM阅读器
  6. msvcp110.dll丢失修复,哪种修复方法效率高?
  7. ssm毕设项目企业绩效考核系统x73jw(java+VUE+Mybatis+Maven+Mysql+sprnig)
  8. java——JDK组成、跨平台原理
  9. 【案例实践】WRF-Python融合技术:WRF 模式前后处理、自动化运行、数据处理、可视化绘图
  10. springmvc拦截器 绝对路径