OKhttp使用详解(一)
参考okhttp官方wiki
https://github.com/square/okhttp/wiki/Calls
okhttp3的设计思路
API部分简介
OkHttpClient.Builder:OkHttpClient可通过Builder采用建造者模式构建,通过Builder可以方便灵活的设置通用参数
- public Builder connectTimeout(long timeout, TimeUnit unit) 为连接设置超时时间
- public Builder readTimeout(long timeout, TimeUnit unit) 设置默认读时间
- public Builder writeTimeout(long timeout, TimeUnit unit) 设置默认写时间
- public Builder proxy(Proxy proxy) 设置代理
- public Builder proxySelector(ProxySelector proxySelector)
- public Builder cookieJar(CookieJar cookieJar) 设置处理cookies的处理者(Sets the handler that can accept cookies from incoming HTTP responses and provides cookies to outgoing HTTP requests)
- void setInternalCache(InternalCache internalCache) 设置内部缓存
- public Builder cache(Cache cache) 设置缓存
- public Builder dns(Dns dns) 设置DNS 服务
- public Builder socketFactory(SocketFactory socketFactory)
- public Builder sslSocketFactory(SSLSocketFactory sslSocketFactory) 配置证书用的
- public Builder sslSocketFactory( SSLSocketFactory sslSocketFactory, X509TrustManager trustManager) 配置证书用的
- public Builder hostnameVerifier(HostnameVerifier hostnameVerifier) Sets the verifier used to confirm that response certificates apply to requested hostnames for HTTPS connections. 应该也是是配置https的
- public Builder certificatePinner(CertificatePinner certificatePinner) 不太懂,没用过
- public Builder authenticator(Authenticator authenticator) Sets the authenticator used to respond to challenges from origin servers.
- public Builder proxyAuthenticator(Authenticator proxyAuthenticator) Sets the authenticator used to respond to challenges from proxy servers.
- public Builder connectionPool(ConnectionPool connectionPool) Sets the connection pool used to recycle HTTP and HTTPS connections. 设置连接池
- public Builder followSslRedirects(boolean followProtocolRedirects) Configure this client to follow redirects from HTTPS to HTTP and from HTTP to HTTPS.不懂
- public Builder followRedirects(boolean followRedirects) Configure this client to follow redirects. If unset, redirects be followed. 不懂
- public Builder retryOnConnectionFailure(boolean retryOnConnectionFailure) 连接失败是否重连
- public Builder dispatcher(Dispatcher dispatcher) Sets the dispatcher used to set policy and execute asynchronous requests. Must not be null.
- public Builder protocols(List<Protocol> protocols) 设置协议
- public Builder connectionSpecs(List<ConnectionSpec> connectionSpecs) 不懂
- public List<Interceptor> interceptors() 应该是获取拦截器列表
- public Builder addInterceptor(Interceptor interceptor) 添加拦截器
- public List<Interceptor> networkInterceptors() 返回网络拦截器
- public Builder addNetworkInterceptor(Interceptor interceptor) 添加网络拦截器
- public OkHttpClient build() 构建OkHttpClient,并为它设置参数
由上面的设计图可知,OkhttpClient是核心,他就像一个浏览器一样,什么工作,比如请求,接受响应都是由它做的,关键就是怎么给他配置http协议的那些配置了,因为配置太多,所以用了Builder帮助配置
翻翻Builder的源码可以知道,即时你没有配置啥参数,Builder也为你默认设置一些参数,比如他默认设置支持 Protocol.HTTP_2, Protocol.SPDY_3, Protocol.HTTP_1_1这三种协议,默认连接,读写超时都是10s,DNS用系统的,默认失败重连。。。。
Dispatcher dispatcher;Proxy proxy;List<Protocol> protocols;List<ConnectionSpec> connectionSpecs;final List<Interceptor> interceptors = new ArrayList<>();final List<Interceptor> networkInterceptors = new ArrayList<>();ProxySelector proxySelector;CookieJar cookieJar;Cache cache;InternalCache internalCache;SocketFactory socketFactory;SSLSocketFactory sslSocketFactory;CertificateChainCleaner certificateChainCleaner;HostnameVerifier hostnameVerifier;CertificatePinner certificatePinner;Authenticator proxyAuthenticator;Authenticator authenticator;ConnectionPool connectionPool;Dns dns;boolean followSslRedirects;boolean followRedirects;boolean retryOnConnectionFailure;int connectTimeout;int readTimeout;int writeTimeout;public Builder() {dispatcher = new Dispatcher();protocols = DEFAULT_PROTOCOLS;connectionSpecs = DEFAULT_CONNECTION_SPECS;proxySelector = ProxySelector.getDefault();cookieJar = CookieJar.NO_COOKIES;socketFactory = SocketFactory.getDefault();hostnameVerifier = OkHostnameVerifier.INSTANCE;certificatePinner = CertificatePinner.DEFAULT;proxyAuthenticator = Authenticator.NONE;authenticator = Authenticator.NONE;connectionPool = new ConnectionPool();dns = Dns.SYSTEM;followSslRedirects = true;followRedirects = true;retryOnConnectionFailure = true;connectTimeout = 10_000;readTimeout = 10_000;writeTimeout = 10_000;}
创建OkhttpHelper
因为只要有请求就必须有okhttpclient参与,一般项目是分模块的,每个模块基本都有网络请求,总不能每个请求都要构造一个okhttpclient去请求吧,所以okhttpclient设计为单例,不用new ,到处都可以用它,方便快捷,代码得到了复用,也节省了内存开销
这里采用内部静态类实现单例
public class OkhttpHelper {public static final int DEFAULTITMEOUT=5;private OkHttpClient okHttpClient;private OkhttpHelper(){OkHttpClient.Builder builder=new OkHttpClient.Builder().connectTimeout(DEFAULTITMEOUT, TimeUnit.SECONDS);okHttpClient=builder.build();}public OkHttpClient getOkHttpClient() {return okHttpClient;}private static class OkhttpHelperHolder{public static OkhttpHelper INSTANCE=new OkhttpHelper();}
}
有Http协议,每个连接必有请求头,是供服务端或客户端读取的,这里Okhttp封装了请求,并且也为它提供builder类,再翻翻看
这些是默认的参数
private HttpUrl url;private String method;private Headers.Builder headers;private RequestBody body;private Object tag;public Builder() {this.method = "GET";this.headers = new Headers.Builder();}private Builder(Request request) {this.url = request.url;this.method = request.method;this.body = request.body;this.tag = request.tag;this.headers = request.headers.newBuilder();}
- public Builder url(HttpUrl url) 设置请求的URl,这个方法相当于浏览器上的地址输入框
- public Builder url(String url) 重载方法
- public Builder url(URL url) 也是重载,所以这里有三种url的表达格式
- public Builder header(String name, String value) 这里是更新header字段的,就是设置键值对,里面的header应该用了个Map来记录的,我还在看
- public Builder addHeader(String name, String value) 添加header字段
- public Builder removeHeader(String name) 删除header字段
- public Builder headers(Headers headers) 看吧,这里直接设置Headers,headers肯定是把所有的字段都包起来了
- public Builder cacheControl(CacheControl cacheControl) 设置缓存控制,它其实也只设置header字段,不信看它的内部实现
public Builder cacheControl(CacheControl cacheControl) {String value = cacheControl.toString();if (value.isEmpty()) return removeHeader("Cache-Control");return header("Cache-Control", value);}
- public Builder method(String method, RequestBody body) 设置请求的方法和请求体,为什么先说这个呢?
- public Builder get() 设置请求方法
其实它的具体实现是调用method()方法
public Builder get() {return method("GET", null);}
怎么使用?
HTTP客户端的工作是接受您的请求并产生响应。这在理论上很简单,但在实践中变得棘手。
Requests 请求
每个HTTP请求包含一个URL,一个请求方法(像GET,POST),和一个头部列表,请求也包含一个body:特殊内容类型的数据流,比如文件,json数据...
Responses 响应
响应使用代码(如200获得成功或404找不到)来应答请求,标题及其自己的可选正文。
重写请求
当你提供给OKhttp一个HTTP请求,他就会按你定义的请求去执行,重写请求上面已给出了源码,直接用builder去set就是,可见okhttp支持很多header,如Content-Length, Transfer-Encoding, User-Agent, Host, Connection, and Content-Type
重写响应
响应是okhttp制造的,我们只是处理响应就行了。
后续请求
当您请求的URL已经移动时,网络服务器将返回一个302类型的响应代码,以指示该文档的新URL。 OkHttp将遵循重定向来检索最终的响应。
重试请求
有时连接失败:池连接过时并断开连接,或者无法访问Web服务器本身。 OkHttp将使用不同的路由重试该请求(如果有)。
Calls 呼叫
通过重写,重定向,后续跟踪和重试,您的简单请求可能会产生许多请求和响应,OkHttp使用Call来建立满足您的请求的任务,但是需要许多中间请求和响应。
有两种执行呼叫的方式
- Synchronous 同步:你的线程被阻塞,直到响应可读。
- Asynchronous 异步:您在任何线程上排队请求,并在响应可读时在另一个线程获取回调
呼叫可以在任何线程被取消,如果呼叫尚未完成,这将呼叫失败!编写请求正文或读取响应主体的代码在其呼叫被取消时将抛出IOException。
Dispatch
对于同步调用,您可以自己创建线程,并负责管理同时发出的请求数。同时连接太多浪费资源;太少的危害延迟。在安卓开发显然不能阻塞主线程,得在另一个线程执行。
对于异步调用,Dispatcher实现最大同时请求的策略。您可以设置每个网络服务器的最大值(默认值为5),总体(默认值为64)。
连接
1.它使用URL并配置了OkHttpClient来创建一个地址。该地址指定我们如何连接到Web服务器。
2.它尝试从连接池检索与该地址的连接.
3.如果在池中没有找到连接,则会选择尝试的路由。这通常意味着进行DNS请求以获取服务器的IP地址。然后,如果需要,可以选择TLS版本和代理服务器。
4.如果它是一个新路由,它通过构建直接套接字连接,TLS隧道(通过HTTP代理的HTTPS)或直接TLS连接进行连接。它需要TLS握手。
5.它发送HTTP请求并读取响应
如果连接有问题,OkHttp将选择另一个路由,然后重试。这允许OkHttp在服务器地址的子集不可达时恢复。
在接收到响应后,连接将返回到池中,以便将来可以重用该请求。连接在一段时间不活动之后从游泳池逐出。
使用实例
由于多处都会使用okhttpClient,所以我把它封装在一个单例类里,便于随处获取
/*** Created by Newtrek on 2017/4/12.*/
public class HttpHelper {public static int DEFAULT_TIMEOUT=5;private OkHttpClient okHttpClient;private HttpHelper(){OkHttpClient.Builder builder=new OkHttpClient.Builder();
// set time outbuilder.connectTimeout(DEFAULT_TIMEOUT, TimeUnit.SECONDS);okHttpClient=builder.build();}public static HttpHelper getInstance(){return HttpHelperHolder.instance;}public OkHttpClient getOkHttpClient() {return okHttpClient;}private static class HttpHelperHolder{private static HttpHelper instance=new HttpHelper();}
}
同步请求 GET
/*** 下载一个文件,并以字符串形式打印响应里的头部,正文*/public static void SynchGet(){
// 获取clientOkHttpClient okHttpClient= HttpHelper.getInstance().getOkHttpClient();
// 构造请求,builder源码已分析,不用设置methos就是默认的get方法Request request=new Request.Builder().url("http://publicobject.com/helloworld.txt").build();try {
// 获取响应Response response=okHttpClient.newCall(request).execute();
// 响应成功,就打印相关内容,其实他是判断code是否属于【200,300)if (response.isSuccessful()){Headers headers=response.headers();for (int i=0;i<headers.size();i++){System.out.println(headers.name(i)+":"+headers.value(i));}System.out.println(response.body().string());}} catch (IOException e) {e.printStackTrace();}}
结果:
异步请求 GET
public static void AsynchGet(){// 获取clientOkHttpClient okHttpClient=HttpHelper.getInstance().getOkHttpClient();// 构造请求Request request=new Request.Builder().url("http://publicobject.com/helloworld.txt").build();
// 请求加入队列okHttpClient.newCall(request).enqueue(new Callback() {@Overridepublic void onFailure(Call call, IOException e) {e.printStackTrace();}@Overridepublic void onResponse(Call call, Response response) throws IOException {if (response.isSuccessful()){Headers headers=response.headers();for (int i=0;i<headers.size();i++){System.out.println(headers.name(i)+":"+headers.value(i));}System.out.println(response.body().string());}}});}
访问HTTP标头
通常,HTTP标头的工作方式与Map <String,String>类似,但是有些标题允许多个值,如Guava的Multimap。
当写请求头时,使用头(名称,值)来设置名称唯一出现的值。
如果存在现有值,则在添加新值之前将删除它们。使用addHeader(name,value)来添加标题,而不会删除已经存在的标题。
private final OkHttpClient client = new OkHttpClient();public void run() throws Exception {Request request = new Request.Builder().url("https://api.github.com/repos/square/okhttp/issues").header("User-Agent", "OkHttp Headers.java").addHeader("Accept", "application/json; q=0.5").addHeader("Accept", "application/vnd.github.v3+json").build();Response response = client.newCall(request).execute();if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);System.out.println("Server: " + response.header("Server"));System.out.println("Date: " + response.header("Date"));System.out.println("Vary: " + response.headers("Vary"));}
post a String
使用HTTP POST将请求体发送到服务
public static void postAString() {MediaType MEDIA_TYPE_MARKDOWN= MediaType.parse("text/x-markdown; charset=utf-8");String postBody = ""+ "Releases\n"+ "--------\n"+ "\n"+ " * _1.0_ May 6, 2013\n"+ " * _1.1_ June 15, 2013\n"+ " * _1.2_ August 11, 2013\n";OkHttpClient okHttpClient = HttpHelper.getInstance().getOkHttpClient();Request request = new Request.Builder().url("https://api.github.com/markdown/raw").post(RequestBody.create(MEDIA_TYPE_MARKDOWN, postBody)).build();try {Response response = okHttpClient.newCall(request).execute();System.out.println( response.toString());} catch (IOException e) {e.printStackTrace();}}
post Streaming
public static final MediaType MEDIA_TYPE_MARKDOWN= MediaType.parse("text/x-markdown; charset=utf-8");private final OkHttpClient client = new OkHttpClient();public void run() throws Exception {RequestBody requestBody = new RequestBody() {@Override public MediaType contentType() {return MEDIA_TYPE_MARKDOWN;}@Override public void writeTo(BufferedSink sink) throws IOException {sink.writeUtf8("Numbers\n");sink.writeUtf8("-------\n");for (int i = 2; i <= 997; i++) {sink.writeUtf8(String.format(" * %s = %s\n", i, factor(i)));}}private String factor(int n) {for (int i = 2; i < n; i++) {int x = n / i;if (x * i == n) return factor(x) + " × " + i;}return Integer.toString(n);}};Request request = new Request.Builder().url("https://api.github.com/markdown/raw").post(requestBody).build();Response response = client.newCall(request).execute();if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);System.out.println(response.body().string());}
post a file
直接 .post(RequestBody.create(MEDIA_TYPE_MARKDOWN, file)),file是File类型
post form parameters (post表单)
RequestBody formBody = new FormBody.Builder()
.add("search", "Jurassic Park")
.build();
Posting a multipart request
MultipartBody.Builder可以构建与HTML文件上传表单兼容的复杂请求体。多部分请求体的每个部分本身就是一个请求体,并且可以定义自己的头。如果存在,这些标题应该描述零件体,例如它的Content-Disposition。如果Content-Length和Content-Type标题可用,则会自动添加。
// Use the imgur image upload API as documented at https://api.imgur.com/endpoints/imageRequestBody requestBody = new MultipartBody.Builder().setType(MultipartBody.FORM).addFormDataPart("title", "Square Logo").addFormDataPart("image", "logo-square.png",RequestBody.create(MEDIA_TYPE_PNG, new File("website/static/logo-square.png"))).build();
用Gson解析json响应
Gist gist = gson.fromJson(response.body().charStream(), Gist.class);for (Map.Entry<String, GistFile> entry : gist.files.entrySet()) {System.out.println(entry.getKey());System.out.println(entry.getValue().content);}
响应缓存
要缓存响应,您需要一个可以读取和写入的缓存目录,并对缓存的大小有限制。缓存目录应该是私有的,不受信任的应用程序不能读取其内容!
响应缓存使用HTTP头进行所有配置,你可以添加请求头,如Cache-Control:max-stale = 3600,OkHttp的缓存将遵守它们。您的网络服务器使用自己的响应头配置响应缓存的时间长短像Cache-Control:max-age = 9600。有缓存标头强制缓存的响应,强制网络响应,或强制使用条件GET验证网络响应
// set cacheint cacheSize=10*1024*1024;//10MBFile cacheFile=new File("E://java/");if (!cacheFile.exists()){cacheFile.mkdir();}Cache cache=new Cache(cacheFile,cacheSize);builder.cache(cache);
public static void ResponseCache(){OkHttpClient okHttpClient = HttpHelper.getInstance().getOkHttpClient();Request request = new Request.Builder().url("http://publicobject.com/helloworld.txt").build();try {Response response1 = okHttpClient.newCall(request).execute();String response1Body = response1.body().string();System.out.println("Response 1 response: " + response1);System.out.println("Response 1 cache response: " + response1.cacheResponse());System.out.println("Response 1 network response: " + response1.networkResponse());} catch (IOException e) {e.printStackTrace();}try {Response response2 = okHttpClient.newCall(request).execute();String response2Body = response2.body().string();System.out.println("Response 2 response: " + response2);System.out.println("Response 2 cache response: " + response2.cacheResponse());System.out.println("Response 2 network response: " + response2.networkResponse());} catch (IOException e) {e.printStackTrace();}}
可以验证到我的E://java/目录里多了四个文件,其实他们就是缓存文件
cancel Call
每次client.newCall()的时候会返回一个Call,所以取消的话就调用call.cancel()就行,如果请求任务未完成,会抛出IO异常
Timeout
.connectTimeout(10, TimeUnit.SECONDS).writeTimeout(10, TimeUnit.SECONDS).readTimeout(30, TimeUnit.SECONDS)
处理身份验证authentication
OkHttp可以自动重试未经身份验证的请求。当答复为401未授权时,将要求身份验证者提供凭证。实现应该构建一个包含缺少凭据的新请求。如果没有凭据可用,返回null以跳过重试。使用Response.challenges()来获取任何认证挑战的方案和领域。在履行基本挑战时,请使用Credentials.basic(用户名,密码)对请求标头进行编码。
client = new OkHttpClient.Builder().authenticator(new Authenticator() {@Override public Request authenticate(Route route, Response response) throws IOException {System.out.println("Authenticating for response: " + response);System.out.println("Challenges: " + response.challenges());String credential = Credentials.basic("jesse", "password1");return response.request().newBuilder().header("Authorization", credential).build();}}).build();
OKhttp使用详解(一)相关推荐
- Android OkHttp 全面详解
Android OkHttp 全面详解 包的导入 基本使用 异步请求 同步请求 build创建 源码跟踪 newCall RealCall.enqueue Dispatcher.enqueue exe ...
- OkHttp用法详解
OkHttp的基本使用 1.首先在工程的app模块下添加okhttp的依赖 implementation 'com.squareup.okhttp3:okhttp:3.12.0' 同步GET请求 同步 ...
- OkHttp使用详解
今天学习了一下OkHttp,在这里做个总结,希望可以帮助到有需要的人,好了,废话不多说,进入正题. 一.OkHttp介绍 OkHttp是一个优秀的网络请求框架,可能一说到网络请求框架,可能很多人都会想 ...
- OKHttp使用详解 (B)
一.巨坑: 通过 OkHttpClient.Builder 生成的 client 会自动把请求的页面加载完,一般的http没什么影响,但如果是请求一个大文件就会报异常.例如 http://XXXXX ...
- Android中okhttp原理详解
目录 1.okhttp工作的大致流程 2.okhttp中的连接 3.Dispatcher和线程池 4.OkHttp中的设计模式 5.OkHttp的优势 6.参考连接 1.okhttp工作的大致流程 1 ...
- android中okhttp原理详解,Android中okhttp原理详解-极度针对面试篇
一.okhttp工做的大体流程 1.一.总体流程 (1).当咱们经过OkhttpClient创立一个Call,并发起同步或者异步请求时: (2).okhttp会经过Dispatcher对咱们全部的Re ...
- Android高工:okhttp原理详解,搞懂了直接去虐面试官
当连接池中有连接时:清理任务由cleanup()方法完成,首先执行清理,并返回下次需要清理的间隔时间,调用调用wait() 方法释放锁.等时间到了以后,再次进行清理,并返回下一次需要清理的时间间隔,再 ...
- okhttp原理详解
一.okhttp工作的大致流程 1.1.整体流程 当我们通过OkhttpClient创建一个Call,并发起同步或异步请求时: okhttp会通过Dispatcher对我们所有的RealCall(Ca ...
- android OKHttp的基本使用详解
今天,简单讲讲Android里如何使用OKHttp. Android框架系列: 一.android EventBus的简单使用 二.android Glide简单使用 三.android OKHttp ...
- OkHttp3源码详解(五) okhttp连接池复用机制
1.概述 提高网络性能优化,很重要的一点就是降低延迟和提升响应速度. 通常我们在浏览器中发起请求的时候header部分往往是这样的 keep-alive 就是浏览器和服务端之间保持长连接,这个连接是可 ...
最新文章
- Python培训教程分享:如何实现pygame的初始化和退出操作?
- 吴琦:视觉-语言导航新进展:Pre-training 与 Sim2Real | 青源 Talk 第 12 期
- 解决Android studio 非法字符的问题
- tkinter 笔记 :主体框架窗口内容
- boost::smart_ptr模块boost/pointer_to_other.hpp 的测试
- 3d slicer如何下载出radiomics_Lumion 10.0 软件下载及安装教程
- c语言如何创建虚拟串口,模拟串口的C语言源程序代码
- mapreduce 算法_MapReduce算法–顺序反转
- 【腾讯Bugly干货分享】Android内存优化总结实践
- DStream算子讲解(一)
- JavaScript从入门到放弃 -(二)继承
- Jmeter之Synchronizing Timer(同步集合点)
- RxJava 的基本使用
- Happy New Year!PR升3啦!
- 操作系统课程设计报告
- 【爬虫】IP代理池的总结、实现与维护,IP代理池小工具(小框架),自建代理ip池
- 我的世界服务器神秘修改节点,我的世界神秘节点指令 | 手游网游页游攻略大全...
- 【武器系统】【2008.06】海军巡航导弹的制导与控制
- directx 9.0c sdk api介绍
- copper铜, bronze青铜和brass黄铜