OkHttp3使用详解
引言
最初我们进行HTTP
请求时使用的是HttpURLConnection
或者HttpClient
,那么这两者都有什么优缺点呢?
HttpClient
是Apache
基金会的一个开源网络库,功能十分强大,API
数量众多,但正是由于庞大的API
数量使得我们很难在不破坏兼容性的情况下对它进行升级和扩展,所以Android
团队在提升和优化HttpClient
方面的工作态度并不积极。官方在Android 2.3
以后就不建议用了,并且在Android 5.0
以后废弃了HttpClient
,在Android 6.0
更是删除了HttpClient
。
HttpURLConnection
是一种多用途、轻量极的HTTP
客户端,提供的API
比较简单,可以容易地去使用和扩展。不过在Android 2.2
版本之前,HttpURLConnection
一直存在着一些令人厌烦的bug
。比如说对一个可读的InputStream
调用close()
方法时,就有可能会导致连接池失效了。那么我们通常的解决办法就是直接禁用掉连接池的功能:
private void disableConnectionReuseIfNecessary() {// 这是一个2.2版本之前的bug if (Integer.parseInt(Build.VERSION.SDK) < Build.VERSION_CODES.FROYO) {System.setProperty("http.keepAlive", "false");}
}
因此一般推荐是在2.2
之前使用HttpClient
,因为其bug
较少。在2.2
之后推荐使用HttpURLConnection
,因为API
简单、体积小、有压缩和缓存机制,并且Android
团队后续会继续优化HttpURLConnection
。
但是上面两个类库和OkHttp
比起来就显得有些不足了,因为OkHttp
不仅具有高效的请求效率,并且提供了很多开箱即用的网络疑难杂症解决方案。
简介
从Android 4.4
开始google
已经开始将源码中的HttpURLConnection
替换为OkHttp
,而在Android 6.0
之后的SDK
中google
更是移除了对于HttpClient
的支持,而现在流行的Retrofit
同样是使用OkHttp
进行再次封装而来的。
OkHttp
是一个快速、高效的网络请求库,它的设计和实现的首要目标便是高效,有如下特性:
- 支持http2,使得对同一个主机发出的所有请求都可以共享相同的socket套接字连接;
- 使用连接池来复用连接以减少延迟、提高效率;
- 支持Gzip压缩响应体,降低传输内容的大小;
- 支持Http缓存,避免重复请求;
- 请求失败时会自动重试主机中的其他IP地址自动重定向;
- 使用Okio来简化数据的访问与存储,提高性能;
使用范围
- 支持
Android 2.3
及其以上版本; - 支持
Java JDK 1.7
以上版本;
依赖
dependencies {compile 'com.squareup.okhttp3:okhttp:3.6.0'compile 'com.squareup.okio:okio:1.11.0'compile 'com.github.franmontiel:PersistentCookieJar:v1.0.1'
}注:如果配置PersistentCookieJar依赖则同时也要在Project的Build.gradle中添加Maven库:allprojects {repositories {jcenter()maven { url "https://jitpack.io" }}
}
权限
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
混淆
#okhttp
-dontwarn okhttp3.**
-keep class okhttp3.**{*;}
#okio
-dontwarn okio.**
-keep class okio.**{*;}
使用
1.Application中初始化OkHttp
/*** @Description 初始化OkHttp*/
private void initOkHttp() {File cache = getExternalCacheDir();int cacheSize = 10 * 1024 * 1024;ClearableCookieJar cookieJar = new PersistentCookieJar(new SetCookieCache(), new SharedPrefsCookiePersistor(mContext));Https.SSLParams sslParams = Https.getSslSocketFactory(null, null, null);OkHttpClient okHttpClient = new OkHttpClient.Builder().connectTimeout(15, TimeUnit.SECONDS)//连接超时(单位:秒).writeTimeout(20, TimeUnit.SECONDS)//写入超时(单位:秒).readTimeout(20, TimeUnit.SECONDS)//读取超时(单位:秒).pingInterval(20, TimeUnit.SECONDS) //websocket轮训间隔(单位:秒).cache(new Cache(cache.getAbsoluteFile(), cacheSize))//设置缓存.cookieJar(cookieJar)//Cookies持久化.hostnameVerifier(new HostnameVerifier() {@Overridepublic boolean verify(String hostname, SSLSession session) {return true;}}).sslSocketFactory(sslParams.sSLSocketFactory, sslParams.trustManager)//https配置.build();OkHttpUtils.initClient(okHttpClient);
}
2.同步数据请求
/*** @param url 请求地址* @param callback 请求回调* @Description GET请求*/
public static void getSync(String url, HttpCallback callback) {Request request = OkHttpRequest.builderRequest(HttpMethodType.GET, url, null, null);OkHttpRequest.doExecute(request, callback);
}/*** @param url 请求地址* @param params 请求参数* @param callback 请求回调* @Description GET请求*/
public static void getSync(String url, Map<String, String> params, HttpCallback callback) {if (params != null && !params.isEmpty()) {url = OkHttpRequest.appendGetParams(url, params);}Request request = OkHttpRequest.builderRequest(HttpMethodType.GET, url, null, null);OkHttpRequest.doExecute(request, callback);
}/*** @param url 请求地址* @param params 请求参数* @param callback 请求回调* @Description POST请求*/
public static void postSync(String url, Map<String, String> params, HttpCallback callback) {Request request = OkHttpRequest.builderRequest(HttpMethodType.POST, url, params, null);OkHttpRequest.doExecute(request, callback);
}
3.异步数据请求
使用enqueue
方法,将call
放入请求队列,然后OkHttp
会在线程池中进行网络访问;只需要在适当的时候(需要操作UI
的时候)发送一个消息给主线程的Handler
(取决于Looper
,使用 Looper.getMainLooper()
创建的Handler
就是主线程Handler
)就可以了。
/*** @param url 请求地址* @param callback 请求回调* @Description GET请求*/
public static void getAsyn(String url, HttpCallback callback) {Request request = OkHttpRequest.builderRequest(HttpMethodType.GET, url, null, null);OkHttpRequest.doEnqueue(request, callback);
}/*** @param url 请求地址* @param params 请求参数* @param callback 请求回调* @Description GET请求*/
public static void getAsyn(String url, Map<String, String> params, HttpCallback callback) {if (params != null && !params.isEmpty()) {url = OkHttpRequest.appendGetParams(url, params);}Request request = OkHttpRequest.builderRequest(HttpMethodType.GET, url, null, null);OkHttpRequest.doEnqueue(request, callback);
}/*** @param url 请求地址* @param params 请求参数* @param callback 请求回调* @Description POST请求*/
public static void postAsyn(String url, Map<String, String> params, HttpCallback callback) {Request request = OkHttpRequest.builderRequest(HttpMethodType.POST, url, params, null);OkHttpRequest.doEnqueue(request, callback);
}/*** @param url 请求地址* @param json json数据格式* @param callback 请求回调* @Description POST提交JSON数据*/
public static void postAync(String url, String json, HttpCallback callback) {Request request = OkHttpRequest.builderRequest(HttpMethodType.POST, url, null, json);OkHttpRequest.doEnqueue(request, callback);
}
4.文件上传
/*** @param url 请求地址* @param file 上传文件* @param callback 请求回调* @Description 单文件上传*/
public static void postAsynFile(String url, File file, HttpCallback callback) {if (!file.exists()) {ToastUtil.showText(UIUtils.getString(R.string.file_does_not_exist));return;}Request request = OkHttpRequest.builderFileRequest(url, file, null, null, null, callback);OkHttpRequest.doEnqueue(request, callback);
}/*** @param url 请求地址* @param pic_key 上传图片关键字(约定pic_key如“upload”作为后台接受多张图片的key)* @param files 上传文件集合* @param params 请求参数* @param callback 请求回调* @Description 多文件上传*/
public static void postAsynFiles(String url, String pic_key, List<File> files, Map<String, String> params, HttpCallback callback) {Request request = OkHttpRequest.builderFileRequest(url, null, pic_key, files, params, callback);OkHttpRequest.doEnqueue(request, callback);
}
5.文件下载
/*** @param url 请求地址* @param destFileDir 目标文件存储的文件夹路径,如:Environment.getExternalStorageDirectory().getAbsolutePath()* @param destFileName 目标文件存储的文件名,如:gson-2.7.jar* @param callback 请求回调* @Description 文件下载*/
public void downloadAsynFile(String url, String destFileDir, String destFileName, HttpCallback callback) {Request request = OkHttpRequest.builderRequest(HttpMethodType.GET, url, null, null);OkHttpRequest.doDownloadEnqueue(request, destFileDir, destFileName, callback);
}/*** @param url 请求地址* @param destFileDir 目标文件存储的文件夹路径* @param destFileName 目标文件存储的文件名* @param params 请求参数* @param callback 请求回调* @Description 文件下载*/
public void downloadAsynFile(String url, String destFileDir, String destFileName, Map<String, String> params, HttpCallback callback) {Request request = OkHttpRequest.builderRequest(HttpMethodType.POST, url, params, null);OkHttpRequest.doDownloadEnqueue(request, destFileDir, destFileName, callback);
}
6.图片显示
/*** @param url 请求地址* @param callback 请求回调* @Description 图片显示*/
public static void displayAsynImage(String url, HttpCallback callback) {Request request = OkHttpRequest.builderRequest(HttpMethodType.GET, url, null, null);OkHttpRequest.doDisplayEnqueue(request, callback);
}
7.流式提交
/*** @param url 请求地址* @param content 提交内容* @param callback 请求回调* @Description 使用流的方式提交POST请求*/
public static void postAsynStream(String url, String content, HttpCallback callback) {Request request = OkHttpRequest.builderStreamRequest(url, content);OkHttpRequest.doEnqueue(request, callback);
}
8.Websocket
/*** @param url 请求地址* @Description WebSocket协议首先会发起http请求,握手成功后,转换协议保持长连接,类似心跳*/
public static void websocket(String url) {Request request = OkHttpRequest.builderRequest(HttpMethodType.GET, url, null, null);OkHttpRequest.doNewWebSocket(request);
}
9.HTTP头部的设置和读取
HTTP
头部的数据结构是Map<String, List<String>>
类型,也就是说对于每个HTTP
头可能有多个值。但是大部分HTTP
头都只有一个值,只有少部分HTTP
头允许多个值。OkHttp
的处理方式是:
- 使用
header(name,value)
来设置HTTP
头的唯一值(如果name
已经存在,将会移除该name
对应的value
,然后将新value
添加进来,即替换掉原来的value
值); - 使用
addHeader(name,value)
来补充新值(即使当前已经存在值了,也只会添加新的value
值,并不会移除或替换原来的值); - 使用
header(name)
读取唯一值或多个值的最后一个值; - 使用
headers(name)
获取所有值;
/*** @param builder Request.Builder* @param name 名称* @param value 值* @Description 添加单个头部信息*/
public static Request.Builder appendHeader(Request.Builder builder, String name, String value) {builder.header(name, value);return builder;
}/*** @param builder Request.Builder* @param headers 头部参数* @Description 添加多个头部信息*/
public static Request.Builder appendHeaders(Request.Builder builder, Map<String, String> headers) {Headers.Builder headerBuilder = new Headers.Builder();if (headers == null || headers.isEmpty()) {return builder;}for (String key : headers.keySet()) {headerBuilder.add(key, headers.get(key));}builder.headers(headerBuilder.build());return builder;
}
10.缓存控制
强制不缓存
Request request = new Request.Builder().cacheControl(new CacheControl.Builder().noCache().build()).url(url).build();
强制缓存
Request request = new Request.Builder().cacheControl(new CacheControl.Builder().onlyIfCached().build()).url(url).build();
Response response = mOkHttpClient.newCall(request).execute();
if (response.code() != 504) {// The resource was cached! Show it.
} else {// The resource was not cached.
}
缓存策略由服务器指定
Request request = new Request.Builder().cacheControl(new CacheControl.Builder().maxAge(0, TimeUnit.SECONDS).build()).url(url).build();
允许使用旧的缓存
Request request = new Request.Builder().cacheControl(new CacheControl.Builder().maxStale(365, TimeUnit.DAYS).build()).url(url).build();
11.Cookies缓存
OkHttpClient mOkHttpClient = new OkHttpClient.Builder().cookieJar(new CookieJar() {private final HashMap<String, List<Cookie>> cookieStore = new HashMap<>();@Overridepublic void saveFromResponse(HttpUrl url, List<Cookie> cookies) {//注:key是String类型且为url的host部分cookieStore.put(url.host(), cookies);}@Overridepublic List<Cookie> loadForRequest(HttpUrl url) {List<Cookie> cookies = cookieStore.get(url.host());return cookies != null ? cookies : new ArrayList<Cookie>();}
}).build();
12.关闭请求
/*** @param tag 请求标签* @Description 取消请求*/
public static void cancelTag(Object tag) {if (tag == null) {return;}synchronized (mOkHttpClient.dispatcher().getClass()) {for (Call call : mOkHttpClient.dispatcher().queuedCalls()) {if (tag.equals(call.request().tag())) {call.cancel();}}for (Call call : mOkHttpClient.dispatcher().runningCalls()) {if (tag.equals(call.request().tag())) {call.cancel();}}}
}
注意事项
Android 4.0
之后要求网络请求必须在工作线程中运行,不允许在主线程中运行。因此如果使用OkHttp3
的同步方法,需要新起工作线程进行调用。- 一般情况下我们希望获得
Response
返回的字符串,可以通过response.body().string()
获取;如果希望获得返回的二进制字节数组,则调用response.body().bytes()
;如果想获取到返回的InputStream
,则调用response.body().byteStream()
。 - 异步请求
enqueue
的回调是子线程,非主线程,所以是不能直接操作UI
界面的。 - 响应体的
string()
方法适用于获取小数据信息,如果返回的数据太大(超过1MB
),建议使用stream()
获取返回的数据,因为string()
方法会将整个文档加载到内存中。
项目地址 ☞ 传送门
OkHttp3使用详解相关推荐
- Android OkHttp3简介和使用详解
一 OKHttp简介 OKHttp是一个处理网络请求的开源项目,Android 当前最火热网络框架,由移动支付Square公司贡献,用于替代HttpUrlConnection和Apache HttpC ...
- OkHttp3源码详解(五) okhttp连接池复用机制
1.概述 提高网络性能优化,很重要的一点就是降低延迟和提升响应速度. 通常我们在浏览器中发起请求的时候header部分往往是这样的 keep-alive 就是浏览器和服务端之间保持长连接,这个连接是可 ...
- OKHttp3的使用和详解
一.概述 OKHttp是处理网络请求的开源框架,Andorid当前最火热的网络框架,Retrofit的底层也是OKHttp,用于替换HttpUrlConnection和Apache HttpClien ...
- OkHttp3源码详解
前言:为什么有些人宁愿吃生活的苦也不愿吃学习的苦,大概是因为懒惰吧,学习的苦是需要自己主动去吃的,而生活的苦,你躺着不动它就会来找你了. 一.概述 OKHttp是一个非常优秀的网络请求框架,已经被谷歌 ...
- Retrofit2 multpart多文件上传详解
原文出处:http://www.chenkaihua.com/2016/04/02/retrofit2-upload-multipart-files.html Retrofit2是目前很流行的andr ...
- Retrofit 注解参数详解
转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/121000230 本文出自[赵彦军的博客] 系列文章推荐: Android Flow ...
- android OKHttp的基本使用详解
今天,简单讲讲Android里如何使用OKHttp. Android框架系列: 一.android EventBus的简单使用 二.android Glide简单使用 三.android OKHttp ...
- retrofit2使用详解_秒懂Retrofit2之Converter
>[版权申明]非商业目的注明出处可自由转载 博文地址:https://blog.csdn.net/ShuSheng0007/article/details/89675797 出自:shushe ...
- Android OkHttp 全面详解
Android OkHttp 全面详解 包的导入 基本使用 异步请求 同步请求 build创建 源码跟踪 newCall RealCall.enqueue Dispatcher.enqueue exe ...
最新文章
- CVPR 2022放榜!录用 2067 篇,接收数量上升24%
- Emotion英语学习
- 【Java】集合框架基础编程练习题
- linux中杀死指定进程,Linux中通过 kill命令 杀死指定进程
- 它们才是真正的数学家 寻找自然界中的数学
- Eclipse--java.lang.OutOfMemoryError: PermGen space
- DXF文件中将多段线凸度转换成圆弧
- iOS学习笔记(十五)——数据库操作(SQLite)
- 吉他扒谱该怎么做?分享一款超好用的扒谱工具!
- 十大算法(动图讲解)❤️超详细!
- 使用arcgis、matlab与R语言GD包进行地理探测器 批量运行,并导出探测结果
- 卫星图像数据下载地址
- Oracle增加字段
- 一键封装app--webapp、HybridApp
- 优秀产品经理必备的“十个证书”+项目管理工具
- ajax前端分页实现
- android 模拟器启动慢,针对Android 模拟器启动慢的问题
- 【算法分析】实验 1. 基于贪心的会议安排问题
- 《Python神经网络编程(Make Your Own Neural Network)》读书笔记
- 图书管理系统 jsp + servlet + mysql (2023)