博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
OKHttp开源框架学习一:同步请求总结
阅读量:618 次
发布时间:2019-03-11

本文共 6031 字,大约阅读时间需要 20 分钟。

目录


系列文章:

版本:

compile 'com.squareup.okhttp3:okhttp:3.9.0' //okttp依赖

参考文章: 

OkHttp同步方法总结:

  • 1、创建OkHttpClient和Request对象
  • 2、将Request封装成Call对象
  • 3、调用Call的execute()发送同步请求

Okhttp同步需要注意:发送请求后,就会进入阻塞状态,直到收到响应。

OkHttpClient mClient = new OkHttpClient.Builder().build();        Request request = new Request.Builder().url("http://www.baidu.com").get().build();        //可以把call看做连接Request和Response的桥梁        Call call = mClient.newCall(request);        try {            Response response = call.execute();            LogUtils.json(response.body().string());        } catch (IOException e) {            e.printStackTrace();        }

OkHttp异步方法总结:

  • 1、创建OkHttpClient和Request对象
  • 2、将Request封装成Call对象
  • 3、调用Call的enqueue()方法进行异步请求

注意:onResponse()和onFailure()两个回调方法是在工作线程即子线程进行的

OkHttpClient mClient = new OkHttpClient.Builder().build();        Request request = new Request.Builder().url("http://www.baidu.com").get().build();        //可以把call看做连接Request和Response的桥梁        Call call = mClient.newCall(request);        call.enqueue(new Callback() {            @Override            public void onFailure(Call call, IOException e) {            }            @Override            public void onResponse(Call call, Response response) throws IOException {                LogUtils.json(response.body().string());                runOnUiThread(new Runnable() {                    @Override                    public void run() {                        tvShow.setText("eeeeee");                    }                });            }        });

同步和异步区别:

  • 1、发起请求调用的方法不同
  • 2、阻塞线程与否

同步请求流程分析:

下面我们来一步步分析,首先,就是

第一步,创建一个OkHttpClient对象

OkHttpClient mClient = new OkHttpClient.Builder().build();

我们来查看OkHttpClient的内部Builder类的构造方法:

public Builder() {      dispatcher = new Dispatcher();      protocols = DEFAULT_PROTOCOLS;      connectionSpecs = DEFAULT_CONNECTION_SPECS;      eventListenerFactory = EventListener.factory(EventListener.NONE);      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;      pingInterval = 0;    }

初始化了一堆东西,比较重要的就是Dispatcher、ConnectionPool的初始化。Dispatcher和异步请求有关,我们先不讲,我们重要讲一下ConnectionPool。

ConnectionPool,顾名思义,就是连接池的意思,我们可以把客户端与服务端的链接抽象为一个个Connection,而每一个Connection都会被放在ConnectionPool中统一管理。

ConnectionPool有两个作用:

  • 作用一,当你请求的url是相同的时候,就可以复用这个Connection;
  • 作用二,ConnectionPool可以设置哪些Connection保持打开,哪些Connection可以保持复用;

第二步,创建携带请求信息的Request对象

Request request = new Request.Builder().url("http://www.baidu.com").get().build();

可以看出,这也是一个build构造模式,先看内部类Builder

Builder(Request request) {      this.url = request.url;      this.method = request.method;      this.body = request.body;      this.tag = request.tag;      this.headers = request.headers.newBuilder();    }

通过Builder来设置请求地址url、请求方法method、头部信息headers等信息。

Request(Builder builder) {    this.url = builder.url;    this.method = builder.method;    this.headers = builder.headers.build();    this.body = builder.body;    this.tag = builder.tag != null ? builder.tag : this;  }

然后再把Builder的这些参数传递给Request

第三步,创建Call对象

Call是一个接口类,具体实现是在RealCall类中

static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {    // Safely publish the Call instance to the EventListener.    RealCall call = new RealCall(client, originalRequest, forWebSocket);    call.eventListener = client.eventListenerFactory().create(call);    return call;  }

再接着往下看:

private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {    this.client = client;    this.originalRequest = originalRequest;    this.forWebSocket = forWebSocket;    this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);  }
RetryAndFollowUpInterceptor是重定向拦截器

第四步,call.execute()

我们来看RealCall的execute方法

public Response execute() throws IOException {    synchronized (this) {      if (executed) throw new IllegalStateException("Already Executed");      executed = true;    }    captureCallStackTrace();    eventListener.callStart(this);    try {      client.dispatcher().executed(this);      Response result = getResponseWithInterceptorChain();      if (result == null) throw new IOException("Canceled");      return result;    } catch (IOException e) {      eventListener.callFailed(this, e);      throw e;    } finally {      client.dispatcher().finished(this);    }  }

核心代码是这些:

client.dispatcher().executed(this);      Response result = getResponseWithInterceptorChain();      if (result == null) throw new IOException("Canceled");      return result;

需要着重介绍的就是这个方法:

synchronized void executed(RealCall call) {    runningSyncCalls.add(call);  }

 runningSyncCalls是一个存放RealCall的队列,Deque继承自Queue

private final Deque
runningSyncCalls = new ArrayDeque<>();

由此,我们可以看到同步的请求会被放到一个队列之中。在这里先简单讲解一下Dispatcher

可以看到,Dispatcher的主要作用就是保存Call请求的各种状态,然后它又维护了一个线程池,用于执行网络请求。具体就是,这些call被一个个地发送给一个个的子线程来执行(异步请求,不是同步请求)。

回到execute()方法:

finally {      client.dispatcher().finished(this);    }

点进去:

void finished(RealCall call) {    finished(runningSyncCalls, call, false);  }  private 
void finished(Deque
calls, T call, boolean promoteCalls) { int runningCallsCount; Runnable idleCallback; synchronized (this) { if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!"); if (promoteCalls) promoteCalls(); runningCallsCount = runningCallsCount(); idleCallback = this.idleCallback; } if (runningCallsCount == 0 && idleCallback != null) { idleCallback.run(); } }

需要注意两点:第一,需要执行calls.remove(call)这个操作,如果未执行,会抛出异常;

第二,同步请求不会走promoteCalls()这个方法,异步请求才会走,我们在上面关于Dispatcher的图中可以看到这个方法。

转载地址:http://puttz.baihongyu.com/

你可能感兴趣的文章