Okhttp3使用及解析

目录

1.使用

1)同步请求

2)异步请求

3)提交string

4)提交流

5)提交文件

6)提交表单

7)提交分块请求

8)非系统拦截器

2.解析执行流程

1)OkHttpClient创建

2)Request创建

3)Call创建

4)execute执行

         5)enqueue执行


Okhttp3使用及解析:https://mp.csdn.net/postedit/83339916

okhttp系统拦截器:https://mp.csdn.net/postedit/83536609

Okhttp的连接池ConnectionPool:https://mp.csdn.net/postedit/83650740

1.使用

依赖: implementation 'com.squareup.okhttp3:okhttp:3.10.0'

1)同步请求

    //同步请求
    private void sendRequest() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                OkHttpClient okHttpClient = new OkHttpClient
                        .Builder()
                        .readTimeout(5, TimeUnit.SECONDS)//设置超时...
                        .build();
                try {
                    Request request = new Request.Builder()
                            .url("http://www.baidu.com")
                            .get() //请求方式
                            .build();
                    //Request封装成Call对象
                    Call call = okHttpClient.newCall(request);
                    Response response = call.execute();
                    Log.e("result: ", response.body().string());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }

2)异步请求

    //异步请求
    private void sendAsyRequest() {
        OkHttpClient okHttpClient = new OkHttpClient
                .Builder()
                .readTimeout(5, TimeUnit.SECONDS)//设置超时...
                .build();
        Request request = new Request.Builder()
                .url("http://www.baidu.com")
                .get() //请求方式
                .build();
        //Request封装成Call对象
        Call call = okHttpClient.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {

            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                Log.e("result: ", response.body().string());
            }
        });
    }

除了执行方式为execute与enqueue的区别,execute方式会阻塞当前线程,而enqueue方式会自动开启一个工作线程去处理事务,而且onFailure与onResponse回调都是在工作线程中触发的。

3)提交string

MediaType mediaType = MediaType.parse("text/x-markdown; charset=utf-8");
String requestBody = "content";
Request request = new Request.Builder()
        .url("request url")
        .post(RequestBody.create(mediaType, requestBody))
        .build();

4)提交流

RequestBody requestBody = new RequestBody() {
    @Nullable
    @Override
    public MediaType contentType() {
        return MediaType.parse("text/x-markdown; charset=utf-8");
    }

    @Override
    public void writeTo(BufferedSink sink) throws IOException {
        sink.writeUtf8("request content");
    }
};

Request request = new Request.Builder()
        .url("request url")
        .post(requestBody)
        .build();

5)提交文件

OkHttpClient okHttpClient = new OkHttpClient();
MediaType mediaType = MediaType.parse("text/x-markdown; charset=utf-8");
File file = new File("test.md");
Request request = new Request.Builder()
        .url("request url")
        .post(RequestBody.create(mediaType, file))
        .build();

6)提交表单

OkHttpClient okHttpClient = new OkHttpClient();
RequestBody requestBody = new FormBody.Builder()
        .add("des", "content")
        .build();
Request request = new Request.Builder()
        .url("request url")
        .post(requestBody)
        .build();

7)提交分块请求

    OkHttpClient client = new OkHttpClient();
    MultipartBody body = new MultipartBody.Builder(" ")
            .setType(MultipartBody.FORM)
            .addPart(
                    Headers.of("Content-Disposition", "form-data; name=\"title\""),
                    RequestBody.create(null, "content"))
            .addPart(
                    Headers.of("Content-Disposition", "form-data; name=\"image\""),
                    RequestBody.create(MediaType.parse("image/png"), new File(" ")))
            .build();

    Request request = new Request.Builder()
            .url("request url")
            .post(body)
            .build();

8)非系统拦截器

转自:https://www.jianshu.com/p/ba6e219a0af6

这里是一个简单的拦截器,用来打印出去的请求和收到的响应。

class LoggingInterceptor implements Interceptor {
  @Override public Response intercept(Interceptor.Chain chain) throws IOException {
    Request request = chain.request();

    long t1 = System.nanoTime();
    logger.info(String.format("Sending request %s on %s%n%s",
        request.url(), chain.connection(), request.headers()));

    Response response = chain.proceed(request);

    long t2 = System.nanoTime();
    logger.info(String.format("Received response for %s in %.1fms%n%s",
        response.request().url(), (t2 - t1) / 1e6d, response.headers()));

    return response;
  }
}

应用拦截器:

OkHttpClient okHttpClient = new OkHttpClient.Builder()
        .addInterceptor(new LoggingInterceptor())
        .build();

或者自定义级别:

OkHttpClient.Builder builder = new OkHttpClient.Builder();
HttpLoggingInterceptor.Level level = HttpLoggingInterceptor.Level.BODY;
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
            @Override
            public void log(String message) {
                Log.e("日志拦截器: ", "网络接口数据");
            }
        });
loggingInterceptor.setLevel(level);
builder.addInterceptor(loggingInterceptor);
        okHttpClient = builder.build();

网络拦截器:

OkHttpClient client = new OkHttpClient.Builder()
    .addNetworkInterceptor(new LoggingInterceptor())
    .build();

2.解析执行流程

1)OkHttpClient创建

Builder()中的分发器Dispatcher:分发事务,决定异步事务是直接处理还是缓存等待。

ConnectionPool:存储请求链接的池,比如请求链接相同,可决定复用等等操作。

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;
    }

2)Request创建

Builder()中,指定请求一些参数和请求头信息

public Builder() {
  this.method = "GET"; //默认get
  this.headers = new Headers.Builder(); //请求头信息
}

build方法,实例化Request,将请求参数赋值

public Request build() {
      if (url == null) throw new IllegalStateException("url == null");
      return new Request(this);
    }


 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;
  }

3)Call创建

Call官方解释:为了执行请求调用Call,Call可以取消。Call对象表示单个请求/响应对(流),不能执行两次。

先看okHttpClient.newCall(request)中newCall:

//准备在将来某个时候执行
@Override 
public Call newCall(Request request) {
    return RealCall.newRealCall(this, request, false /* for web socket */);
}

RealCall为Call的实现类,看看newRealCall函数:

static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    // 安全地将调用实例发布到EventListener。
    RealCall call = new RealCall(client, originalRequest, forWebSocket);
    call.eventListener = client.eventListenerFactory().create(call);
    return call;
  }

newRealCall中,实例化RealCall,并设置监听。看看RealCall构造:

private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    this.client = client;
    this.originalRequest = originalRequest;
    this.forWebSocket = forWebSocket;
    this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
  }

可以看到之前创建好的OkHttpClient和Request都被传入,同时实例化了RetryAndFollowUpInterceptor(重定向拦截器)。

4)execute执行

public interface Call extends Cloneable {
...
Response execute() throws IOException;
...
}

execute()是Call接口中的,具体看它实现类RealCall中的操作:

@Override public Response execute() throws IOException {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
    //execute只能被执行一次,执行一次后修改标记为true,再次execute抛异常
      executed = true;
    }
    captureCallStackTrace();//捕捉异常信息
    eventListener.callStart(this);//callstart回调执行
    try {
      client.dispatcher().executed(this);//分发事务
      Response result = getResponseWithInterceptorChain();//拦截器
      if (result == null) throw new IOException("Canceled");
      return result;
    } catch (IOException e) {
      eventListener.callFailed(this, e);//callfail回调执行
      throw e;
    } finally {
      client.dispatcher().finished(this);  //处理完成后调用
    }
  }

上面有句重点:

client.dispatcher().executed(this);

而dispatcher()只是拿到分发器:

public Dispatcher dispatcher() {
    return dispatcher;
  }

而executed():

public final class Dispatcher {

//同步请求队列
private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
......
 synchronized void executed(RealCall call) {
    runningSyncCalls.add(call);
  }
}

将同步请求的Call添加到队列runningSyncCalls中。可以看出分发器在execute()过程中起到了很核心的作用,最后看到execute()末尾的client.dispatcher().finished(this),这finished(this):

void finished(RealCall call) {
    finished(runningSyncCalls, call, false);
  }

  private <T> void finished(Deque<T> 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();  //传入promoteCalls==false,不走这个
      runningCallsCount = runningCallsCount();//获取总数
      idleCallback = this.idleCallback;
    }

    if (runningCallsCount == 0 && idleCallback != null) {//同步队列无任务且回调非空
      idleCallback.run();
    }
  }

//获取同步和异步执行队列任务总和
public synchronized int runningCallsCount() {
    return runningAsyncCalls.size() + runningSyncCalls.size();
  }

作用为将Call从同步执行队列移除。

总结:将Call的事件,通过分发器分发到到队列runningSyncCalls中,事件执行完,再通过分发器将这个事件移除。

5)enqueue执行

 @Override public void enqueue(Callback responseCallback) {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      //call只被执行一次
      executed = true;
    }
    captureCallStackTrace();
    eventListener.callStart(this);
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
  }

重点enqueue函数:

public final class Dispatcher {

private int maxRequests = 64;
private int maxRequestsPerHost = 5;
//处理请求的线程池
private @Nullable ExecutorService executorService;
//存储不满足立刻运行条件的异步请求
private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();
//存储可立刻运行的异步请求
private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();

synchronized void enqueue(AsyncCall call) {
    //当前执行的异步队列数量小于最大请求数  且 当前主机执行请求数小于主机请求数最大值
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
      runningAsyncCalls.add(call); //加入可立刻执行的异步请求队列
      executorService().execute(call); //线程池执行单次事务
    } else {
      readyAsyncCalls.add(call);  //不满足条件,加入缓存异步队列readyAsyncCalls
    }
  }
...}

看看enqueue函数的入参AsyncCall:

final class AsyncCall extends NamedRunnable {
......
private final Callback responseCallback;

 AsyncCall(Callback responseCallback) {
      super("OkHttp %s", redactedUrl());
      this.responseCallback = responseCallback;
    }
}

是将CallBack(单次事件处理的完成的回调)赋值给AsyncCall中的Callback。那NamedRunnable是什么:

public abstract class NamedRunnable implements Runnable {
  protected final String name;
  protected abstract void execute();

  public NamedRunnable(String format, Object... args) {
    this.name = Util.format(format, args);
  }

  //单次任务的执行
  @Override public final void run() {
    String oldName = Thread.currentThread().getName();
    Thread.currentThread().setName(name);
    try {
      //单次任务的执行内容抽象
      execute();
    } finally {
      Thread.currentThread().setName(oldName);
    }
  }
}

得知单次请求事务的处理,还是交给实现类的execute()方法去处理,即AsyncCall类,看看AsyncCall类的execute()方法:

 @Override protected void execute() {
      boolean signalledCallback = false;
      try {
        Response response = getResponseWithInterceptorChain(); //拦截器链操作
        if (retryAndFollowUpInterceptor.isCanceled()) {//拦截器被取消
          signalledCallback = true;
          responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
        } else {
          signalledCallback = true;
          responseCallback.onResponse(RealCall.this, response);//(子线程)真正发送请求
        }
      } catch (IOException e) {
        if (signalledCallback) {
          Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
        } else {
          eventListener.callFailed(RealCall.this, e);
          responseCallback.onFailure(RealCall.this, e);
        }
      } finally {
        client.dispatcher().finished(this);//从队列移除本次请求
      }
    }

下面看看移除的过程:

void finished(AsyncCall call) {
    finished(runningAsyncCalls, call, true);
}

//调用3参数的finish()
private <T> void finished(Deque<T> 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();
    }
  }

public synchronized int runningCallsCount() {
    return runningAsyncCalls.size() + runningSyncCalls.size();
}

promoteCalls函数具体如下:

private void promoteCalls() {
    if (runningAsyncCalls.size() >= maxRequests) return; // 当前为最大请求数量
    if (readyAsyncCalls.isEmpty()) return; //就绪队列为空

    for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
      AsyncCall call = i.next();
      if (runningCallsForHost(call) < maxRequestsPerHost) {
        i.remove();
        runningAsyncCalls.add(call);//将就绪队列的请求拿出
        executorService().execute(call);//拿出后给线程池处理
      }
      if (runningAsyncCalls.size() >= maxRequests) return; 
    }
  }

可以看出:异步运行队列的请求执行完,会通过promoteCalls函数,将异步就绪队列runningAsyncCalls中满足条件的请求拿出给线程池处理

总结为:将CallBack,也就是单次事务执行完回调绑定到Runnable,也就是AsyncCall,再将AsyncCall所代表的Runnable给线程池处理,最终通过回调返回结果。

猜你喜欢

转载自blog.csdn.net/qq_37321098/article/details/83339916
今日推荐