概述
Okhttp:
1.概述
OkHttp是一个现代,快速,高效的Http client,支持HTTP/2以及SPDY(HTTP/2是基于spdy的二者用于最小网络化延迟,提升网络速度),主要用于以下几点:
1.OkHttp是HTTP客户端的有效默认: (对HTTP/2协议相同)
1共享同一个Socket(插座)来处理同一个服务器的所有请求
2如果SPDY不可用,则通过连接池来减少请求延时
3无缝的支持GZIP压缩来减少数据流量
4缓存响应数据来减少重复的网络请求
2.Okhttpget请求网络
1.get的异步请求
//创建okhttp对象
okHttpClient = new OkHttpClient();
//创建request对象
request = new Request.Builder().url(image_path).build();
okHttpClient.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
@Override
public void onResponse(Call call, Response response) throws IOException {
if(response.isSuccessful()){
Message message = handler.obtainMessage();
message.what = 1;
message.obj = response.body().bytes();
handler.sendMessage(message);
}else {
handler.sendEmptyMessage(0);
}
}
});
2.get的同步请求
t的异步请求
//创建okhttp对象
okHttpClient = new OkHttpClient();
//创建request对象
request = new Request.Builder().url(image_path).build();
new Thread(new Runnable() {
@Override
public void run() {
try {
Response response = okHttpClient.newCall(request).execute();
Call call = okHttpClient.newCall(request);
if(response.isSuccessful()){
Message message = handler.obtainMessage();
message.what = 2;
message.obj = response.body().bytes();
handler.sendMessage(message);
}else {
Toast.makeText(MainActivity.this,"false",Toast.LENGTH_SHORT).show();
handler.sendEmptyMessage(3);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
3.post的异步请求
okHttpClient = new OkHttpClient();
url = "http://172.31.5.52:8080/a.txt";
FormBody formBody = new FormBody.Builder().add("1","1").add("2","2").build();
request = new Request.Builder().url(url).post(formBody).build();
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
handler.sendEmptyMessage(0);
Log.e("123","false");
}
@Override
public void onResponse(Call call, Response response) throws IOException {
if (response.isSuccessful()){
String s = response.body().string();
Message message = handler.obtainMessage();
message.obj = s;
message.what = 1;
handler.sendMessage(message);
Log.e("123","true");
}else {
Log.e("123","false");
}
}
});
3.okhttp的源码分析(okhttp两种请求方式,同步(execute)和异步(enqueue))具体发送请求之前的操作
同步:提交请求->等待服务器处理->处理完毕返回。这个期间客户端浏览器不能干任何事。
异步: 请求通过事件触发->服务器处理(这是浏览器仍然可以作其他事情)->处理完毕。
1.okhttpclint同步和异步请求只是接口不一样,最后都要走到call接口里面
2.同步请求为execute方法,此方式为同步请求方式:其中利用到了okhttpclint的一个Dispatcher分配器把请求分配到executeCalls队列中,传入的参数为call,在操作完成的时候会自动在队列中remove掉;
this.client.getDispatcher().executed(this);
Response result = this.getResponseWithInterceptorChain(false);
3.异步请求为enqueue方法,此方式为异步请求方式:同样利用到了okhttpclint的一个Dispatcher分配器把请求分配到enqueueAsyncCall队列中,传入的参数为AsyncCall。即异步和同步在分配队列时传入的参数不同。而且异步请求是有条件限制的,默认最多64个请求,而同一个请求默认最多同时存在5个,当然这些是可以根据分发器提供的设置方法进行更改的。
-->AsyncCall>getResponseWithInterceptorChain
this.client.getDispatcher().enqueue(new Call.AsyncCall(responseCallback, forWebSocket, null));
4.在请求执行的过程中会执行到拦截链方法getResponseWithInterceptorChain,从而会执行到proceed方法
private Response getResponseWithInterceptorChain(boolean forWebSocket) throws IOException {
Call.ApplicationInterceptorChain chain = new Call.ApplicationInterceptorChain(0, this.originalRequest, forWebSocket);
return chain.proceed(this.originalRequest);
}
5.在请求执行的过程中会执行到proceed方法,此方法就是判断是否有拦截器需要执行,有则先执行拦截器内容然后执行请求操作,否则直接执行请求而操作。
public Response proceed(Request request) throws IOException {
//判断还有拦截器需要执行不,生成新的ApplicationInterceptorChain并调用它的intercept去执行用户定义的操作
if(this.index < Call.this.client.interceptors().size()) {
Call.ApplicationInterceptorChain chain = Call.this.new ApplicationInterceptorChain(this.index + 1, request, this.forWebSocket);
return ((Interceptor)Call.this.client.interceptors().get(this.index)).intercept(chain)
}
6.当执行完拦截器操作内容之后会执行请求操作:getresponse方法
okhttpclient会新建一个HttpEngine去处理具体的操作,如果请求为post会先处理一些头部信息(客户端向服务器发送一个请求,请求头包含请求的方法、URI、协议版本、以及包含请求修饰符、客户信息和内容的类似于MIME的消息结构。)。通过sendRequest发送具体请求操作-->见sendrequest方法,readResponse(-->见readresponse方法)对服务器的答复做一定处理然后通过engine.getresponse得到服务器的响应。关闭相应的连接池,关闭回收socket。
其中的具体响应方法: this.engine = new HttpEngine(this.client, request, false, false, forWebSocket, (Connection)null, (RouteSelector)null, (RetryableSink)null, (Response)null);
第四个参数为:callerWritesRequestBody为false,最后一个参数为:requestBodyOut(最后响应的内容)为null-->下文会用到
4.okhttp的源码分析具体发送请求时的操作
1.从HttpEngine的sendRequest说起,sendRequest方法会针对请求策略去判断是否走网络,若不走网络,networkResponse为空,直接读取缓存,去生成userReponse,否则就进入网络请求状态并且进行建立连接操作,
2. sendRequest方法源码简解
(1)InternalCache responseCache = Internal.instance.internalCache(this.client);//读取用户设置缓存的内容,缓存中可能有之前相同的请求得到的 response
(2)Response cacheCandidate = responseCache != null?responseCache.get(request):null;//读取与之前相同的response的缓存,当然此缓存可能为空。
(3)this.cacheStrategy = (new Factory(now, request, cacheCandidate)).get();//确定缓存策略的代码,是根据用户的请求和上述读取的缓存内容确定的缓存策略,得到缓存策略会确定 只要缓存还是需要再次处理你的网络请求。
(4)if(this.networkRequest != null) {//网络请求不为空,处理网络请求
if(this.connection == null) {
//建立连接
this.connect();//此方法较为重要,下文简解。-->connect
}
}
(5)this.transport = Internal.instance.newTransport(this.connection, this);//建立连接之后,得到httptransport(Http的请求)网络请求承载者
(6)后续会根据相应的条件判断走网络需求的执行,其中会利用到httpConnection的sink写入请求头部,然后处理网络请求得到requestBodyOut,即最后响应的内容,中间判断程序过多不再赘述。
(7)当不走网络请求的时候会将缓存数据组装成response
this.userResponse = this.cacheResponse.newBuilder().request(this.userRequest).priorResponse(stripBody(this.priorResponse)).cacheResponse(stripBody(this.cacheResponse)).build();
(8)最后解压请求结果
this.userResponse = this.unzip(this.userResponse);
5.okhttp的源码分析读取response时的操作
public void readResponse() throws IOException {
(1)if(){}
else if(!this.callerWritesRequestBody) {//上方传入的参数callerWritesRequestBody为false,所以肯定会执行到此if判断
//利用拦截器方式对答复进行操作处理,后续会根据得到的networkresponse进行操作组装,得到该请求的response,最后解压该请求结果得到最终的userresponse
networkResponse = (new HttpEngine.NetworkInterceptorChain(0, this.networkRequest)).proceed(this.networkRequest);
}
}
(2)上方代码在进行拦截链操作时执行到了proceed方法,同样为拦截器会先执行完所有的拦截器操作在执行相应的后续请求操作,比如写入请求体,请求头部之类的东西,最后会将之前写入的数据flush给socket并读取服务器答复
public Response proceed(Request request){
将之前写入的数据flush给socket并读取服务器答复
Response var6 = HttpEngine.this.readNetworkResponse();-->(5)flush操作
}
(3)在写入请求头部的时候会调用到一个writeRequest的方法
public void writeRequestHeaders(Request request){
//组装请求的信息,比如url,请求方式,请求协议
String requestLine = RequestLine.get(request, this.httpEngine.getConnection().getRoute().getProxy().type(), this.httpEngine.getConnection().getProtocol());
//将请求头和请求体写入socket,重要-->socket方法
this.httpConnection.writeRequest(request.headers(), requestLine);
}
(4)public void writeRequest(Headers headers, String requestLine){//这段代码中可以看到许多sink,也就是得到了socket的写入流,然后会进行flush操作
if(this.state != 0) {
throw new IllegalStateException("state: " + this.state);
} else {
this.sink.writeUtf8(requestLine).writeUtf8("rn");
int i = 0;
for(int size = headers.size(); i < size; ++i) {
this.sink.writeUtf8(headers.name(i)).writeUtf8(": ").writeUtf8(headers.value(i)).writeUtf8("rn");
}
this.sink.writeUtf8("rn");
this.state = 1;
}
}
(5)flush操作
private Response readNetworkResponse() throws IOException {
//执行flush操作
this.transport.finishRequest(); //其中finishRequest()进行了flush操作-->(6)
//等待服务器相应并读取服务器返回信息组装成我们需要的response
Response networkResponse = this.transport.readResponseHeaders().request(this.networkRequest).handshake(this.connection.getHandshake()).header(OkHeaders.SENT_MILLIS, Long.toString(this.sentRequestMillis)).header(OkHeaders.RECEIVED_MILLIS, Long.toString(System.currentTimeMillis())).build();
if(!this.forWebSocket) {
//组装整理response的body
networkResponse = networkResponse.newBuilder().body(this.transport.openResponseBody(networkResponse)).build();-->(7)
}
Internal.instance.setProtocol(this.connection, networkResponse.protocol());
return networkResponse;
}
(6)public void finishRequest() throws IOException {
this.httpConnection.flush();
}
public void flush() throws IOException {
this.sink.flush();
}
(7)public ResponseBody openResponseBody(Response response) throws IOException {
Source source = this.getTransferStream(response);
//最终返回一个输入流
return new RealResponseBody(response.headers(), Okio.buffer(source));
}
(8)最最后3中getresponse将得到的userresponse,然后进行后续的善后操作
拦截器
在执行同步和异步请求的过程中都会执行到一个okhttp里面的拦截器功能(官方文档对拦截器的解释为:拦截器是可以用来转换,重试,重写请求的机制)。拦截器可以自己编写添加到okhttpclient中,拦截器会执行相应的操作比如请求头部等等。
class LoggingInterceptor implements Interceptor{}
OkHttpClient client = new OkHttpClient();
client.interceptors().add(new LoggingInterceptor());
AsyncCall, AsyncCall本身是一个call的内部类,并且继承了Runnable这个类里面重写的run方法核心为相应的execute提交方法。
public final void run() {
String oldName = Thread.currentThread().getName();
Thread.currentThread().setName(this.name);
try {
this.execute();-->下文execute
} finally {
Thread.currentThread().setName(oldName);
}
}
在execute中会执行
Response e = Call.this.getResponseWithInterceptorChain(this.forWebSocket);
connect方法
1.private void connect(){//建立连接的方法
//建立一个Address,用来记录web服务器,以及要连接服务器需要的一些静态配置比如端口号,网络协议等
this.address = createAddress(this.client, this.networkRequest);
//得到路由选择器,用于记录连接服务器的一些动态配置,比如查询DNS的ip,代理服务器,TLS协议版本
this.routeSelector = RouteSelector.get(this.address, this.networkRequest, this.client);
//得到httpConnection和route
this.connection = this.nextConnection();
this.route = this.connection.getRoute();
private Connection nextConnection() throws RouteException {//内部方法将connection设置到okhttpclient
Connection connection = this.createNextConnection();
//将connection设置到okhttpclient
Internal.instance.connectAndSetOwner(this.client, connection, this, this.networkRequest);
return connection;
}
private Connection createNextConnection(){
//在createNextConnection里先去线程池里找是否有之前请求过该Address且还在存活时间里的connection否则新建一个,进行连接
}
}
2.void connect(){//连接建立好判断与服务器连接成功与否的方法
(1)SocketConnector socketConnector = new SocketConnector(this, this.pool);//想要连接网络需要先建立一个socket连接器,
(2)ConnectedSocket connectedSocket = socketConnector.connectCleartext(connectTimeout, readTimeout, this.route);
//建立socket并进行 socket.connect发起对服务器的连接
(3)//然后我们会得到协议protocol为Protocol.HTTP_1_1
this.protocol = connectedSocket.alpnProtocol == null?Protocol.HTTP_1_1:connectedSocket.alpnProtocol;
(4)this.connected = true;//最后连接成功
}
3.public HttpConnection(ConnectionPool pool, Connection connection, Socket socket){得到socket后建立输入输出流最重要
his.pool = pool;
this.connection = connection;
this.socket = socket;
this.source = Okio.buffer(Okio.source(socket));//source(socket的输入流用来读)
this.sink = Okio.buffer(Okio.sink(socket));//sink(socket的输出流用来写)
}
最后
以上就是坚定咖啡为你收集整理的文章标题Okhttp:的全部内容,希望文章能够帮你解决文章标题Okhttp:所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复