OKHttp源碼解析

源碼解析之前先看之前請(qǐng)求過(guò)程

 private  OkHttpClient mClient=new OkHttpClient();

    public static final String BASE_URL="http://greatfeng.top/app";

    public void run() throws Exception {
        Request request = new Request.Builder()
                .url(BASE_URL+"/weather?cityname="+"上海")
                .build();

        mClient.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()) 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());
            }
        });
    }

1.0 初始化一個(gè)OkHttpClient對(duì)象

new OkHttpClient();

1.1. 新建一個(gè)對(duì)象,會(huì)先加載這個(gè)類(lèi)月培,會(huì)初始化這個(gè)類(lèi)的變量和靜態(tài)代碼塊

// 默認(rèn)支持的http協(xié)議版本 http 2.0,http 1.1
  static final List<Protocol> DEFAULT_PROTOCOLS = Util.immutableList(
      Protocol.HTTP_2, Protocol.HTTP_1_1);
  // 默認(rèn)支持的 https,http
  static final List<ConnectionSpec> DEFAULT_CONNECTION_SPECS = Util.immutableList(
      ConnectionSpec.MODERN_TLS, ConnectionSpec.CLEARTEXT);
// //新建一個(gè)內(nèi)部使用的輔助類(lèi),基本上就是調(diào)用第一個(gè)參數(shù)的,同名方法,后面的參數(shù)作為方法的參數(shù)
static {
    Internal.instance = new Internal() {
      @Override public void addLenient(Headers.Builder builder, String line) {
        builder.addLenient(line);
      }
...
...
      @Override public Call newWebSocketCall(OkHttpClient client, Request originalRequest) {
        return new RealCall(client, originalRequest, true);
      }
    };
  }

1.2 執(zhí)行OkHttpClient構(gòu)造函數(shù)

public OkHttpClient() {
    this(new Builder());
  }
  

1.3 初始化builder的成員變量,執(zhí)行builder的構(gòu)造函數(shù)

/* 這兩個(gè)攔截器主要是用來(lái)我們對(duì)請(qǐng)求過(guò)程進(jìn)行攔截處理,添加一些我們直接的處理,
    如打印請(qǐng)求日志,為每個(gè)請(qǐng)求添加相同的請(qǐng)求頭字段,如 apikey 等
    兩個(gè)區(qū)別在于interceptors是在和服務(wù)器未連接的時(shí)候攔截處理,
    networkInterceptors是和服務(wù)器建立連接后添加的攔截處理
*/

    final List<Interceptor> interceptors = new ArrayList<>(); // 攔截器
    final List<Interceptor> networkInterceptors = new ArrayList<>(); // 網(wǎng)絡(luò)攔截器
    @Nullable Cache cache; // 配置緩存的
     @Nullable Proxy proxy; //配置代理服務(wù)器
     SocketFactory socketFactory; // http 用于創(chuàng)建 Socket 對(duì)象 
     @Nullable SSLSocketFactory sslSocketFactory; // https 用于配置創(chuàng)建 SSLSocket 對(duì)象 
 public Builder() {
    // 新建一個(gè) Dispatcher 對(duì)象,主要是用來(lái)構(gòu)建線程池分發(fā)請(qǐng)求的
      dispatcher = new Dispatcher(); 
      protocols = DEFAULT_PROTOCOLS;  // 協(xié)議版本 http 2.0, http 1.1
      connectionSpecs = DEFAULT_CONNECTION_SPECS; // 協(xié)議類(lèi)型 https,http
      /* 
      事件監(jiān)聽(tīng)器 默認(rèn)為NONE,如果要對(duì)網(wǎng)絡(luò)請(qǐng)求過(guò)程(請(qǐng)求開(kāi)始,連接 請(qǐng)求結(jié)束,
      發(fā)送請(qǐng)求頭,發(fā)送請(qǐng)求體,dns解析)進(jìn)行監(jiān)聽(tīng),可以進(jìn)行自定義 EventListener
      */
      eventListenerFactory = EventListener.factory(EventListener.NONE); 
      // 設(shè)置默認(rèn)代理選擇器,是 sun.net.spi.DefaultProxySelector 
      proxySelector = ProxySelector.getDefault();
      // 處理 cookie 
      cookieJar = CookieJar.NO_COOKIES;
      socketFactory = SocketFactory.getDefault(); // 設(shè)置默認(rèn)的 SocketFactory
      hostnameVerifier = OkHostnameVerifier.INSTANCE; // 配置 https 檢測(cè)服務(wù)器返回的證書(shū)信息與域名匹配認(rèn)證
      certificatePinner = CertificatePinner.DEFAULT; // 配置 https證書(shū)校驗(yàn)信息sha256
      // 當(dāng)有設(shè)置代理服務(wù)器訪問(wèn)時(shí),代理服務(wù)器需要賬號(hào)密碼認(rèn)證,默認(rèn)不啟用代理
      proxyAuthenticator = Authenticator.NONE;
      // http 認(rèn)證,有些服務(wù)器進(jìn)行訪問(wèn)時(shí)需要賬號(hào)密碼的,如 basic認(rèn)證 默認(rèn)為空
      authenticator = Authenticator.NONE;
      // 緩存連接池
      connectionPool = new ConnectionPool();
      // DNS解析器,默認(rèn)使用系統(tǒng)的,可以自己自定義的,也可以使用阿里的HttpDns
      dns = Dns.SYSTEM;
      // https連接是否重定向
      followSslRedirects = true;
      // http 連接是否重定向
      followRedirects = true;
      // 連接失敗是否重試
      retryOnConnectionFailure = true;
      // 連接超時(shí)時(shí)間
      connectTimeout = 10_000; 
      // 讀取服務(wù)器數(shù)據(jù)超時(shí)時(shí)間
      readTimeout = 10_000;
      // 發(fā)送給服務(wù)器數(shù)據(jù)超時(shí)時(shí)間
      writeTimeout = 10_000;
      // http 2.0 版本新增的功能(用于計(jì)算往返時(shí)間劣坊,執(zhí)行“ 活性” 檢活)得问。設(shè)置間隔多少毫秒執(zhí)行一次,為 0 默認(rèn)不啟用
      pingInterval = 0; 
    }

1.4. Dispatcher對(duì)象的初始化,里面有個(gè)線程池背稼,使用懶加載的形式,只有發(fā)生第一次請(qǐng)求的時(shí)候才會(huì)初始化線程池玻蝌,使用線程池進(jìn)行管理發(fā)送的網(wǎng)絡(luò)請(qǐng)求的Call

  // 線程池中最大同時(shí)請(qǐng)求個(gè)數(shù)蟹肘,超過(guò)個(gè)數(shù)得進(jìn)行排隊(duì) 词疼。可進(jìn)行設(shè)置
  private int maxRequests = 64;
  // 為避免線程池中請(qǐng)求中被同一個(gè)host的請(qǐng)求占滿(mǎn)了帘腹,同時(shí)也是減輕服務(wù)器壓力贰盗,可設(shè)置每個(gè)host的最大請(qǐng)求數(shù) ,若超過(guò)就會(huì)進(jìn)入排隊(duì)序列
  private int maxRequestsPerHost = 5;
  
  private @Nullable Runnable idleCallback;

  private @Nullable ExecutorService executorService;

 /* 準(zhǔn)備執(zhí)行的異常請(qǐng)求集合,由于設(shè)置了線程池中最大同時(shí)請(qǐng)求個(gè)數(shù),每個(gè)host的最大請(qǐng)求數(shù),
    如果超過(guò)就會(huì)存起來(lái),當(dāng)正在請(qǐng)求的 Call請(qǐng)求完成了,就會(huì)調(diào)度該集合里面的 Call 去請(qǐng)求
 */
  private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();

// 正在執(zhí)行的異步請(qǐng)求 Call集合
  private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();

 // 正在執(zhí)行的同步請(qǐng)求 Call 集合,只做統(tǒng)計(jì)
  private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
  
  // 單例模式,懶加載,線程池
   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;
  }
  

1.5. ConnectionPool對(duì)象的初始化


  private static final Executor executor = new ThreadPoolExecutor(0 /* corePoolSize */,
      Integer.MAX_VALUE /* maximumPoolSize */, 60L /* keepAliveTime */, TimeUnit.SECONDS,
      new SynchronousQueue<Runnable>(), Util.threadFactory("OkHttp ConnectionPool", true));

  /** The maximum number of idle connections for each address. */
  private final int maxIdleConnections;
  private final long keepAliveDurationNs;
  private final Runnable cleanupRunnable = new Runnable() {
    @Override public void run() {
      while (true) {
        long waitNanos = cleanup(System.nanoTime());
        if (waitNanos == -1) return;
        if (waitNanos > 0) {
          long waitMillis = waitNanos / 1000000L;
          waitNanos -= (waitMillis * 1000000L);
          synchronized (ConnectionPool.this) {
            try {
              ConnectionPool.this.wait(waitMillis, (int) waitNanos);
            } catch (InterruptedException ignored) {
            }
          }
        }
      }
    }
  };

  private final Deque<RealConnection> connections = new ArrayDeque<>();
  final RouteDatabase routeDatabase = new RouteDatabase();


public ConnectionPool() {
    this(5, 5, TimeUnit.MINUTES);
  }

  public ConnectionPool(int maxIdleConnections, long keepAliveDuration, TimeUnit timeUnit) {
    this.maxIdleConnections = maxIdleConnections;
    this.keepAliveDurationNs = timeUnit.toNanos(keepAliveDuration);

    // Put a floor on the keep alive duration, otherwise cleanup will spin loop.
    if (keepAliveDuration <= 0) {
      throw new IllegalArgumentException("keepAliveDuration <= 0: " + keepAliveDuration);
    }
  }

1.6. 對(duì)應(yīng)1.2中OKHttpClient構(gòu)造函數(shù)調(diào)用,OkHttpClient對(duì)象已生成

OkHttpClient(Builder builder) {
    this.dispatcher = builder.dispatcher;
    this.proxy = builder.proxy;
   .......
    this.pingInterval = builder.pingInterval;
  }

2.0 創(chuàng)建Request對(duì)象阳欲,包含一個(gè)網(wǎng)絡(luò)請(qǐng)求的信息舵盈,有請(qǐng)求頭,請(qǐng)求體胸完,請(qǐng)求網(wǎng)址等

        Request request = new Request.Builder()
                .url(BASE_URL+"/weather?cityname="+"上海")
                .build();

3.0 進(jìn)行網(wǎng)絡(luò)請(qǐng)求

 mClient.newCall(request).enqueue(new Callback() {})

3.1 創(chuàng)建RealCall對(duì)象

  @Override public Call newCall(Request request) {
    return new RealCall(this, request, false /* for web socket */);
  }
  
  
  RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    final EventListener.Factory eventListenerFactory = client.eventListenerFactory();

    this.client = client;
    this.originalRequest = originalRequest;
    this.forWebSocket = forWebSocket;
    this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);

    // TODO(jwilson): this is unsafe publication and not threadsafe.
    this.eventListener = eventListenerFactory.create(this);
  }
  
  

3.2 把請(qǐng)求發(fā)送到Dispatcher的線程池隊(duì)列

  @Override public void enqueue(Callback responseCallback) {
    synchronized (this) {
    //新建的RealCall的executed 變量為false,執(zhí)行完為ture,這就是RealCall對(duì)象不能執(zhí)行兩次的原因
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    // 創(chuàng)建AsyncCall對(duì)象加入到線程池
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
  }

3.3 AsyncCall繼承NamedRunnable 實(shí)現(xiàn)了Runnable接口

  final class AsyncCall extends NamedRunnable {
   
    }
    
    public abstract class NamedRunnable implements Runnable {
 
  @Override public final void run() {
      execute();
  }

  protected abstract void execute();
}

3.4 加入線程池后就會(huì)執(zhí)行在子線程中執(zhí)行AsyncCall 的execute的方法

  synchronized void enqueue(AsyncCall call) {
    // 1.4已說(shuō)明
    if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
      runningAsyncCalls.add(call);
      executorService().execute(call);
    } else {
      readyAsyncCalls.add(call);
    }
  }

3.5 execute 調(diào)用getResponseWithInterceptorChain方法獲取到Response對(duì)象,即服務(wù)器返回?cái)?shù)據(jù)

 @Override protected void execute() {
        Response response = getResponseWithInterceptorChain();
        if (retryAndFollowUpInterceptor.isCanceled()) {
          signalledCallback = true;
          // 若請(qǐng)求失敗會(huì)回調(diào) 3.0 中設(shè)置Callback onFailure方法
          responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
        } else {
          signalledCallback = true;
          // 若請(qǐng)求成功會(huì)回調(diào) 3.0 中設(shè)置 Callback onResponse方法
          responseCallback.onResponse(RealCall.this, response);
        }
finally {
        client.dispatcher().finished(this);
      }
    
  }
  

3.6 getResponseWithInterceptorChain

// 把之前設(shè)置的自定義interceptors书释,RetryAndFollowUpInterceptor翘贮,BridgeInterceptor
//赊窥,CacheInterceptor,ConnectInterceptor狸页,自定義的networkInterceptors锨能,CallServerInterceptor對(duì)象
// 保存在interceptors里面作為參數(shù)進(jìn)行構(gòu)造RealInterceptorChain
  Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List<Interceptor> interceptors = new ArrayList<>();
    // addInterceptor
    interceptors.addAll(client.interceptors());
    interceptors.add(retryAndFollowUpInterceptor);
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
    interceptors.add(new CacheInterceptor(client.internalCache()));
    interceptors.add(new ConnectInterceptor(client));
    // addNetworkInterceptor
    if (!forWebSocket) {
      interceptors.addAll(client.networkInterceptors());
    }
    interceptors.add(new CallServerInterceptor(forWebSocket));
   
    Interceptor.Chain chain = new RealInterceptorChain(
        interceptors, null, null, null, 0, originalRequest);
    return chain.proceed(originalRequest);
  }
  

3.7. interceptors 保存很多攔截器

OkHttpClient Builder有兩個(gè)添加Interceptor 兩者處于不通的調(diào)用階段,我們可以在網(wǎng)絡(luò)請(qǐng)求的這兩個(gè)階段自定義攔截器芍耘,做不同的處理如上篇博文提到的HttpLoggingInterceptor HttpCacheInterceptor等

    
    public Builder addInterceptor(Interceptor interceptor) {
      interceptors.add(interceptor);
      return this;
    }
    
 public Builder addNetworkInterceptor(Interceptor interceptor) {
      networkInterceptors.add(interceptor);
      return this;
    }

Interceptor 和 Chain 接口都比較簡(jiǎn)單

  public interface Interceptor {
  Response intercept(Chain chain) throws IOException;

  interface Chain {
    Request request();

    Response proceed(Request request) throws IOException;

    /**
     * Returns the connection the request will be executed on. This is only available in the chains
     * of network interceptors; for application interceptors this is always null.
     */
    @Nullable Connection connection();
  }
}

3.8 創(chuàng)建RealInterceptorChain

  public RealInterceptorChain(List<Interceptor> interceptors, StreamAllocation streamAllocation,
      HttpCodec httpCodec, RealConnection connection, int index, Request request) {
    this.interceptors = interceptors;
    this.connection = connection;
    this.streamAllocation = streamAllocation;
    this.httpCodec = httpCodec;
    this.index = index;
    this.request = request;
  }

3.9 調(diào)用proceed方法

  • proceed方法先進(jìn)行了數(shù)據(jù)的校驗(yàn)址遇,數(shù)據(jù)如果不正確,拋出異常
  • proceed每次都會(huì)用之前構(gòu)造的這個(gè)RealInterceptorChain 的interceptors, streamAllocation, httpCodec, connection, index + 1, request 或者傳入單個(gè)或者多個(gè)新的值去替換部分原來(lái)的值 從而去構(gòu)建新的RealInterceptorChain對(duì)象斋竞,然后獲取到第index個(gè)Interceptor,調(diào)用Interceptor的intercept方法倔约,并把新建的RealInterceptorChain對(duì)象作為參數(shù)傳入
  • 然后Interceptor的intercept方法又會(huì)調(diào)用上部分傳入的RealInterceptorChain對(duì)象的proceed方法 這就成了一個(gè)循環(huán)直到最后一個(gè)CallServerInterceptor,沒(méi)有調(diào)用 RealInterceptorChain的proceed方法跳出循環(huán)返回服務(wù)器返回的Response坝初,請(qǐng)求完成

這個(gè)就使得浸剩,interceptors中的上一個(gè)Interceptor的intercept方法依賴(lài)下一個(gè)Interceptor的intercept的返回結(jié)果,依次調(diào)用完interceptors中的所有Interceptor的intercept方法鳄袍。

@Override public Response proceed(Request request) throws IOException {
    return proceed(request, streamAllocation, httpCodec, connection);
  }

  public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
      RealConnection connection) throws IOException {

    // Call the next interceptor in the chain.
    RealInterceptorChain next = new RealInterceptorChain(
        interceptors, streamAllocation, httpCodec, connection, index + 1, request);
    Interceptor interceptor = interceptors.get(index);
    Response response = interceptor.intercept(next);

    return response;
  }

所以一個(gè)正常的請(qǐng)求會(huì)依次調(diào)用下列Interceptor的intercept方法得到返回Response

4.0 RetryAndFollowUpInterceptor

4.1 在RealCall構(gòu)造函數(shù)中新建RetryAndFollowUpInterceptor 對(duì)象

 
// 見(jiàn) 3.1
this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
// 見(jiàn) 3.6
interceptors.add(retryAndFollowUpInterceptor);

4.2 若未添加自定義Intercept,這是第一個(gè)調(diào)用的Intercept的intercept

==followUpRequest方法和HTTP協(xié)議相關(guān)是對(duì)HTTP協(xié)議的實(shí)現(xiàn)==

 @Override public Response intercept(Chain chain) throws IOException {
    Request request = chain.request();
// 新建 StreamAllocation對(duì)象 
    streamAllocation = new StreamAllocation(
        client.connectionPool(), createAddress(request.url()), callStackTrace);

    int followUpCount = 0;
    Response priorResponse = null;
    // 無(wú)限循環(huán)绢要,可以多次重試請(qǐng)求,followUpCount請(qǐng)求次數(shù)大于MAX_FOLLOW_UPS(20)會(huì)拋出ProtocolException拗小,跳出循環(huán)
    while (true) {
    //進(jìn)行取消請(qǐng)求重罪,跳出循環(huán)就是由這個(gè)標(biāo)志位控制的
      if (canceled) {
        streamAllocation.release();
        throw new IOException("Canceled");
      }

      Response response = null;
      boolean releaseConnection = true;
    
      // 把streamAllocation對(duì)象傳入下一個(gè) BridgeInterceptor
      
        response = ((RealInterceptorChain) chain).proceed(request, streamAllocation, null, null);
        releaseConnection = false;
...
      // Attach the prior response if it exists. Such responses never have a body.
      // 有些請(qǐng)求可能要多次和服務(wù)器進(jìn)行通信才能完成,如代理,認(rèn)證等哀九,若之前有先前有返回響應(yīng)體剿配,存儲(chǔ)到Response的priorResponse中
      if (priorResponse != null) {
        response = response.newBuilder()
            .priorResponse(priorResponse.newBuilder()
                    .body(null)
                    .build())
            .build();
      }

// 根據(jù)服務(wù)器返回代碼和HTTP協(xié)議格式,判斷是否需要多次和服務(wù)器進(jìn)行通信
      Request followUp = followUpRequest(response);
// 若不需要阅束,直接返回請(qǐng)求結(jié)果惨篱,循環(huán)結(jié)束
      if (followUp == null) {
        if (!forWebSocket) {
          streamAllocation.release();
        }
        return response;
      }
// 關(guān)閉這個(gè)返回體的流,進(jìn)行判斷重試次數(shù)围俘,及異常檢查 繼續(xù)進(jìn)行下一次循環(huán)

...
      closeQuietly(response.body());
      
      if (++followUpCount > MAX_FOLLOW_UPS) {
        streamAllocation.release();
        throw new ProtocolException("Too many follow-up requests: " + followUpCount);
      }

      request = followUp;
      priorResponse = response;
    }
  }

4.3 新建Address對(duì)象

  private Address createAddress(HttpUrl url) {
    SSLSocketFactory sslSocketFactory = null;
    HostnameVerifier hostnameVerifier = null;
    CertificatePinner certificatePinner = null;
    if (url.isHttps()) {
      sslSocketFactory = client.sslSocketFactory();
      hostnameVerifier = client.hostnameVerifier();
      certificatePinner = client.certificatePinner();
    }

    return new Address(url.host(), url.port(), client.dns(), client.socketFactory(),
        sslSocketFactory, hostnameVerifier, certificatePinner, client.proxyAuthenticator(),
        client.proxy(), client.protocols(), client.connectionSpecs(), client.proxySelector());
  }

4.4 新建StreamAllocation對(duì)象

  public StreamAllocation(ConnectionPool connectionPool, Address address, Object callStackTrace) {
    this.connectionPool = connectionPool;
    this.address = address;
    this.routeSelector = new RouteSelector(address, routeDatabase());
    this.callStackTrace = callStackTrace;
  }
  

4.5 構(gòu)建RouteSelector

  
    public RouteSelector(Address address, RouteDatabase routeDatabase) {
    this.address = address;
    this.routeDatabase = routeDatabase;

    resetNextProxy(address.url(), address.proxy());
  }
  
  
    /** Prepares the proxy servers to try. */
  private void resetNextProxy(HttpUrl url, Proxy proxy) {
    if (proxy != null) {
      // If the user specifies a proxy, try that and only that.
      proxies = Collections.singletonList(proxy);
    } else {
      // Try each of the ProxySelector choices until one connection succeeds.
      List<Proxy> proxiesOrNull = address.proxySelector().select(url.uri());
      proxies = proxiesOrNull != null && !proxiesOrNull.isEmpty()
          ? Util.immutableList(proxiesOrNull)
          : Util.immutableList(Proxy.NO_PROXY);
    }
    nextProxyIndex = 0;
  }

5.0 BridgeInterceptor

5.1

     //  見(jiàn)3.6
interceptors.add(new BridgeInterceptor(client.cookieJar()));

  public BridgeInterceptor(CookieJar cookieJar) {
    this.cookieJar = cookieJar;
  }

5.2 構(gòu)建完整的請(qǐng)求砸讳,把一些默認(rèn)的請(qǐng)求頭添加上去琢融,默認(rèn) Keep-Alive ,對(duì)請(qǐng)求進(jìn)行g(shù)zip壓縮傳輸簿寂,用okio庫(kù)中GzipSource進(jìn)行解壓

 @Override public Response intercept(Chain chain) throws IOException {
    Request userRequest = chain.request();
    Request.Builder requestBuilder = userRequest.newBuilder();

    RequestBody body = userRequest.body();
    if (body != null) {
      MediaType contentType = body.contentType();
     ...

    if (userRequest.header("Connection") == null) {
      requestBuilder.header("Connection", "Keep-Alive");
    }

    // If we add an "Accept-Encoding: gzip" header field we're responsible for also decompressing
    // the transfer stream.
    boolean transparentGzip = false;
    if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) {
      transparentGzip = true;
      requestBuilder.header("Accept-Encoding", "gzip");
    }

    List<Cookie> cookies = cookieJar.loadForRequest(userRequest.url());
    if (!cookies.isEmpty()) {
      requestBuilder.header("Cookie", cookieHeader(cookies));
    }

    if (userRequest.header("User-Agent") == null) {
      requestBuilder.header("User-Agent", Version.userAgent());
    }


// 把構(gòu)建好的新的請(qǐng)求傳入CacheInterceptor替換原來(lái)的Request對(duì)象
    Response networkResponse = chain.proceed(requestBuilder.build());

    HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());

    if (transparentGzip
        && "gzip".equalsIgnoreCase(networkResponse.header("Content-Encoding"))
        && HttpHeaders.hasBody(networkResponse)) {
      GzipSource responseBody = new GzipSource(networkResponse.body().source());
      Headers strippedHeaders = networkResponse.headers().newBuilder()
          .removeAll("Content-Encoding")
          .removeAll("Content-Length")
          .build();
      responseBuilder.headers(strippedHeaders);
      responseBuilder.body(new RealResponseBody(strippedHeaders, Okio.buffer(responseBody)));
    }

    return responseBuilder.build();
  }

6.0 CacheInterceptor

6.1 Cache的使用見(jiàn) OkHttp使用介紹
中Cache-Control 字段

// 見(jiàn)3.6 
interceptors.add(new CacheInterceptor(client.internalCache()));
// 默認(rèn)情況下 Okhttp是沒(méi)有設(shè)置cache和internalCache的
  InternalCache internalCache() {
    return cache != null ? cache.internalCache : internalCache;
  }
  
  public CacheInterceptor(InternalCache cache) {
    this.cache = cache;
  }

6.2 我們考慮設(shè)置了Cache的情況

==Cache的情況也是HTTP協(xié)議相關(guān)是對(duì)HTTP協(xié)議的實(shí)現(xiàn)==

使用的是DiskLruCache作為緩存漾抬,磁盤(pán)緩存庫(kù)

  @Override public Response intercept(Chain chain) throws IOException {
    Response cacheCandidate = cache != null
    // 6.3
        ? cache.get(chain.request())
        : null;

    long now = System.currentTimeMillis();
// 6.4
    CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
    Request networkRequest = strategy.networkRequest;
    Response cacheResponse = strategy.cacheResponse;
    
    // If we don't need the network, we're done.
    if (networkRequest == null) {
      return cacheResponse.newBuilder()
          .cacheResponse(stripBody(cacheResponse))
          .build();
    }

// ...

    Response networkResponse = null;
    try {
    
    
     // 若沒(méi)有緩存,進(jìn)入ConnectInterceptor的intercept方法
      networkResponse = chain.proceed(networkRequest);
      
    } finally {
      // If we're crashing on I/O or otherwise, don't leak the cache body.
      if (networkResponse == null && cacheCandidate != null) {
        closeQuietly(cacheCandidate.body());
      }
    }

  // ...

    return response;
  }

6.3.1

    @Override public Response get(Request request) throws IOException {
      return Cache.this.get(request);
    }

6.3.2

    @Nullable Response get(Request request) {
    String key = key(request.url());
    DiskLruCache.Snapshot snapshot;
    Entry entry;
    try {
      snapshot = cache.get(key);
      if (snapshot == null) {
        return null;
      }
    } catch (IOException e) {
      // Give up because the cache cannot be read.
      return null;
    }

    try {
      entry = new Entry(snapshot.getSource(ENTRY_METADATA));
    } catch (IOException e) {
      Util.closeQuietly(snapshot);
      return null;
    }

    Response response = entry.response(snapshot);

    if (!entry.matches(request, response)) {
      Util.closeQuietly(response.body());
      return null;
    }

    return response;
  }

6.3.3

public Response response(DiskLruCache.Snapshot snapshot) {
      String contentType = responseHeaders.get("Content-Type");
      String contentLength = responseHeaders.get("Content-Length");
      Request cacheRequest = new Request.Builder()
          .url(url)
          .method(requestMethod, null)
          .headers(varyHeaders)
          .build();
      return new Response.Builder()
          .request(cacheRequest)
          .protocol(protocol)
          .code(code)
          .message(message)
          .headers(responseHeaders)
          .body(new CacheResponseBody(snapshot, contentType, contentLength))
          .handshake(handshake)
          .sentRequestAtMillis(sentRequestMillis)
          .receivedResponseAtMillis(receivedResponseMillis)
          .build();
    }
  }

6.4.1

public Factory(long nowMillis, Request request, Response cacheResponse) {
      this.nowMillis = nowMillis;
      this.request = request;
      this.cacheResponse = cacheResponse;

      if (cacheResponse != null) {
        this.sentRequestMillis = cacheResponse.sentRequestAtMillis();
        this.receivedResponseMillis = cacheResponse.receivedResponseAtMillis();
        Headers headers = cacheResponse.headers();
        for (int i = 0, size = headers.size(); i < size; i++) {
          String fieldName = headers.name(i);
          String value = headers.value(i);
          if ("Date".equalsIgnoreCase(fieldName)) {
            servedDate = HttpDate.parse(value);
            servedDateString = value;
          } else if ("Expires".equalsIgnoreCase(fieldName)) {
            expires = HttpDate.parse(value);
          } else if ("Last-Modified".equalsIgnoreCase(fieldName)) {
            lastModified = HttpDate.parse(value);
            lastModifiedString = value;
          } else if ("ETag".equalsIgnoreCase(fieldName)) {
            etag = value;
          } else if ("Age".equalsIgnoreCase(fieldName)) {
            ageSeconds = HttpHeaders.parseSeconds(value, -1);
          }
        }
      }
    }
    

6.4.2

    public CacheStrategy get() {
      CacheStrategy candidate = getCandidate();

      if (candidate.networkRequest != null && request.cacheControl().onlyIfCached()) {
        // We're forbidden from using the network and the cache is insufficient.
        return new CacheStrategy(null, null);
      }

      return candidate;
    }
  private CacheStrategy getCandidate() {
      // No cached response.
      if (cacheResponse == null) {
        return new CacheStrategy(request, null);
      }

      // Drop the cached response if it's missing a required handshake.
      if (request.isHttps() && cacheResponse.handshake() == null) {
        return new CacheStrategy(request, null);
      }

      // If this response shouldn't have been stored, it should never be used
      // as a response source. This check should be redundant as long as the
      // persistence store is well-behaved and the rules are constant.
      if (!isCacheable(cacheResponse, request)) {
        return new CacheStrategy(request, null);
      }

      CacheControl requestCaching = request.cacheControl();
      if (requestCaching.noCache() || hasConditions(request)) {
        return new CacheStrategy(request, null);
      }

      long ageMillis = cacheResponseAge();
      long freshMillis = computeFreshnessLifetime();

      if (requestCaching.maxAgeSeconds() != -1) {
        freshMillis = Math.min(freshMillis, SECONDS.toMillis(requestCaching.maxAgeSeconds()));
      }

      long minFreshMillis = 0;
      if (requestCaching.minFreshSeconds() != -1) {
        minFreshMillis = SECONDS.toMillis(requestCaching.minFreshSeconds());
      }

      long maxStaleMillis = 0;
      CacheControl responseCaching = cacheResponse.cacheControl();
      if (!responseCaching.mustRevalidate() && requestCaching.maxStaleSeconds() != -1) {
        maxStaleMillis = SECONDS.toMillis(requestCaching.maxStaleSeconds());
      }

      if (!responseCaching.noCache() && ageMillis + minFreshMillis < freshMillis + maxStaleMillis) {
        Response.Builder builder = cacheResponse.newBuilder();
        if (ageMillis + minFreshMillis >= freshMillis) {
          builder.addHeader("Warning", "110 HttpURLConnection \"Response is stale\"");
        }
        long oneDayMillis = 24 * 60 * 60 * 1000L;
        if (ageMillis > oneDayMillis && isFreshnessLifetimeHeuristic()) {
          builder.addHeader("Warning", "113 HttpURLConnection \"Heuristic expiration\"");
        }
        return new CacheStrategy(null, builder.build());
      }

      // Find a condition to add to the request. If the condition is satisfied, the response body
      // will not be transmitted.
      String conditionName;
      String conditionValue;
      if (etag != null) {
        conditionName = "If-None-Match";
        conditionValue = etag;
      } else if (lastModified != null) {
        conditionName = "If-Modified-Since";
        conditionValue = lastModifiedString;
      } else if (servedDate != null) {
        conditionName = "If-Modified-Since";
        conditionValue = servedDateString;
      } else {
        return new CacheStrategy(request, null); // No condition! Make a regular request.
      }

      Headers.Builder conditionalRequestHeaders = request.headers().newBuilder();
      Internal.instance.addLenient(conditionalRequestHeaders, conditionName, conditionValue);

      Request conditionalRequest = request.newBuilder()
          .headers(conditionalRequestHeaders.build())
          .build();
      return new CacheStrategy(conditionalRequest, cacheResponse);
    }

7.0 ConnectInterceptor

  @Override public Response intercept(Chain chain) throws IOException {
    RealInterceptorChain realChain = (RealInterceptorChain) chain;
    Request request = realChain.request();
    // 見(jiàn)4.2 獲取新建的StreamAllocation對(duì)象
    StreamAllocation streamAllocation = realChain.streamAllocation();

    // We need the network to satisfy this request. Possibly for validating a conditional GET.
    boolean doExtensiveHealthChecks = !request.method().equals("GET");
    
  
    HttpCodec httpCodec = streamAllocation.newStream(client, doExtensiveHealthChecks);
    
    RealConnection connection = streamAllocation.connection();

    // 把request, streamAllocation, httpCodec, connection傳入
    
    //進(jìn)入CallServerInterceptor的intercept
    return realChain.proceed(request, streamAllocation, httpCodec, connection);
  }
public HttpCodec newStream(OkHttpClient client, boolean doExtensiveHealthChecks) {
// ...

// 7.1.0
      RealConnection resultConnection = findHealthyConnection(connectTimeout, readTimeout,
          writeTimeout, connectionRetryEnabled, doExtensiveHealthChecks);
          
// 7.2.0        
      HttpCodec resultCodec = resultConnection.newCodec(client, this);

      synchronized (connectionPool) {
        codec = resultCodec;
        return resultCodec;
      }

7.1.1

  private RealConnection findHealthyConnection(int connectTimeout, int readTimeout,
      int writeTimeout, boolean connectionRetryEnabled, boolean doExtensiveHealthChecks)
      throws IOException {
    while (true) {
      RealConnection candidate = findConnection(connectTimeout, readTimeout, writeTimeout,
          connectionRetryEnabled);

     .....

      return candidate;
    }
  }

7.1.2

private RealConnection findConnection(int connectTimeout, int readTimeout, int writeTimeout,
      boolean connectionRetryEnabled) throws IOException {
    Route selectedRoute;
    synchronized (connectionPool) {
  ...

      // Attempt to use an already-allocated connection.
      // 嘗試使用自身存儲(chǔ)的連接
      RealConnection allocatedConnection = this.connection;
      if (allocatedConnection != null && !allocatedConnection.noNewStreams) {
        return allocatedConnection;
      }

      // Attempt to get a connection from the pool.
      
      //嘗試從連接池中獲取
      Internal.instance.get(connectionPool, address, this, null);
      if (connection != null) {
        return connection;
      }

      selectedRoute = route;
    }

    // If we need a route, make one. This is a blocking operation.
    if (selectedRoute == null) {
      selectedRoute = routeSelector.next();
    }

    RealConnection result;
    synchronized (connectionPool) {
      if (canceled) throw new IOException("Canceled");

      // Now that we have an IP address, make another attempt at getting a connection from the pool.
      // This could match due to connection coalescing.
      
      //有了selectedRoute后再次從連接池中獲取連接
      Internal.instance.get(connectionPool, address, this, selectedRoute);
      if (connection != null) {
        route = selectedRoute;
        return connection;
      }

      // Create a connection and assign it to this allocation immediately. This makes it possible
      // for an asynchronous cancel() to interrupt the handshake we're about to do.
      route = selectedRoute;
      refusedStreamCount = 0;
      
    // 新建連接常遂,并把這個(gè)RealConnection對(duì)象和StreamAllocation對(duì)象綁定
      
      result = new RealConnection(connectionPool, selectedRoute);
      acquire(result);
    }


    // Do TCP + TLS handshakes. This is a blocking operation.
    result.connect(connectTimeout, readTimeout, writeTimeout, connectionRetryEnabled);
    
    Socket socket = null;
    synchronized (connectionPool) {
      // Pool the connection.
      // 把新建的RealConnection對(duì)象纳令,放入到連接池
      Internal.instance.put(connectionPool, result);

      // If another multiplexed connection to the same address was created concurrently, then
      // release this connection and acquire that one.
      if (result.isMultiplexed()) {
        socket = Internal.instance.deduplicate(connectionPool, address, this);
        result = connection;
      }
    }
    closeQuietly(socket);

    return result;
  }

7.1.3

 public void connect(
      int connectTimeout, int readTimeout, int writeTimeout, boolean connectionRetryEnabled) {
   // ....

    while (true) {
      try {
      // 若為https
        if (route.requiresTunnel()) {
          connectTunnel(connectTimeout, readTimeout, writeTimeout);
        } else {
      //如果為http
      
      // 7.1.3.1.0
          connectSocket(connectTimeout, readTimeout);
        }
    // 7.1.3.2.0
        establishProtocol(connectionSpecSelector);
        break;
      }

 

7.1.3.1.1

==和服務(wù)建立socket連接,source克胳,sink是Okio庫(kù)的IO讀寫(xiě)類(lèi)==,它補(bǔ)充了java.io和java.nio的不足平绩,以便能夠更加方便,快速的訪問(wèn)漠另、存儲(chǔ)和處理你的數(shù)據(jù).

private void connectSocket(int connectTimeout, int readTimeout) throws IOException {
    Proxy proxy = route.proxy();
    Address address = route.address();

    rawSocket = proxy.type() == Proxy.Type.DIRECT || proxy.type() == Proxy.Type.HTTP
        ? address.socketFactory().createSocket() //創(chuàng)建 Socket
        : new Socket(proxy);

    rawSocket.setSoTimeout(readTimeout);
    try {
      Platform.get().connectSocket(rawSocket, route.socketAddress(), connectTimeout); //和服務(wù)器建立連接 
    } catch (ConnectException e) {
 //   ...
    }
// ...
      source = Okio.buffer(Okio.source(rawSocket));
      sink = Okio.buffer(Okio.sink(rawSocket));
 // ...  
  }

7.1.3.1.2

public Socket createSocket() {
        return new Socket();
    }

 public void connectSocket(Socket socket, InetSocketAddress address,
      int connectTimeout) throws IOException {
    socket.connect(address, connectTimeout);
  }

7.1.3.2.2

 private void establishProtocol(ConnectionSpecSelector connectionSpecSelector) throws IOException {
    if (route.address().sslSocketFactory() == null) {
      protocol = Protocol.HTTP_1_1;
// 普通http,socket連接
      socket = rawSocket;
      return;
    }

// 構(gòu)建https連接捏雌,暫不分析
    connectTls(connectionSpecSelector);

    if (protocol == Protocol.HTTP_2) {
      socket.setSoTimeout(0); // HTTP/2 connection timeouts are set per-stream.
      http2Connection = new Http2Connection.Builder(true)
          .socket(socket, route.address().url().host(), source, sink)
          .listener(this)
          .build();
      http2Connection.start();
    }
  }

7.2 構(gòu)建Http1Codec對(duì)象

  public HttpCodec newCodec(
      OkHttpClient client, StreamAllocation streamAllocation) throws SocketException {
 // 若為http,http2Connection為null
    if (http2Connection != null) {
      return new Http2Codec(client, streamAllocation, http2Connection);
    } else {
      socket.setSoTimeout(client.readTimeoutMillis());
      source.timeout().timeout(client.readTimeoutMillis(), MILLISECONDS);
      sink.timeout().timeout(client.writeTimeoutMillis(), MILLISECONDS);
      
      // 把source, sink 負(fù)責(zé)和服務(wù)器IO讀寫(xiě)流傳入
      return new Http1Codec(client, streamAllocation, source, sink);
    }
  }

8.0 CallServerInterceptor

@Override public Response intercept(Chain chain) throws IOException {
    RealInterceptorChain realChain = (RealInterceptorChain) chain;
// ...

// 8.1
    httpCodec.writeRequestHeaders(request);

    Response.Builder responseBuilder = null;
// 8.2  向服務(wù)器傳輸請(qǐng)求體
    if (HttpMethod.permitsRequestBody(request.method()) && request.body() != null) {
      // If there's a "Expect: 100-continue" header on the request, wait for a "HTTP/1.1 100
      // Continue" response before transmitting the request body. If we don't get that, return what
      // we did get (such as a 4xx response) without ever transmitting the request body.
      if ("100-continue".equalsIgnoreCase(request.header("Expect"))) {
        httpCodec.flushRequest();
        responseBuilder = httpCodec.readResponseHeaders(true);
      }

      if (responseBuilder == null) {
        // Write the request body if the "Expect: 100-continue" expectation was met.
        Sink requestBodyOut = httpCodec.createRequestBody(request, request.body().contentLength());
        BufferedSink bufferedRequestBody = Okio.buffer(requestBodyOut);
        request.body().writeTo(bufferedRequestBody);
        bufferedRequestBody.close();
      } else if (!connection.isMultiplexed()) {
        // If the "Expect: 100-continue" expectation wasn't met, prevent the HTTP/1 connection from
        // being reused. Otherwise we're still obligated to transmit the request body to leave the
        // connection in a consistent state.
        streamAllocation.noNewStreams();
      }
    }
// ...  flush()請(qǐng)求
    httpCodec.finishRequest();
// 8.3
    if (responseBuilder == null) {
      responseBuilder = httpCodec.readResponseHeaders(false);
    }

// ...

    int code = response.code();
    if (forWebSocket && code == 101) {
      // Connection is upgrading, but we need to ensure interceptors see a non-null response body.
      response = response.newBuilder()
          .body(Util.EMPTY_RESPONSE)
          .build();
    } else {
// 8.4
      response = response.newBuilder()
          .body(httpCodec.openResponseBody(response))
          .build();
    }

//...

    return response;
  }

==HTTP請(qǐng)求格式==

image

8.1 向服務(wù)器傳輸請(qǐng)求頭

  @Override public void writeRequestHeaders(Request request) throws IOException {
    String requestLine = RequestLine.get(
        request, streamAllocation.connection().route().proxy().type());
    writeRequest(request.headers(), requestLine);
  }

public static String get(Request request, Proxy.Type proxyType) {
    StringBuilder result = new StringBuilder();
    result.append(request.method());
    result.append(' ');

    if (includeAuthorityInRequestLine(request, proxyType)) {
      result.append(request.url());
    } else {
      result.append(requestPath(request.url()));
    }

    result.append(" HTTP/1.1");
    return result.toString();
  }
  
  
public void writeRequest(Headers headers, String requestLine) throws IOException {
    if (state != STATE_IDLE) throw new IllegalStateException("state: " + state);
    sink.writeUtf8(requestLine).writeUtf8("\r\n");
    for (int i = 0, size = headers.size(); i < size; i++) {
      sink.writeUtf8(headers.name(i))
          .writeUtf8(": ")
          .writeUtf8(headers.value(i))
          .writeUtf8("\r\n");
    }
    sink.writeUtf8("\r\n");
    state = STATE_OPEN_REQUEST_BODY;
  }

8.3 獲取響應(yīng)頭


@Override public Response.Builder readResponseHeaders(boolean expectContinue) throws IOException {
    if (state != STATE_OPEN_REQUEST_BODY && state != STATE_READ_RESPONSE_HEADERS) {
      throw new IllegalStateException("state: " + state);
    }

    try {
      StatusLine statusLine = StatusLine.parse(source.readUtf8LineStrict());

      Response.Builder responseBuilder = new Response.Builder()
          .protocol(statusLine.protocol)
          .code(statusLine.code)
          .message(statusLine.message)
          .headers(readHeaders());

      if (expectContinue && statusLine.code == HTTP_CONTINUE) {
        return null;
      }

      state = STATE_OPEN_RESPONSE_BODY;
      return responseBuilder;
    } catch (EOFException e) {
      // Provide more context if the server ends the stream before sending a response.
      IOException exception = new IOException("unexpected end of stream on " + streamAllocation);
      exception.initCause(e);
      throw exception;
    }
  }

8.4 獲取響應(yīng)體

  @Override public ResponseBody openResponseBody(Response response) throws IOException {
    Source source = getTransferStream(response);
    return new RealResponseBody(response.headers(), Okio.buffer(source));
  }
  
  private Source getTransferStream(Response response) throws IOException {
    if (!HttpHeaders.hasBody(response)) {
      return newFixedLengthSource(0);
    }

    if ("chunked".equalsIgnoreCase(response.header("Transfer-Encoding"))) {
      return newChunkedSource(response.request().url());
    }

    long contentLength = HttpHeaders.contentLength(response);
    if (contentLength != -1) {
      return newFixedLengthSource(contentLength);
    }

    // Wrap the input stream from the connection (rather than just returning
    // "socketIn" directly here), so that we can control its use after the
    // reference escapes.
    return newUnknownLengthSource();
  }

9. 整個(gè)網(wǎng)絡(luò)請(qǐng)求過(guò)程已經(jīng)完成

見(jiàn)3.5 Responsed對(duì)象返回回調(diào)Callback

// 不管請(qǐng)求成功還是失敗都會(huì)調(diào)用finished方法
finally {
        client.dispatcher().finished(this);
      }
  void finished(AsyncCall call) {
    finished(runningAsyncCalls, call, true);
  }
  private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
...
...
      if (promoteCalls) promoteCalls();
}

// 每次運(yùn)行完一個(gè)AsyncCall如果運(yùn)行的個(gè)數(shù)不超過(guò)限制笆搓,就添加一個(gè)請(qǐng)求進(jìn)入線程池
  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.
    }
  }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末性湿,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子满败,更是在濱河造成了極大的恐慌肤频,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,198評(píng)論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件算墨,死亡現(xiàn)場(chǎng)離奇詭異宵荒,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)净嘀,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評(píng)論 3 398
  • 文/潘曉璐 我一進(jìn)店門(mén)报咳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人面粮,你說(shuō)我怎么就攤上這事少孝。” “怎么了熬苍?”我有些...
    開(kāi)封第一講書(shū)人閱讀 167,643評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵稍走,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我柴底,道長(zhǎng)婿脸,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,495評(píng)論 1 296
  • 正文 為了忘掉前任柄驻,我火速辦了婚禮狐树,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘鸿脓。我一直安慰自己抑钟,他們只是感情好涯曲,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,502評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著在塔,像睡著了一般幻件。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蛔溃,一...
    開(kāi)封第一講書(shū)人閱讀 52,156評(píng)論 1 308
  • 那天绰沥,我揣著相機(jī)與錄音,去河邊找鬼贺待。 笑死徽曲,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的麸塞。 我是一名探鬼主播秃臣,決...
    沈念sama閱讀 40,743評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼喘垂!你這毒婦竟也來(lái)了甜刻?” 一聲冷哼從身側(cè)響起绍撞,我...
    開(kāi)封第一講書(shū)人閱讀 39,659評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤正勒,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后傻铣,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體章贞,經(jīng)...
    沈念sama閱讀 46,200評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,282評(píng)論 3 340
  • 正文 我和宋清朗相戀三年非洲,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了鸭限。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,424評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡两踏,死狀恐怖败京,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情梦染,我是刑警寧澤赡麦,帶...
    沈念sama閱讀 36,107評(píng)論 5 349
  • 正文 年R本政府宣布,位于F島的核電站帕识,受9級(jí)特大地震影響泛粹,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜肮疗,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,789評(píng)論 3 333
  • 文/蒙蒙 一晶姊、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧伪货,春花似錦们衙、人聲如沸钾怔。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,264評(píng)論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)蒂教。三九已至,卻和暖如春脆荷,著一層夾襖步出監(jiān)牢的瞬間凝垛,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,390評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工蜓谋, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留梦皮,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,798評(píng)論 3 376
  • 正文 我出身青樓桃焕,卻偏偏與公主長(zhǎng)得像剑肯,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子观堂,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,435評(píng)論 2 359

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