OkHttp3使用详解
2017-04-20 16:11
417 查看
引言
最初我们进行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开始
HttpURLConnection替换为
OkHttp,而在
Android 6.0之后的
SDK中
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() { @Override public 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<>(); @Override public void saveFromResponse(HttpUrl url, List<Cookie> cookies) { //注:key是String类型且为url的host部分 cookieStore.put(url.host(), cookies); } @Override public 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()方法会将整个文档加载到内存中。
项目地址 ☞ 传送门
相关文章推荐
- OkHttp在Android中使用详解
- OkHttp使用详解——完全版
- OKHttp使用详解
- Android中okhttp3使用详解
- okhttp实战使用详解
- OkHttp使用方法详解
- Retrofit 2.0使用详解,配合OkHttp、Gson,Android最强网络请求框架
- okhttp的详解及其缓存的使用
- OkHttp的封装和使用详解
- okhttp的使用及详解
- Retrofit 2.0使用详解,配合OkHttp、Gson,Android最强网络请求框架
- OkHttp3使用详解
- OkHttp使用详解
- OkHttp使用详解一
- Okhttp使用详解
- OKHttp使用详解及源码解析
- Android OkHttp 网络请求 使用详解
- Android okhttp3的使用详解
- OkHttp使用详解
- OKHttp使用详解,步骤挺详细的,适合初学者使用!