1. 程式人生 > >Okhttp3使用及解析

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給執行緒池處理,最終通過回撥返回結果。