OkHttp3源碼分析

OkHttp3是目前Android熱門(mén)的網(wǎng)絡(luò)請(qǐng)求框架之一轰绵,本篇來(lái)分析一下OkHttp3最主要的幾部分:

  • 同步扼菠、異步請(qǐng)求流程
  • 攔截器對(duì)請(qǐng)求的補(bǔ)充和攔截

連接池的復(fù)用連接和緩存連接也是一大亮點(diǎn)化焕,不過(guò)水平有限萄窜,暫時(shí)先不分析

OkHttpClient構(gòu)建

OkHttp3請(qǐng)求前需要?jiǎng)?chuàng)建一個(gè)OkHttpClient,所有的配置都在OkHttpClient的構(gòu)建時(shí)配置撒桨,它使用了構(gòu)建者模式(Builder模式)來(lái)具體化每個(gè)配置查刻,并且提供默認(rèn)配置。

  • 如果想使用默認(rèn)配置凤类,可直接使用new關(guān)鍵字創(chuàng)建(也是使用默認(rèn)配置的Builder來(lái)創(chuàng)建)穗泵,需要自定義配置則創(chuàng)建OkHttpClient.Builder配置類(lèi)傳入client進(jìn)行構(gòu)造。
OkHttpClient client = new OkHttpClient();

//使用默認(rèn)Builder配置來(lái)構(gòu)造client對(duì)象
public OkHttpClient() {
    this(new Builder());
}

//自定義Builder配置對(duì)象傳入構(gòu)造client
OkHttpClient(Builder builder) {
    //...
}

public static final class Builder {
/**
     * 請(qǐng)求分發(fā)器
     */
    Dispatcher dispatcher;
    @Nullable
    Proxy proxy;
    List<Protocol> protocols;
    List<ConnectionSpec> connectionSpecs;
    /**
     * 全局請(qǐng)求攔截器列表
     */
    final List<Interceptor> interceptors = new ArrayList<>();
    /**
     * 非WebSocket時(shí)會(huì)添加的請(qǐng)求攔截器
     */
    final List<Interceptor> networkInterceptors = new ArrayList<>();
    EventListener.Factory eventListenerFactory;
    ProxySelector proxySelector;
    CookieJar cookieJar;
    @Nullable
    Cache cache;
    /**
     * 內(nèi)部緩存谜疤,實(shí)際是緩存的Api接口佃延,內(nèi)部還是調(diào)用Cache對(duì)象中的方法來(lái)實(shí)現(xiàn)緩存
     */
    @Nullable
    InternalCache internalCache;
    SocketFactory socketFactory;
    /**
     * HTTPS使用的安全套接字Socket工廠
     */
    @Nullable
    SSLSocketFactory sslSocketFactory;
    @Nullable
    CertificateChainCleaner certificateChainCleaner;
    HostnameVerifier hostnameVerifier;
    CertificatePinner certificatePinner;
    Authenticator proxyAuthenticator;
    Authenticator authenticator;
    /**
     * 連接池
     */
    ConnectionPool connectionPool;
    Dns dns;
    /**
     * 安全套接字層重定向
     */
    boolean followSslRedirects;
    /**
     * 本地重定向
     */
    boolean followRedirects;
    /**
     * 重試連接失敗
     */
    boolean retryOnConnectionFailure;
    int connectTimeout;
    int readTimeout;
    int writeTimeout;
    int pingInterval;
    
    /**
     * 默認(rèn)的配置
     */
    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;
    }
    
    //...
}

同步請(qǐng)求流程分析

同步請(qǐng)求就是直接在當(dāng)前線程請(qǐng)求,會(huì)阻塞線程夷磕,所以如果在主線程請(qǐng)求履肃,必須開(kāi)啟一個(gè)子線程進(jìn)行請(qǐng)求。

OkHttpClient client = new OkHttpClient();
//創(chuàng)建請(qǐng)求配置類(lèi)Request
Request request = 
    new Request.Builder()
    .url(url)
    .build();
//newCall創(chuàng)建請(qǐng)求對(duì)象Call坐桩,再通過(guò)execute()開(kāi)始執(zhí)行尺棋,獲取請(qǐng)求結(jié)果Response對(duì)象
Response response = client.newCall(request).execute();
  • 先來(lái)看下client.newCall(request)方法,調(diào)用RealCall.newRealCall()靜態(tài)方法绵跷,創(chuàng)建一個(gè)RealCall對(duì)象膘螟,傳入request請(qǐng)求配置類(lèi)對(duì)象,并指定請(qǐng)求不是WebSocket請(qǐng)求碾局。
/**
 * 發(fā)起一個(gè)請(qǐng)求荆残,指定不是WebSocket請(qǐng)求
 *
 * @param request 請(qǐng)求的配置
 */
@Override
public Call newCall(Request request) {
    return RealCall.newRealCall(this, request, false /* for web socket */);
}
  • RealCall.newRealCall()靜態(tài)方法,實(shí)際就是創(chuàng)建了RealCall對(duì)象净当,并配置了一個(gè)事件回調(diào)内斯。
/**
 * 靜態(tài)方法配置一個(gè)RealCall,RealCall實(shí)現(xiàn)了Call接口蚯瞧,暫時(shí)是唯一實(shí)現(xiàn)嘿期,這么設(shè)計(jì)是為了后續(xù)拓展不同的Call
 *
 * @param client          請(qǐng)求客戶(hù)端
 * @param originalRequest 請(qǐng)求配置類(lèi)
 * @param forWebSocket    是否是WebSocket請(qǐng)求
 * @return 返回RealCall實(shí)例
 */
static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    // Safely publish the Call instance to the EventListener.
    RealCall call = new RealCall(client, originalRequest, forWebSocket);
    //為每個(gè)請(qǐng)求配置上外部設(shè)置的事件監(jiān)聽(tīng)回調(diào)
    call.eventListener = client.eventListenerFactory().create(call);
    return call;
}
  • RealCall類(lèi),實(shí)現(xiàn)了Call接口埋合,Call接口規(guī)定了請(qǐng)求可用的Api备徐,內(nèi)部還有一個(gè)Factory接口,newCall()創(chuàng)建Call對(duì)象的方法甚颂∶刍看到newCall()方法秀菱,那么OkHttpClient也肯定實(shí)現(xiàn)了這個(gè)接口(果然沒(méi)猜錯(cuò))
public interface Call extends Cloneable {
  //獲取配置的Request對(duì)象
  Request request();

  //異步請(qǐng)求Api
  void enqueue(Callback responseCallback);

  //取消任務(wù)
  void cancel();

  //任務(wù)是否執(zhí)行過(guò)了
  boolean isExecuted();

  //是否取消了
  boolean isCanceled();

  //克隆自己
  Call clone();

  //Call對(duì)象生成工廠
  interface Factory {
    Call newCall(Request request);
  }
}

//OkHttpClient實(shí)現(xiàn)了Call.Factory,所以它充當(dāng)了請(qǐng)求工廠的角色
public class OkHttpClient implements Cloneable, Call.Factory, WebSocket.Factory {
    //...
}

//Call接口的實(shí)現(xiàn)
final class RealCall implements Call {
    //構(gòu)造方法就是保存了構(gòu)造參數(shù)蹭睡,創(chuàng)建了一個(gè)攔截器衍菱,后面講
    private RealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
            this.client = client;
            this.originalRequest = originalRequest;
            this.forWebSocket = forWebSocket;
            this.retryAndFollowUpInterceptor = new RetryAndFollowUpInterceptor(client, forWebSocket);
        }
}
  • execute執(zhí)行方法,注意一個(gè)RealCall對(duì)象只能執(zhí)行一次肩豁,重點(diǎn)在getResponseWithInterceptorChain()方法脊串,調(diào)用它后就獲得了一個(gè)Response對(duì)象,那么請(qǐng)求肯定是在這個(gè)方法做的清钥。try-catch異常處理琼锋,finally塊調(diào)用分發(fā)器dispatcher的finished()方法通知分發(fā)器。
/**
 * 馬上執(zhí)行祟昭,為同步調(diào)用(非線程池)
 *
 * @return 請(qǐng)求結(jié)果類(lèi)
 */
@Override
public Response execute() throws IOException {
    synchronized (this) {
        //執(zhí)行過(guò)一次就不可以再執(zhí)行了缕坎,否則拋出異常
        if (executed) {
            throw new IllegalStateException("Already Executed");
        }
        //請(qǐng)求開(kāi)始,將標(biāo)志位設(shè)置為true
        executed = true;
    }
    captureCallStackTrace();
    //回調(diào)外部的事件監(jiān)聽(tīng)
    eventListener.callStart(this);
    try {
        //通知分發(fā)器篡悟,開(kāi)始執(zhí)行
        client.dispatcher().executed(this);
        //重點(diǎn):開(kāi)始責(zé)任鏈調(diào)用谜叹,讓每個(gè)攔截器處理請(qǐng)求request,得出請(qǐng)求結(jié)果Response
        Response result = getResponseWithInterceptorChain();
        if (result == null) {
            throw new IOException("Canceled");
        }
        return result;
    } catch (IOException e) {
        //異常搬葬,回調(diào)事件監(jiān)聽(tīng)對(duì)象
        eventListener.callFailed(this, e);
        throw e;
    } finally {
        //結(jié)束請(qǐng)求荷腊,調(diào)用分發(fā)器,讓分發(fā)器繼續(xù)執(zhí)行下一個(gè)任務(wù)
        client.dispatcher().finished(this);
    }
}
  • 責(zé)任鏈模式的攔截器踩萎,具體就是創(chuàng)建攔截器到集合列表停局,再創(chuàng)建了一個(gè)RealInterceptorChain攔截器鏈,通過(guò)proceed()方法處理Request對(duì)象香府,最后得出一個(gè)Response請(qǐng)求結(jié)果對(duì)象董栽,整體很干凈,具體工作都交給了攔截器企孩。
    1. 創(chuàng)建了一個(gè)攔截器列表锭碳,List<Interceptor>。
    2. 首先勿璃,先添加我們?cè)贑lient中配置的攔截器(全局的)
    3. 添加失敗和重定向處理的攔截器RetryAndFollowUpInterceptor
    4. 添加請(qǐng)求Header配置和Cookie的攔截器BridgeInterceptor
    5. 添加本地緩存和請(qǐng)求攔截的攔截器CacheInterceptor
    6. 添加服務(wù)請(qǐng)求連接的攔截器ConnectInterceptor
    7. 如果不是WebSocket請(qǐng)求擒抛,添加我們?cè)贑lient配置的networkInterceptors()攔截器(局部攔截器,在WebSocket中不會(huì)添加的攔截器)
    8. 添加真正請(qǐng)求服務(wù)器的攔截器CallServerInterceptor
    9. 創(chuàng)建攔截器鏈RealInterceptorChain补疑,它實(shí)現(xiàn)了Interceptor.Chain接口
    10. 調(diào)用proceed()方法開(kāi)始遍歷攔截器處理請(qǐng)求配置類(lèi)Request
/**
 * 開(kāi)始責(zé)任鏈處理Request請(qǐng)求
 *
 * @return 請(qǐng)求結(jié)果
 */
Response getResponseWithInterceptorChain() throws IOException {
    //攔截器列表
    List<Interceptor> interceptors = new ArrayList<>();
    //1歧沪、先添加我們?cè)贑lient中配置的全局?jǐn)r截器
    interceptors.addAll(client.interceptors());
    //2.添加失敗和重定向處理的攔截器
    interceptors.add(retryAndFollowUpInterceptor);
    //3.對(duì)請(qǐng)求添加一些必要的Header,接受響應(yīng)時(shí)移除掉一些Header
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
    //4.緩存攔截器莲组,對(duì)請(qǐng)求前本地緩存查詢(xún)和攔截诊胞,對(duì)請(qǐng)求結(jié)果進(jìn)行本地緩存
    interceptors.add(new CacheInterceptor(client.internalCache()));
    //5.連接攔截器,負(fù)責(zé)請(qǐng)求和服務(wù)器建立連接
    interceptors.add(new ConnectInterceptor(client));
    //不是WebSocket請(qǐng)求锹杈,添加Client中我們添加的攔截器撵孤,不是全局的迈着,對(duì)WebSocket無(wú)效的攔截器
    if (!forWebSocket) {
        interceptors.addAll(client.networkInterceptors());
    }
    //真正請(qǐng)求服務(wù)器,獲取響應(yīng)的攔截器
    interceptors.add(new CallServerInterceptor(forWebSocket));
    //創(chuàng)建攔截器鏈
    Interceptor.Chain chain = new RealInterceptorChain(interceptors, null, null, null, 0,
            originalRequest, this, eventListener, client.connectTimeoutMillis(),
            client.readTimeoutMillis(), client.writeTimeoutMillis());
    //開(kāi)始責(zé)任鏈遍歷
    return chain.proceed(originalRequest);
}
  • Interceptor攔截器接口邪码,主要是intercept()方法裕菠,每種攔截器處理請(qǐng)求Request對(duì)象都在這個(gè)intercept()方法中處理。
/**
 * 攔截器接口
 */
public interface Interceptor {
    /**
     * 攔截回調(diào)方法
     *
     * @param chain 攔截器鏈
     */
    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();

        Call call();

        int connectTimeoutMillis();

        Chain withConnectTimeout(int timeout, TimeUnit unit);

        int readTimeoutMillis();

        Chain withReadTimeout(int timeout, TimeUnit unit);

        int writeTimeoutMillis();

        Chain withWriteTimeout(int timeout, TimeUnit unit);
    }
}
  • RealInterceptorChain闭专,實(shí)現(xiàn)了Interceptor.Chain接口奴潘,接下來(lái)看proceed()方法

    1. calls變量自增計(jì)數(shù),記錄這個(gè)攔截器被調(diào)用proceed()方法的次數(shù)喻圃。
    2. index變量代表自身攔截器在攔截器列表集合中的角標(biāo)位置萤彩,通過(guò)index + 1獲取下一個(gè)攔截器粪滤,調(diào)用它的proceed()方法斧拍,對(duì)Request對(duì)象進(jìn)行處理。
    3. 重新創(chuàng)建一個(gè)RealInterceptorChain杖小,將攔截器列表肆汹、下一個(gè)攔截器角標(biāo)位置等其他配置傳入,繼續(xù)調(diào)用interceptor攔截器的next()方法繼續(xù)分發(fā)Request對(duì)象予权,并返回一個(gè)Response請(qǐng)求結(jié)果對(duì)象昂勉,并返回出去。
/**
 * 攔截器鏈扫腺,管理所有攔截器
 */
public final class RealInterceptorChain implements Interceptor.Chain {
    /**
     * 攔截器列表
     */
    private final List<Interceptor> interceptors;
    private final StreamAllocation streamAllocation;
    /**
     * Http版本處理岗照,Http1和Http2
     */
    private final HttpCodec httpCodec;
    private final RealConnection connection;
    /**
     * 當(dāng)前遍歷到的攔截器角標(biāo),每次自增獲取下一個(gè)攔截器
     */
    private final int index;
    /**
     * 請(qǐng)求配置對(duì)象笆环,給每個(gè)攔截器處理
     */
    private final Request request;
    /**
     * 請(qǐng)求對(duì)象
     */
    private final Call call;
    /**
     * 事件回調(diào)對(duì)象
     */
    private final EventListener eventListener;
    /**
     * 連接超時(shí)事件
     */
    private final int connectTimeout;
    /**
     * 讀超時(shí)事件
     */
    private final int readTimeout;
    /**
     * 寫(xiě)超時(shí)事件
     */
    private final int writeTimeout;
    /**
     * 已調(diào)用的攔截器數(shù)量
     */
    private int calls;

    public RealInterceptorChain(List<Interceptor> interceptors, StreamAllocation streamAllocation,
                                HttpCodec httpCodec, RealConnection connection, int index, Request request, Call call,
                                EventListener eventListener, int connectTimeout, int readTimeout, int writeTimeout) {
        this.interceptors = interceptors;
        this.connection = connection;
        this.streamAllocation = streamAllocation;
        this.httpCodec = httpCodec;
        this.index = index;
        this.request = request;
        this.call = call;
        this.eventListener = eventListener;
        this.connectTimeout = connectTimeout;
        this.readTimeout = readTimeout;
        this.writeTimeout = writeTimeout;
    }

    //...省略其他方法

    /**
     * 請(qǐng)求處理
     *
     * @param request 請(qǐng)求對(duì)象
     * @return 請(qǐng)求結(jié)果
     */
    @Override
    public Response proceed(Request request) throws IOException {
        return proceed(request, streamAllocation, httpCodec, connection);
    }

    /**
     * 遍歷攔截器進(jìn)行請(qǐng)求處理攒至,請(qǐng)求處理
     *
     * @param request 請(qǐng)求對(duì)象
     */
    public Response proceed(Request request, StreamAllocation streamAllocation, HttpCodec httpCodec,
                            RealConnection connection) throws IOException {
        //超出界限拋出異常
        if (index >= interceptors.size()) {
            throw new AssertionError();
        }
        //記錄調(diào)用過(guò)的攔截器數(shù)量
        calls++;
        // If we already have a stream, confirm that the incoming request will use it.
        if (this.httpCodec != null && !this.connection.supportsUrl(request.url())) {
            throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
                    + " must retain the same host and port");
        }
        // If we already have a stream, confirm that this is the only call to chain.proceed().
        if (this.httpCodec != null && calls > 1) {
            throw new IllegalStateException("network interceptor " + interceptors.get(index - 1)
                    + " must call proceed() exactly once");
        }
        //創(chuàng)建一個(gè)新的攔截器鏈
        RealInterceptorChain next = new RealInterceptorChain(interceptors, streamAllocation, httpCodec,
                connection, index + 1, request, call, eventListener, connectTimeout, readTimeout,
                writeTimeout);
        //位置自增,index + 1躁劣,獲取下一個(gè)攔截器
        Interceptor interceptor = interceptors.get(index);
        //調(diào)用下一個(gè)攔截器處理請(qǐng)求迫吐,獲取處理結(jié)果
        Response response = interceptor.intercept(next);
        // Confirm that the next interceptor made its required call to chain.proceed().
        if (httpCodec != null && index + 1 < interceptors.size() && next.calls != 1) {
            throw new IllegalStateException("network interceptor " + interceptor
                    + " must call proceed() exactly once");
        }
        // Confirm that the intercepted response isn't null.
        if (response == null) {
            throw new NullPointerException("interceptor " + interceptor + " returned null");
        }
        if (response.body() == null) {
            throw new IllegalStateException(
                    "interceptor " + interceptor + " returned a response with no body");
        }
        return response;
    }
}

異步請(qǐng)求流程分析

  • 創(chuàng)建Request請(qǐng)求配置類(lèi),調(diào)用client的newCall(request)方法創(chuàng)建Call對(duì)象账忘,調(diào)用Call對(duì)象的enqueue()將請(qǐng)求加入請(qǐng)求隊(duì)列志膀,enqueue(callback)需要傳入Callback回調(diào)對(duì)象。
OkHttpClient client = new OkHttpClient();
//創(chuàng)建請(qǐng)求配置類(lèi)Request
Request request = 
    new Request.Builder()
    .url(url)
    .build();
//newCall創(chuàng)建請(qǐng)求對(duì)象Call鳖擒,再通過(guò)enqueue()將請(qǐng)求加入到請(qǐng)求隊(duì)列
Response response = client.newCall(request).enqueue(new Callback() {
    @Override 
    public void onFailure(Call call, IOException e) {
        e.printStackTrace();
        //請(qǐng)求失敗
    }

    @Override 
    public void onResponse(Call call, Response response) throws IOException {
        //請(qǐng)求成功溉浙,請(qǐng)求響應(yīng)保存再Response中
    }
);
  • enqueue(callback)方法流程
    1. 判斷executed標(biāo)志,不允許多次執(zhí)行
    2. 獲取client的dispatcher請(qǐng)求分發(fā)器蒋荚,調(diào)用enqueue()戳稽,新建一個(gè)AsyncCall()包裝callback回調(diào)對(duì)象。
/**
 * 異步執(zhí)行圆裕,將請(qǐng)求交給分發(fā)器隊(duì)列處理
 *
 * @param responseCallback 請(qǐng)求的回調(diào)對(duì)象
 */
@Override
public void enqueue(Callback responseCallback) {
    synchronized (this) {
        //同樣判斷是否執(zhí)行過(guò)了广鳍,不允許多次執(zhí)行
        if (executed) {
            throw new IllegalStateException("Already Executed");
        }
        //標(biāo)記
        executed = true;
    }
    captureCallStackTrace();
    //回調(diào)事件回調(diào)對(duì)象
    eventListener.callStart(this);
    //將請(qǐng)求交給分發(fā)器入隊(duì)
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
}
  • 先來(lái)看下dispatcher請(qǐng)求分發(fā)器

    1. Dispatcher內(nèi)部有3個(gè)ArrayDeque雙端隊(duì)列荆几,分別為

      • 準(zhǔn)備執(zhí)行異步任務(wù)隊(duì)列readyAsyncCalls
      • 正在執(zhí)行異步任務(wù)隊(duì)列runningAsyncCalls
      • runningSyncCalls同步請(qǐng)求任務(wù)的隊(duì)列
    2. enqueue()方法,將包裝了callback的AsyncCall對(duì)象按規(guī)則處理添加不同的隊(duì)列

      • runningAsyncCalls正在運(yùn)行的異步任務(wù)的隊(duì)列小于最大請(qǐng)求數(shù)
      • runningCallsForHost赊时,異步任務(wù)的host主機(jī)沒(méi)有同時(shí)5個(gè)在允許
    3. 上面2個(gè)條件

      • 滿(mǎn)足條件,將任務(wù)添加到(正在執(zhí)行異步任務(wù)隊(duì)列)runningAsyncCalls竭缝,調(diào)用executorService()獲取線程池執(zhí)行器,將任務(wù)提交給執(zhí)行器馬上執(zhí)行
      • 不滿(mǎn)足條件,將任務(wù)添加到(準(zhǔn)備執(zhí)行異步任務(wù)隊(duì)列)readyAsyncCalls,等待任務(wù)被執(zhí)行
public final class Dispatcher {
    /**
     * 準(zhǔn)備執(zhí)行異步任務(wù)的隊(duì)列
     */
    private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();

    /**
     * 正在執(zhí)行異步任務(wù)的隊(duì)列
     */
    private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();

    /**
     * 同步請(qǐng)求任務(wù)的隊(duì)列
     */
    private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();
    
    /**
     * 分發(fā)器將異步任務(wù)入隊(duì)
     *
     * @param call 異步請(qǐng)求對(duì)象
     */
    synchronized void enqueue(AsyncCall call) {
        //入隊(duì)前限制規(guī)則:
        //1、runningAsyncCalls正在運(yùn)行的異步任務(wù)的隊(duì)列小于最大請(qǐng)求數(shù)
        //2、runningCallsForHost槐秧,異步任務(wù)的host主機(jī)沒(méi)有同時(shí)5個(gè)在允許
        //3、如果上面2個(gè)條件都滿(mǎn)足,那么馬上將任務(wù)排到允許任務(wù)隊(duì)列刘陶,并且馬上執(zhí)行
        if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
            //任務(wù)添加到正在執(zhí)行異步任務(wù)的隊(duì)列
            runningAsyncCalls.add(call);
            //獲取線程池纷责,馬上執(zhí)行曲横,執(zhí)行到AsyncCall時(shí)胳喷,會(huì)回調(diào)AsyncCall的execute()方法尊惰!
            executorService().execute(call);
        } else {
            //不滿(mǎn)足條件鞋诗,添加到準(zhǔn)備執(zhí)行異步任務(wù)的隊(duì)列中
            readyAsyncCalls.add(call);
        }
    }
}
  • 接下來(lái)來(lái)分析,AsyncCall類(lèi)

    1. AsyncCall繼承NamedRunnable類(lèi)
    2. execute()方法為異步執(zhí)行時(shí)調(diào)用的方法雁刷,和同步一樣炮障,調(diào)用getResponseWithInterceptorChain()責(zé)任鏈方式遍歷攔截器處理Request對(duì)象徘键,返回請(qǐng)求結(jié)果Response它呀。
    3. 通過(guò)retryAndFollowUpInterceptor.isCanceled()處理任務(wù)被取消的情況谓媒,取消則調(diào)用callback.onFailure()表示請(qǐng)求失敗抢野。
    4. 沒(méi)有被取消,則為成功扁瓢,調(diào)用callback.onResponse()表示請(qǐng)求成功详恼。
    5. try-catch處理拋出異常的情況,同樣調(diào)用callback.onFailure()表示請(qǐng)求失敗引几。
    6. 最后finall塊昧互,調(diào)用分發(fā)器dispatcher的finished方法挽铁,表示任務(wù)完成,繼續(xù)執(zhí)行下一個(gè)任務(wù)敞掘。
/**
 * 異步請(qǐng)求叽掘,繼承NamedRunnable類(lèi),NamedRunnable類(lèi)實(shí)現(xiàn)了Runnable接口
 */
final class AsyncCall extends NamedRunnable {
    private final Callback responseCallback;

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

    //省略一些方法...

    @Override
    protected void execute() {
        boolean signalledCallback = false;
        try {
            //和同步一樣玖雁,被線程池執(zhí)行時(shí)更扁,進(jìn)行責(zé)任鏈處理請(qǐng)求,獲取請(qǐng)求結(jié)果
            Response response = getResponseWithInterceptorChain();
            //處理被取消的情況
            if (retryAndFollowUpInterceptor.isCanceled()) {
                signalledCallback = true;
                //回調(diào)回調(diào)接口對(duì)象
                responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
            } else {
                //請(qǐng)求成功赫冬,回調(diào)回調(diào)對(duì)象
                signalledCallback = true;
                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)事件回調(diào)接口
                eventListener.callFailed(RealCall.this, e);
                //回調(diào)接口回調(diào)為失敗
                responseCallback.onFailure(RealCall.this, e);
            }
        } finally {
            //結(jié)束請(qǐng)求浓镜,調(diào)用分發(fā)器,讓分發(fā)器繼續(xù)執(zhí)行下一個(gè)任務(wù)
            client.dispatcher().finished(this);
        }
    }
}
  • 分析任務(wù)結(jié)束調(diào)用的finished()

    1. finished()方法有2個(gè)
      • finished(AsyncCall)劲厌,異步任務(wù)結(jié)束后調(diào)用膛薛。
      • finished(RealCal)同步任務(wù)結(jié)束后調(diào)用。
    2. 2個(gè)finished()都會(huì)調(diào)用到第三個(gè)參數(shù)的finished补鼻。
      • calls哄啄,任務(wù)隊(duì)列,異步任務(wù)傳runningAsyncCalls隊(duì)列风范,同步任務(wù)傳runningSyncCalls隊(duì)列咨跌,
      • call,任務(wù)類(lèi)硼婿,異步任務(wù)為AsyncCall锌半,同步任務(wù)為RealCal。
      • promoteCalls加酵,代表是否推動(dòng)下一個(gè)異步任務(wù)執(zhí)行拳喻,同步任務(wù)執(zhí)行完就完了,沒(méi)有下一個(gè)所以為false猪腕,異步任務(wù)執(zhí)行完會(huì)有執(zhí)行下一個(gè)異步任務(wù),為true钦勘。
    3. 先從隊(duì)列中移除任務(wù)(已經(jīng)已經(jīng)完成了)陋葡,再調(diào)用promoteCalls()推動(dòng)下一個(gè)任務(wù)執(zhí)行。
    4. 獲取同步任務(wù)和異步任務(wù)的數(shù)量彻采,都為0腐缤,則回調(diào)idleCallback表示任務(wù)閑置。
/**
 * 異步任務(wù)執(zhí)行結(jié)束肛响,會(huì)推動(dòng)下一個(gè)異步任務(wù)執(zhí)行岭粤,因?yàn)榭赡苡械却漠惒饺蝿?wù)在隊(duì)列中等待
 *
 * @param call 異步請(qǐng)求對(duì)象
 */
void finished(AsyncCall call) {
    finished(runningAsyncCalls, call, true);
}

/**
 * 同步任務(wù)執(zhí)行結(jié)束,同步任務(wù)沒(méi)有等待隊(duì)列特笋,所以執(zhí)行完就完了
 */
void finished(RealCall call) {
    finished(runningSyncCalls, call, false);
}

/**
 * 任務(wù)結(jié)束處理
 *
 * @param calls        任務(wù)隊(duì)列
 * @param call         異步任務(wù)
 * @param promoteCalls 是否推動(dòng)下一個(gè)異步任務(wù)執(zhí)行
 */
private <T> void finished(Deque<T> calls, T call, boolean promoteCalls) {
    int runningCallsCount;
    Runnable idleCallback;
    synchronized (this) {
        //從隊(duì)列中移除異步請(qǐng)求
        if (!calls.remove(call)) {
            throw new AssertionError("Call wasn't in-flight!");
        }
        //推動(dòng)下一個(gè)異步任務(wù)執(zhí)行
        if (promoteCalls) {
            promoteCalls();
        }
        //獲取正在運(yùn)行的異步任務(wù)和同步任務(wù)的總數(shù)量
        runningCallsCount = runningCallsCount();
        idleCallback = this.idleCallback;
    }
    //正在運(yùn)行的異步任務(wù)和同步任務(wù)的數(shù)量之和為0剃浇,則回調(diào)空閑回調(diào)
    if (runningCallsCount == 0 && idleCallback != null) {
        idleCallback.run();
    }
}
  • 分析promoteCalls()方法。由于只有異步任務(wù)才會(huì)調(diào)用promoteCalls(),所以都是異步任務(wù)的處理
    1. 先判斷runningAsyncCalls執(zhí)行中的異步任務(wù)隊(duì)列的任務(wù)是否已滿(mǎn)虎囚。滿(mǎn)了則不繼續(xù)角塑,等待其他任務(wù)執(zhí)行完來(lái)再來(lái)帶動(dòng)任務(wù)執(zhí)行。
    2. 再判斷任務(wù)隊(duì)列是否為空淘讥,沒(méi)有任務(wù)則執(zhí)行圃伶,這種情況在最后一個(gè)任務(wù)執(zhí)行完畢后會(huì)出現(xiàn)。
    3. 遍歷異步任務(wù)隊(duì)列蒲列,獲取每個(gè)異步對(duì)象
    4. 判斷這個(gè)異步任務(wù)的host窒朋,在當(dāng)前請(qǐng)求的隊(duì)列中,是否小于5個(gè)正在請(qǐng)求蝗岖,小于則開(kāi)始執(zhí)行下一個(gè)異步任務(wù)
    5. 如果滿(mǎn)足則將任務(wù)添加到runningAsyncCalls正在執(zhí)行異步任務(wù)的隊(duì)列中侥猩,讓執(zhí)行器馬上執(zhí)行
/**
 * 推動(dòng)下一個(gè)異步任務(wù)執(zhí)行
 */
private void promoteCalls() {
    //正在運(yùn)行的異步任務(wù)隊(duì)列滿(mǎn)了,不開(kāi)始下一個(gè)任務(wù)剪侮,跳出
    if (runningAsyncCalls.size() >= maxRequests) {
        return; // Already running max capacity.
    }
    //沒(méi)有任務(wù)執(zhí)行拭宁,跳出
    if (readyAsyncCalls.isEmpty()) {
        return; // No ready calls to promote.
    }
    //遍歷準(zhǔn)備開(kāi)始異步任務(wù)的隊(duì)列
    for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
        //下一個(gè)異步任務(wù)
        AsyncCall call = i.next();
        //判斷這個(gè)異步任務(wù)的host,在當(dāng)前請(qǐng)求的隊(duì)列中瓣俯,是否小于5個(gè)正在請(qǐng)求杰标,小于則開(kāi)始執(zhí)行下一個(gè)異步任務(wù)
        if (runningCallsForHost(call) < maxRequestsPerHost) {
            //從準(zhǔn)備執(zhí)行異步任務(wù)隊(duì)列中移除任務(wù)
            i.remove();
            //將任務(wù)添加到正在執(zhí)行的異步任務(wù)
            runningAsyncCalls.add(call);
            //將任務(wù)交給線程池執(zhí)行
            executorService().execute(call);
        }
        //判斷當(dāng)前運(yùn)行的任務(wù)隊(duì)列是否已滿(mǎn)
        if (runningAsyncCalls.size() >= maxRequests) {
            return; // Reached max capacity.
        }
    }
}
  • 最后來(lái)看下NamedRunnable類(lèi)
    1. NamedRunnable實(shí)現(xiàn)了Runnable接口,所以他可以被線程池執(zhí)行器執(zhí)行彩匕。
    2. 使用模板模式腔剂,復(fù)寫(xiě)run()方法,調(diào)用定義的抽象execute()執(zhí)行方法驼仪,讓子類(lèi)重寫(xiě)掸犬,同時(shí)將run()方法設(shè)置為final防止被重寫(xiě)流程。
    3. 統(tǒng)一設(shè)置線程名稱(chēng)绪爸。
/**
 * 實(shí)現(xiàn)了Runnable接口湾碎,給每個(gè)異步任務(wù)定義線程名
 */
public abstract class NamedRunnable implements Runnable {
    protected final String name;

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

    /**
     * 任務(wù)run()方法,為被子線程執(zhí)行的回調(diào)方法奠货,設(shè)置為final介褥,不允許子類(lèi)復(fù)寫(xiě)修改,并提供execute抽象方法递惋,模板模式處理柔滔,定義統(tǒng)一行為
     */
    @Override
    public final void run() {
        //配置線程名
        String oldName = Thread.currentThread().getName();
        Thread.currentThread().setName(name);
        try {
            //調(diào)用定義execute抽象方法
            execute();
        } finally {
            Thread.currentThread().setName(oldName);
        }
    }

    /**
     * 將run()方法轉(zhuǎn)化為抽象的execute給子類(lèi)復(fù)寫(xiě),執(zhí)行異步任務(wù)
     */
    protected abstract void execute();
}

攔截器分析

主要分析的攔截器:

  1. BridgeInterceptor:對(duì)請(qǐng)求添加一些必要的Header萍虽,接受響應(yīng)時(shí)移除掉一些Header的攔截器
  2. CacheInterceptor:緩存攔截器睛廊,對(duì)請(qǐng)求前本地緩存查詢(xún)和攔截,對(duì)請(qǐng)求結(jié)果進(jìn)行本地緩存
  3. ConnectInterceptor:連接攔截器杉编,負(fù)責(zé)請(qǐng)求和服務(wù)器建立連接
  • BridgeInterceptor超全,對(duì)請(qǐng)求添加一些必要的Header咆霜,接受響應(yīng)時(shí)移除掉一些Header的攔截器
public final class BridgeInterceptor implements Interceptor {
    private final CookieJar cookieJar;

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

    /**
     * 將配置的Request對(duì)象中的Header配置整理一遍重新創(chuàng)建一個(gè)Request
     *
     * @param chain 攔截器鏈
     * @return 請(qǐng)求結(jié)果
     */
    @Override
    public Response intercept(Chain chain) throws IOException {
        //獲取原始配置
        Request userRequest = chain.request();
        //新建Request配置
        Request.Builder requestBuilder = userRequest.newBuilder();
        //獲取Body
        RequestBody body = userRequest.body();
        if (body != null) {
            MediaType contentType = body.contentType();
            if (contentType != null) {
                requestBuilder.header("Content-Type", contentType.toString());
            }

            long contentLength = body.contentLength();
            if (contentLength != -1) {
                requestBuilder.header("Content-Length", Long.toString(contentLength));
                requestBuilder.removeHeader("Transfer-Encoding");
            } else {
                requestBuilder.header("Transfer-Encoding", "chunked");
                requestBuilder.removeHeader("Content-Length");
            }
        }
        //配置默認(rèn)Host
        if (userRequest.header("Host") == null) {
            requestBuilder.header("Host", hostHeader(userRequest.url(), false));
        }
        //配置默認(rèn)Connection為保持連接
        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.
        //添加透明的Gzip壓縮
        boolean transparentGzip = false;
        if (userRequest.header("Accept-Encoding") == null && userRequest.header("Range") == null) {
            transparentGzip = true;
            requestBuilder.header("Accept-Encoding", "gzip");
        }
        //如果有配置Cookie,添加Cookie到Header
        List<Cookie> cookies = cookieJar.loadForRequest(userRequest.url());
        if (!cookies.isEmpty()) {
            requestBuilder.header("Cookie", cookieHeader(cookies));
        }
        //配置默認(rèn)的UA
        if (userRequest.header("User-Agent") == null) {
            requestBuilder.header("User-Agent", Version.userAgent());
        }
        //生成新的Request卵迂,執(zhí)行下一個(gè)攔截器獲取響應(yīng)結(jié)果
        Response networkResponse = chain.proceed(requestBuilder.build());
        //處理請(qǐng)求頭
        HttpHeaders.receiveHeaders(cookieJar, userRequest.url(), networkResponse.headers());
        //重新生成響應(yīng)配置
        Response.Builder responseBuilder = networkResponse.newBuilder()
                .request(userRequest);
        //響應(yīng)報(bào)文裕便,移除一些Header,壓縮響應(yīng)報(bào)文
        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);
            String contentType = networkResponse.header("Content-Type");
            responseBuilder.body(new RealResponseBody(contentType, -1L, Okio.buffer(responseBody)));
        }
        //重新生成響應(yīng)報(bào)文
        return responseBuilder.build();
    }

    /**
     * Returns a 'Cookie' HTTP request header with all cookies, like {@code a=b; c=d}.
     */
    private String cookieHeader(List<Cookie> cookies) {
        StringBuilder cookieHeader = new StringBuilder();
        for (int i = 0, size = cookies.size(); i < size; i++) {
            if (i > 0) {
                cookieHeader.append("; ");
            }
            Cookie cookie = cookies.get(i);
            cookieHeader.append(cookie.name()).append('=').append(cookie.value());
        }
        return cookieHeader.toString();
    }
}
  • CacheInterceptor见咒,緩存攔截器偿衰,對(duì)請(qǐng)求前本地緩存查詢(xún)和攔截,對(duì)請(qǐng)求結(jié)果進(jìn)行本地緩存
public final class CacheInterceptor implements Interceptor {
    final InternalCache cache;

    public CacheInterceptor(InternalCache cache) {
        this.cache = cache;
    }

    /**
     * 緩存攔截器
     *
     * @param chain 攔截器鏈
     * @return 請(qǐng)求結(jié)果
     */
    @Override
    public Response intercept(Chain chain) throws IOException {
        //獲取緩存容器中請(qǐng)求的緩存改览,候選緩存
        Response cacheCandidate = cache != null
                ? cache.get(chain.request())
                : null;
        long now = System.currentTimeMillis();
        //緩存策略工廠下翎,傳入網(wǎng)絡(luò)請(qǐng)求的Request和緩存的響應(yīng),決定緩存策略
        CacheStrategy strategy = new CacheStrategy.Factory(now, chain.request(), cacheCandidate).get();
        Request networkRequest = strategy.networkRequest;
        Response cacheResponse = strategy.cacheResponse;
        if (cache != null) {
            cache.trackResponse(strategy);
        }
        if (cacheCandidate != null && cacheResponse == null) {
            closeQuietly(cacheCandidate.body()); // The cache candidate wasn't applicable. Close it.
        }
        //請(qǐng)求失敗宝当、緩存也失敗视事,返回失敗的結(jié)果
        if (networkRequest == null && cacheResponse == null) {
            return new Response.Builder()
                    .request(chain.request())
                    .protocol(Protocol.HTTP_1_1)
                    .code(504)
                    .message("Unsatisfiable Request (only-if-cached)")
                    .body(Util.EMPTY_RESPONSE)
                    .sentRequestAtMillis(-1L)
                    .receivedResponseAtMillis(System.currentTimeMillis())
                    .build();
        }
        //緩存不為空,網(wǎng)絡(luò)請(qǐng)求也不需要庆揩,則我們緩存命中
        if (networkRequest == null) {
            return cacheResponse.newBuilder()
                    .cacheResponse(stripBody(cacheResponse))
                    .build();
        }
        //緩存不命中俐东,要請(qǐng)求網(wǎng)絡(luò),調(diào)用下一個(gè)攔截器去請(qǐng)求订晌,并獲取結(jié)果
        Response networkResponse = null;
        try {
            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());
            }
        }
        //本地緩存是存在的虏辫,將本地緩存和網(wǎng)絡(luò)請(qǐng)求結(jié)果比較,決定是否將這個(gè)網(wǎng)絡(luò)請(qǐng)求結(jié)果緩存到本地
        if (cacheResponse != null) {
            if (networkResponse.code() == HTTP_NOT_MODIFIED) {
                Response response = cacheResponse.newBuilder()
                        .headers(combine(cacheResponse.headers(), networkResponse.headers()))
                        .sentRequestAtMillis(networkResponse.sentRequestAtMillis())
                        .receivedResponseAtMillis(networkResponse.receivedResponseAtMillis())
                        .cacheResponse(stripBody(cacheResponse))
                        .networkResponse(stripBody(networkResponse))
                        .build();
                networkResponse.body().close();

                // Update the cache after combining headers but before stripping the
                // Content-Encoding header (as performed by initContentStream()).
                cache.trackConditionalCacheHit();
                //以本次網(wǎng)絡(luò)請(qǐng)求結(jié)果锈拨,更新本地緩存
                cache.update(cacheResponse, response);
                return response;
            } else {
                closeQuietly(cacheResponse.body());
            }
        }
        //重新構(gòu)造輸出結(jié)果
        Response response = networkResponse.newBuilder()
                .cacheResponse(stripBody(cacheResponse))
                .networkResponse(stripBody(networkResponse))
                .build();

        if (cache != null) {
            if (HttpHeaders.hasBody(response) && CacheStrategy.isCacheable(response, networkRequest)) {
                //本地緩存不存在砌庄,保存本次網(wǎng)絡(luò)請(qǐng)求的緩存到本地
                CacheRequest cacheRequest = cache.put(response);
                return cacheWritingResponse(cacheRequest, response);
            }
            //如果緩存是無(wú)效的,則移除緩存
            if (HttpMethod.invalidatesCache(networkRequest.method())) {
                try {
                    cache.remove(networkRequest);
                } catch (IOException ignored) {
                    // The cache cannot be written.
                }
            }
        }
        return response;
    }
    
    //...省略其他代碼
}
  • ConnectInterceptor奕枢,連接攔截器娄昆,負(fù)責(zé)請(qǐng)求和服務(wù)器建立連接
public final class ConnectInterceptor implements Interceptor {
    public final OkHttpClient client;

    public ConnectInterceptor(OkHttpClient client) {
        this.client = client;
    }

    @Override
    public Response intercept(Chain chain) throws IOException {
        RealInterceptorChain realChain = (RealInterceptorChain) chain;
        Request request = realChain.request();
        //StreamAllocation將連接創(chuàng)建、連接管理的封裝類(lèi)
        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是對(duì) HTTP 協(xié)議操作的抽象缝彬,有兩個(gè)實(shí)現(xiàn):Http1Codec和Http2Codec萌焰,顧名思義,它們分別對(duì)應(yīng) HTTP/1.1 和 HTTP/2 版本的實(shí)現(xiàn)谷浅。在這個(gè)方法的內(nèi)部實(shí)現(xiàn)連接池的復(fù)用處理
        //開(kāi)始連接
        HttpCodec httpCodec = streamAllocation.newStream(client, chain, doExtensiveHealthChecks);
        RealConnection connection = streamAllocation.connection();
        //調(diào)用下一個(gè)攔截器
        return realChain.proceed(request, streamAllocation, httpCodec, connection);
    }
}

總結(jié)

本篇分析了OkHttp3的請(qǐng)求流程和攔截器杆怕,作為最為核心的就是攔截器部分,將每部分的附加和處理都分類(lèi)到不同的攔截器進(jìn)行解耦壳贪。

  • 異步流程通過(guò)Dispatch分發(fā)器將任務(wù)提交給線程池,每個(gè)任務(wù)執(zhí)行完后通知分發(fā)器寝杖,分發(fā)器再繼續(xù)尋找下一個(gè)異步任務(wù)進(jìn)行執(zhí)行违施。

  • 橋接攔截器對(duì)Request做Header補(bǔ)充,并且自動(dòng)處理Cookie瑟幕。

  • 緩存攔截器磕蒲,先對(duì)Request對(duì)象從本地獲取緩存留潦,緩存可用時(shí)則使用,否則調(diào)用下一個(gè)攔截器進(jìn)行請(qǐng)求辣往,請(qǐng)求成功后返回兔院,獲取到結(jié)果后,再緩存到本地站削。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末坊萝,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子许起,更是在濱河造成了極大的恐慌十偶,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,427評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件园细,死亡現(xiàn)場(chǎng)離奇詭異惦积,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)猛频,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén)狮崩,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人鹿寻,你說(shuō)我怎么就攤上這事睦柴。” “怎么了烈和?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,747評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵爱只,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我招刹,道長(zhǎng)恬试,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,939評(píng)論 1 295
  • 正文 為了忘掉前任疯暑,我火速辦了婚禮训柴,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘妇拯。我一直安慰自己甸私,他們只是感情好曼追,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,955評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般家破。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上柬批,一...
    開(kāi)封第一講書(shū)人閱讀 51,737評(píng)論 1 305
  • 那天握截,我揣著相機(jī)與錄音,去河邊找鬼丹弱。 笑死德撬,一個(gè)胖子當(dāng)著我的面吹牛铲咨,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蜓洪,決...
    沈念sama閱讀 40,448評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼纤勒,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了隆檀?” 一聲冷哼從身側(cè)響起摇天,我...
    開(kāi)封第一講書(shū)人閱讀 39,352評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎刚操,沒(méi)想到半個(gè)月后闸翅,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,834評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡菊霜,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,992評(píng)論 3 338
  • 正文 我和宋清朗相戀三年坚冀,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鉴逞。...
    茶點(diǎn)故事閱讀 40,133評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡记某,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出构捡,到底是詐尸還是另有隱情液南,我是刑警寧澤,帶...
    沈念sama閱讀 35,815評(píng)論 5 346
  • 正文 年R本政府宣布勾徽,位于F島的核電站滑凉,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏喘帚。R本人自食惡果不足惜畅姊,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,477評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望吹由。 院中可真熱鬧若未,春花似錦、人聲如沸倾鲫。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,022評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)乌昔。三九已至隙疚,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間磕道,已是汗流浹背甚淡。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,147評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人贯卦。 一個(gè)月前我還...
    沈念sama閱讀 48,398評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像焙贷,于是被迫代替她去往敵國(guó)和親撵割。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,077評(píng)論 2 355