OkHttp3實現(xiàn)原理分析(二)

概述

前言:前一節(jié)http://www.reibang.com/p/f3f228d3598c够傍,總結(jié)了一下OkHttp3的簡單使用教程。在項目中使用了這個網(wǎng)絡(luò)框架,在看完基本的源碼之后,還是想總結(jié)一下OkHttp的實現(xiàn)流程。在學(xué)習(xí)框架的過程中阔逼,從使用方法出發(fā),首先是怎么使用地沮,其次是我們使用的功能在內(nèi)部是如何實現(xiàn)的嗜浮,實現(xiàn)方案上有什么技巧羡亩,有什么范式。

OkHttp的整體流程

整個流程是:通過OkHttpClient將構(gòu)建的Request轉(zhuǎn)換為Call危融,然后在RealCall中進行異步或同步任務(wù)畏铆,最后通過一些的攔截器interceptor發(fā)出網(wǎng)絡(luò)請求和得到返回的response〖辏總體流程用下面的圖表示

Okhttp3整體流程.png

拆組件

在整體流程中辞居,主要的組件是OkHttpClient,其次有Call,RealCall,Disptcher,各種InterceptorsRequest和Response組件蛋勺。Request和Response已經(jīng)在上一篇的對其結(jié)構(gòu)源碼進行了分析瓦灶。

1. OkHttpClient對象:網(wǎng)絡(luò)請求的主要操控者

創(chuàng)建OkHttpClient對象

//通過Builder構(gòu)造OkHttpClient
 OkHttpClient.Builder builder = new OkHttpClient.Builder()
                .connectTimeout(20, TimeUnit.SECONDS)
                .writeTimeout(20, TimeUnit.SECONDS)
                .readTimeout(20, TimeUnit.SECONDS);
        return builder.build();

OkHttpClient.Builder類有很多變量,OkHttpClient有很多的成員變量:

final Dispatcher dispatcher;  //重要:分發(fā)器,分發(fā)執(zhí)行和關(guān)閉由request構(gòu)成的Call
    final Proxy proxy;  //代理
    final List<Protocol> protocols; //協(xié)議
    final List<ConnectionSpec> connectionSpecs; //傳輸層版本和連接協(xié)議
    final List<Interceptor> interceptors; //重要:攔截器
    final List<Interceptor> networkInterceptors; //網(wǎng)絡(luò)攔截器
    final ProxySelector proxySelector; //代理選擇
    final CookieJar cookieJar; //cookie
    final Cache cache; //緩存
    final InternalCache internalCache;  //內(nèi)部緩存
    final SocketFactory socketFactory;  //socket 工廠
    final SSLSocketFactory sslSocketFactory; //安全套接層socket 工廠抱完,用于HTTPS
    final CertificateChainCleaner certificateChainCleaner; // 驗證確認(rèn)響應(yīng)證書 適用 HTTPS 請求連接的主機名贼陶。
    final HostnameVerifier hostnameVerifier;    //  主機名字確認(rèn)
    final CertificatePinner certificatePinner;  //  證書鏈
    final Authenticator proxyAuthenticator;     //代理身份驗證
    final Authenticator authenticator;      // 本地身份驗證
    final ConnectionPool connectionPool;    //連接池,復(fù)用連接
    final Dns dns;  //域名
    final boolean followSslRedirects;  //安全套接層重定向
    final boolean followRedirects;  //本地重定向
    final boolean retryOnConnectionFailure; //重試連接失敗
    final int connectTimeout;    //連接超時
    final int readTimeout; //read 超時
    final int writeTimeout; //write 超時

OkHttpClient完成整個請求設(shè)計到很多參數(shù),都可以通過OkHttpClient.builder使用創(chuàng)建者模式構(gòu)建巧娱。事實上每界,你能夠通過它來設(shè)置改變一些參數(shù),因為他是通過建造者模式實現(xiàn)的家卖,因此你可以通過builder()來設(shè)置。如果不進行設(shè)置庙楚,在Builder中就會使用默認(rèn)的設(shè)置:

 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上荡,RealCall:真正的請求執(zhí)行者

2.1之前文章中的Http發(fā)起同步請求的代碼:

 Request request = new Request.Builder()
      .url(url)
      .build();
  Response response = client.newCall(request).execute();

client.newCall(request).execute()創(chuàng)建了Call執(zhí)行了網(wǎng)絡(luò)請求獲得response響應(yīng)。重點看一看這個執(zhí)行的請求者的內(nèi)部是什么鬼馒闷。

/**
   * Prepares the {@code request} to be executed at some point in the future.
   */
//OkHttpClient中的方法酪捡,可以看出RealCall的真面目
  @Override public Call newCall(Request request) {
    return RealCall.newRealCall(this, request, false /* for web socket */);
  }

RealCall的構(gòu)造函數(shù):

 private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    //client請求
    this.client = client;
    //我們構(gòu)造的請求
    this.originalRequest = originalRequest;
    this.forWebSocket = forWebSocket;
    //負責(zé)重試和重定向攔截器
    this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
  }

Call對象其實是一個接口,Call的源碼:


public interface Call extends Cloneable {
  /** Returns the original request that initiated this call. */
  //用于返回Call對象中的request對象
  Request request();
  //用于執(zhí)行同步請求的方法
  Response execute() throws IOException;
 //用于執(zhí)行異步請求的方法纳账,通過responseCallback回調(diào)結(jié)果
  void enqueue(Callback responseCallback);

  /** Cancels the request, if possible. Requests that are already complete cannot be canceled. */
  //取消這個call逛薇,當(dāng)call被取消時請求不在執(zhí)行,拋出異常疏虫∮婪#可以用于終止請求
  void cancel();

  /**
   * Returns true if this call has been either {@linkplain #execute() executed} or {@linkplain
   * #enqueue(Callback) enqueued}. It is an error to execute a call more than once.
   */
 //是否被執(zhí)行
  boolean isExecuted();
 //是否被取消
  boolean isCanceled();

  /**
   * Create a new, identical call to this one which can be enqueued or executed even if this call
   * has already been.
   */
  Call clone();

  interface Factory {
    Call newCall(Request request);
  }
}

Realcall是Call的實現(xiàn)類。顯然重要的執(zhí)行任務(wù)就交個RealCall對象execute()和enqueue(Callback responseCallback)方法了卧秘。
我們首先看 RealCall#execute

 @Override public Response execute() throws IOException {
    //(1)
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    //事件監(jiān)聽器調(diào)
    eventListener.callStart(this);
    try {
      //(2)
      client.dispatcher().executed(this);
      //(3)
      Response result = getResponseWithInterceptorChain();
      if (result == null) throw new IOException("Canceled");
      return result;
    } catch (IOException e) {
      eventListener.callFailed(this, e);
      throw e;
    } finally {
      //(4)
      client.dispatcher().finished(this);
    }
  }

(1)檢查這個 call 是否已經(jīng)被執(zhí)行了呢袱,每個 call 只能被執(zhí)行一次,如果想要一個完全一樣的 call翅敌,可以利用 call#clone 方法進行克隆羞福。
(2)利用 client.dispatcher().executed(this) 來進行實際執(zhí)行,分發(fā)器負責(zé)分發(fā)蚯涮。dispatcher 是剛才看到的 OkHttpClient.Builder 的成員之一治专,它的文檔說自己是異步 HTTP 請求的執(zhí)行策略卖陵,現(xiàn)在看來,同步請求它也有摻和张峰。
(3)調(diào)用 getResponseWithInterceptorChain() 函數(shù)獲取 HTTP 返回結(jié)果泪蔫,從函數(shù)名可以看出,這一步還會進行一系列“攔截”操作挟炬。
(4)最后還要通知 dispatcher 自己已經(jīng)執(zhí)行完畢

dispatcher 這里我們不過度關(guān)注鸥滨,在同步執(zhí)行的流程中,涉及到 dispatcher 的內(nèi)容只不過是告知它我們的執(zhí)行狀態(tài)谤祖,比如開始執(zhí)行了(調(diào)用 executed)婿滓,比如執(zhí)行完畢了(調(diào)用 finished)在異步執(zhí)行流程中它會有更多的參與粥喜。

Dispatcher的源碼主要在異步請求時參與多凸主,這里有執(zhí)行異步請求的線程池

/**
 * Policy on when async requests are executed.
 *
 * <p>Each dispatcher uses an {@link ExecutorService} to run calls internally. If you supply your
 * own executor, it should be able to run {@linkplain #getMaxRequests the configured maximum} number
 * of calls concurrently.
 */
//請求分發(fā)器,**主要在異步請求時參與多额湘,這里有執(zhí)行異步請求的線程池**
public final class Dispatcher {
  //最大的請求數(shù)量
  private int maxRequests = 64;
  //每個主機的請求數(shù)量卿吐,默認(rèn)在摸個主機上同時請求5個
  private int maxRequestsPerHost = 5;
  private @Nullable Runnable idleCallback;

  /** Executes calls. Created lazily. */
 //執(zhí)行異步call時的線程池,就在這兒
  private @Nullable ExecutorService executorService;

  /** Ready async calls in the order they'll be run. */
 //即將被執(zhí)行的異步call隊列
  private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();

  /** Running asynchronous calls. Includes canceled calls that haven't finished yet. */
 //正在運行的異步call锋华,包括被取消的還沒有完成的
  private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();

  /** Running synchronous calls. Includes canceled calls that haven't finished yet. */
 //正在運行的同步call嗡官。包括被取消的還沒有完成的
  private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
 //可以執(zhí)行自定義線程池,傳進來
  public Dispatcher(ExecutorService executorService) {
    this.executorService = executorService;
  }

  public Dispatcher() {
  }
 //構(gòu)造線程池
  public synchronized ExecutorService executorService() {
    if (executorService == null) {
      executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
          new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp Dispatcher", false));
    }
    return executorService;
  }

  /**
   * Set the maximum number of requests to execute concurrently. Above this requests queue in
   * memory, waiting for the running calls to complete.
   *
   * <p>If more than {@code maxRequests} requests are in flight when this is invoked, those requests
   * will remain in flight.
   */
  public synchronized void setMaxRequests(int maxRequests) {
    if (maxRequests < 1) {
      throw new IllegalArgumentException("max < 1: " + maxRequests);
    }
    this.maxRequests = maxRequests;
    promoteCalls();
  }

  public synchronized int getMaxRequests() {
    return maxRequests;
  }

  /**
   * Set the maximum number of requests for each host to execute concurrently. This limits requests
   * by the URL's host name. Note that concurrent requests to a single IP address may still exceed
   * this limit: multiple hostnames may share an IP address or be routed through the same HTTP
   * proxy.
   *
   * <p>If more than {@code maxRequestsPerHost} requests are in flight when this is invoked, those
   * requests will remain in flight.
   */
  public synchronized void setMaxRequestsPerHost(int maxRequestsPerHost) {
    if (maxRequestsPerHost < 1) {
      throw new IllegalArgumentException("max < 1: " + maxRequestsPerHost);
    }
    this.maxRequestsPerHost = maxRequestsPerHost;
    promoteCalls();
  }

  public synchronized int getMaxRequestsPerHost() {
    return maxRequestsPerHost;
  }

  /**
   * Set a callback to be invoked each time the dispatcher becomes idle (when the number of running
   * calls returns to zero).
   *
   * <p>Note: The time at which a {@linkplain Call call} is considered idle is different depending
   * on whether it was run {@linkplain Call#enqueue(Callback) asynchronously} or
   * {@linkplain Call#execute() synchronously}. Asynchronous calls become idle after the
   * {@link Callback#onResponse onResponse} or {@link Callback#onFailure onFailure} callback has
   * returned. Synchronous calls become idle once {@link Call#execute() execute()} returns. This
   * means that if you are doing synchronous calls the network layer will not truly be idle until
   * every returned {@link Response} has been closed.
   */
  public synchronized void setIdleCallback(@Nullable Runnable idleCallback) {
    this.idleCallback = idleCallback;
  }
  //分發(fā)異步執(zhí)行的call毯焕,是提交到線程池
  synchronized void enqueue(AsyncCall call) {
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
      runningAsyncCalls.add(call);
     //提交到線程此執(zhí)行
      executorService().execute(call);
    } else {
      readyAsyncCalls.add(call);
    }
  }

  /**
   * Cancel all calls currently enqueued or executing. Includes calls executed both {@linkplain
   * Call#execute() synchronously} and {@linkplain Call#enqueue asynchronously}.
   */
  public synchronized void cancelAll() {
    for (AsyncCall call : readyAsyncCalls) {
      call.get().cancel();
    }

    for (AsyncCall call : runningAsyncCalls) {
      call.get().cancel();
    }

    for (RealCall call : runningSyncCalls) {
      call.cancel();
    }
  }

  private void promoteCalls() {
    if (runningAsyncCalls.size() >= maxRequests) return; // Already running max capacity.
    if (readyAsyncCalls.isEmpty()) return; // No ready calls to promote.

    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; // Reached max capacity.
    }
  }

  /** Returns the number of running calls that share a host with {@code call}. */
  private int runningCallsForHost(AsyncCall call) {
    int result = 0;
    for (AsyncCall c : runningAsyncCalls) {
      if (c.host().equals(call.host())) result++;
    }
    return result;
  }

  /** Used by {@code Call#execute} to signal it is in-flight. */
 //分發(fā)同步call衍腥,只加入到正在運行同步call的隊列
  synchronized void executed(RealCall call) {
    runningSyncCalls.add(call);
  }

  /** Used by {@code AsyncCall#run} to signal completion. */

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

  /** Used by {@code Call#execute} to signal completion. */
  //同步call已經(jīng)完成,移除隊列
  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();
      runningCallsCount = runningCallsCount();
      idleCallback = this.idleCallback;
    }

    if (runningCallsCount == 0 && idleCallback != null) {
      idleCallback.run();
    }
  }

  /** Returns a snapshot of the calls currently awaiting execution. */
 //返回等待執(zhí)行call的集合
  public synchronized List<Call> queuedCalls() {
    List<Call> result = new ArrayList<>();
    for (AsyncCall asyncCall : readyAsyncCalls) {
      result.add(asyncCall.get());
    }
    return Collections.unmodifiableList(result);
  }

  /** Returns a snapshot of the calls currently being executed. */
  public synchronized List<Call> runningCalls() {
    List<Call> result = new ArrayList<>();
    result.addAll(runningSyncCalls);
    for (AsyncCall asyncCall : runningAsyncCalls) {
      result.add(asyncCall.get());
    }
    return Collections.unmodifiableList(result);
  }

  public synchronized int queuedCallsCount() {
    return readyAsyncCalls.size();
  }

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

在上面的同步call中纳猫,真正發(fā)出網(wǎng)絡(luò)請求婆咸,解析返回結(jié)果的,還是getResponseWithInterceptorChain

//重要的攔截器的責(zé)任鏈
 Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List<Interceptor> interceptors = new ArrayList<>();
    interceptors.addAll(client.interceptors());                                //(1)
    interceptors.add(retryAndFollowUpInterceptor);                             //(2)
    interceptors.add(new BridgeInterceptor(client.cookieJar()));               //(3)
    interceptors.add(new CacheInterceptor(client.internalCache()));            //(4)
    interceptors.add(new ConnectInterceptor(client));                          //(5)
    if (!forWebSocket) {
      interceptors.addAll(client.networkInterceptors());
    }
    interceptors.add(new CallServerInterceptor(forWebSocket));

    Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
        originalRequest, this, eventListener, client.connectTimeoutMillis(),
        client.readTimeoutMillis(), client.writeTimeoutMillis());

    return chain.proceed(originalRequest);
  }

3芜辕,在獲得相應(yīng)之前經(jīng)過的最后一關(guān)就是攔截器Interceptor

the whole thing is just a stack of built-in interceptors.

可見 Interceptor 是 OkHttp 最核心的一個東西尚骄,不要誤以為它只負責(zé)攔截請求進行一些額外的處理(例如 cookie),實際上它把實際的網(wǎng)絡(luò)請求侵续、緩存倔丈、透明壓縮等功能都統(tǒng)一了起來,每一個功能都只是一個 Interceptor状蜗,它們再連接成一個 Interceptor.Chain乃沙,環(huán)環(huán)相扣,最終圓滿完成一次網(wǎng)絡(luò)請求诗舰。

getResponseWithInterceptorChain 函數(shù)我們可以看到警儒,Interceptor.Chain 的分布依次是:

image.png

(1)在配置 OkHttpClient時設(shè)置的interceptors
(2)負責(zé)失敗重試以及重定向的 RetryAndFollowUpInterceptor
(3)負責(zé)把用戶構(gòu)造的請求轉(zhuǎn)換為發(fā)送到服務(wù)器的請求蜀铲、把服務(wù)器返回的響應(yīng)轉(zhuǎn)換為用戶友好的響應(yīng)的BridgeInterceptor边琉;
(4)負責(zé)讀取緩存直接返回、更新緩存的 CacheInterceptor
(5)負責(zé)和服務(wù)器建立連接的ConnectInterceptor记劝;
(6)配置 OkHttpClient 時設(shè)置的 networkInterceptors变姨;
(7)負責(zé)向服務(wù)器發(fā)送請求數(shù)據(jù)、從服務(wù)器讀取響應(yīng)數(shù)據(jù)CallServerInterceptor

在這里厌丑,位置決定了功能定欧,最后一個 Interceptor 一定是負責(zé)和服務(wù)器實際通訊的重定向怒竿、緩存等一定是在實際通訊之前的

2.2 在2.1中我們深入討論了同步請求的過程砍鸠,下面講講異步請求原理

代碼:

Request request = new Request.Builder()
        .url("http://publicobject.com/helloworld.txt")
        .build();
    //用request新建的call使用enqueue異步請求
    client.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 {
        //相應(yīng)成功回調(diào),response耕驰,非主線程
        if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

        Headers responseHeaders = response.headers();
        for (int i = 0, size = responseHeaders.size(); i < size; i++) {
          System.out.println(responseHeaders.name(i) + ": " + responseHeaders.value(i));
        }

        System.out.println(response.body().string());
      }
    });

由代碼中client.newCall(request).enqueue(Callback)爷辱,開始我們知道client.newCall(request)方法返回的是RealCall對象,接下來繼續(xù)向下看enqueue()方法:

//異步任務(wù)使用
    @Override 
    public void enqueue(Callback responseCallback) {
        synchronized (this) {
            if (executed) throw new IllegalStateException("Already Executed");
            executed = true;
        }
        //送給分發(fā)器Dispatcher分發(fā)朦肘,其實Dispatcher中有線程池饭弓,把AsyncCall這個任務(wù)提交到線程池執(zhí)行,通過responseCallback回調(diào)
        client.dispatcher().enqueue(new AsyncCall(responseCallback));
    }

我們先看一下上面的Dispatcher類中的enqueue(Call )方法媒抠,在看看AsyncCall類:

synchronized void enqueue(AsyncCall call) {
        if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
            runningAsyncCalls.add(call);
            executorService().execute(call);
        } else {
            readyAsyncCalls.add(call);
        }
    }

如果中的runningAsynCalls不滿弟断,且call占用的host小于最大數(shù)量,則將call加入到runningAsyncCalls中執(zhí)行趴生,同時利用線程池執(zhí)行call阀趴;否者將call加入到readyAsyncCalls中。runningAsyncCalls和readyAsyncCalls是什么呢冲秽?在把上面將同步Http請求時講過了,可以瞄一眼矩父。

call加入到線程池中執(zhí)行了★鄙#現(xiàn)在再看AsynCall的代碼,它是RealCall中的內(nèi)部類

//異步請求,顯然是繼承了NamedRunnable 窍株,在NamedRunnable 的run方法中執(zhí)行繼承的execute() 方法
    final class AsyncCall extends NamedRunnable {
        private final Callback responseCallback;

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

        String host() {
            return originalRequest.url().host();
        }

        Request request() {
            return originalRequest;
        }

        RealCall get() {
            return RealCall.this;
        }

        @Override protected void execute() {
            boolean signalledCallback = false;
            try {
                 //還是回到這個攔截器責(zé)任鏈函數(shù)得到響應(yīng)民轴,只不過當(dāng)前這個過程是在線程池中進行的
                Response response = getResponseWithInterceptorChain();
                if (retryAndFollowUpInterceptor.isCanceled()) {
                    signalledCallback = true;
                    //回調(diào)異常
                    responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
                } else {
                    signalledCallback = true;
                     //回調(diào)成功
                    responseCallback.onResponse(RealCall.this, response);
                }
            } catch (IOException e) {
                if (signalledCallback) {
                    // Do not signal the callback twice!
                    Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
                } else {
                   //回調(diào)失敗
                    responseCallback.onFailure(RealCall.this, e);
                }
            } finally {
                //告訴分發(fā)器Dispatcher請求執(zhí)行完成
                client.dispatcher().finished(this);
            }
        }
    }

AysncCall中的execute()中的方法,同樣是通過Response response = getResponseWithInterceptorChain();來獲得response球订,這樣異步任務(wù)也同樣通過了interceptor后裸,剩下的就想看看上面的幾個攔截器是什么鬼。

責(zé)任鏈攔截器Interceptor

RetryAndFollowUpInterceptor:負責(zé)失敗重試以及重定向
BridgeInterceptor:負責(zé)把用戶構(gòu)造的請求轉(zhuǎn)換為發(fā)送到服務(wù)器的請求冒滩、把服務(wù)器返回的響應(yīng)轉(zhuǎn)換為用戶友好的響應(yīng)的 微驶。
ConnectInterceptor:建立連接
NetworkInterceptors:配置OkHttpClient時設(shè)置的 NetworkInterceptors
CallServerInterceptor:發(fā)送和接收數(shù)據(jù)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子因苹,更是在濱河造成了極大的恐慌苟耻,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件扶檐,死亡現(xiàn)場離奇詭異凶杖,居然都是意外死亡,警方通過查閱死者的電腦和手機款筑,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進店門智蝠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人奈梳,你說我怎么就攤上這事杈湾。” “怎么了颈嚼?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵毛秘,是天一觀的道長。 經(jīng)常有香客問我阻课,道長叫挟,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任限煞,我火速辦了婚禮抹恳,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘署驻。我一直安慰自己奋献,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布旺上。 她就那樣靜靜地躺著瓶蚂,像睡著了一般。 火紅的嫁衣襯著肌膚如雪宣吱。 梳的紋絲不亂的頭發(fā)上窃这,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天,我揣著相機與錄音征候,去河邊找鬼杭攻。 笑死,一個胖子當(dāng)著我的面吹牛疤坝,可吹牛的內(nèi)容都是我干的兆解。 我是一名探鬼主播,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼跑揉,長吁一口氣:“原來是場噩夢啊……” “哼锅睛!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤衣撬,失蹤者是張志新(化名)和其女友劉穎乖订,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體具练,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡乍构,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了扛点。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片哥遮。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖陵究,靈堂內(nèi)的尸體忽然破棺而出眠饮,到底是詐尸還是另有隱情,我是刑警寧澤铜邮,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布仪召,位于F島的核電站,受9級特大地震影響松蒜,放射性物質(zhì)發(fā)生泄漏扔茅。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一秸苗、第九天 我趴在偏房一處隱蔽的房頂上張望召娜。 院中可真熱鬧,春花似錦惊楼、人聲如沸玖瘸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽雅倒。三九已至,卻和暖如春弧可,著一層夾襖步出監(jiān)牢的瞬間蔑匣,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工侣诺, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留殖演,地道東北人氧秘。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓年鸳,卻偏偏與公主長得像,于是被迫代替她去往敵國和親丸相。 傳聞我的和親對象是個殘疾皇子搔确,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,033評論 2 355

推薦閱讀更多精彩內(nèi)容