盤它锅睛,okhttp3吊炸天源碼分析(一):同步、異步請求源碼解析

本篇文章已授權(quán)微信公眾號guolin_blog(郭霖)獨家發(fā)布

okhttp一經(jīng)推出历谍,讓其他的網(wǎng)絡(luò)請求框架變得黯然失色现拒。網(wǎng)上關(guān)于okhttp的介紹文章一大堆,這里我就不繼續(xù)bb了扮饶,今天我嘗試從源碼的角度去分析一下okhttp的一個工作流程具练。其實在動筆寫這篇文章之前,我已經(jīng)在網(wǎng)上看過不少關(guān)于okhttp源碼分析的文章甜无,怎么說呢扛点,有些文章,一上來就是一個大總結(jié)或者一個UML圖岂丘,讓人看得晦澀難懂陵究。對于大部分沒有閱讀過okhttp源碼的人來說,全盤托出的一上來一大堆接口啊類啊什么的奥帘,容易把人整暈铜邮。今天我嘗試用通俗易懂的語言來描述一下同步、異步的操作流程寨蹋,以及okhttp背后到底干了啥松蒜。

先貼一下 okhttp的github地址:okhttp項目地址。最新的源碼是3.0+已旧,而3.0與舊版本多少還是有些差別的秸苗,這里我就不介紹了,感興趣的話自行去百度运褪。

同步請求

 //  構(gòu)建okHttpClient惊楼,相當于請求的客戶端,Builder設(shè)計模式
 OkHttpClient okHttpClient = new OkHttpClient.Builder().readTimeout(5, TimeUnit.SECONDS).build();
        // 構(gòu)建一個請求體秸讹,同樣也是Builder設(shè)計模式
        Request request = new Request.Builder().url("http://www.baidu.com").build();
        //  生成一個Call對象檀咙,該對象是接口類型,后面會說
        Call call = okHttpClient.newCall(request);
        try {
            //  拿到Response
            Response response = call.execute();
            Log.i("TAG",response.body().string());
        } catch (IOException e) {
            
        }

以上就是一個簡單的同步請求示例代碼璃诀,我們來看一看要經(jīng)過哪些步驟:

1.通過Builder模式創(chuàng)建OkHttpClient對象和Request對象
2.調(diào)用OkHttpClient的newCall方法弧可,獲取一個Call對象,參數(shù)是Request
3.調(diào)用execute方法獲取一個Respone

(一) OkHttpClient源碼分析

public static final class Builder {
    Dispatcher dispatcher;
    ...
    ...
    public Builder() {
      dispatcher = new Dispatcher();
      protocols = DEFAULT_PROTOCOLS;
      connectionSpecs = DEFAULT_CONNECTION_SPECS;
      eventListenerFactory = EventListener.factory(EventListener.NONE);
      proxySelector = ProxySelector.getDefault();
      if (proxySelector == null) {
        proxySelector = new NullProxySelector();
      }
      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;
      callTimeout = 0;
      connectTimeout = 10_000;
      readTimeout = 10_000;
      writeTimeout = 10_000;
      pingInterval = 0;
    }
   
}
  

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

Builder是OkHttpClient一個靜態(tài)內(nèi)部類文虏,在Builder的構(gòu)造函數(shù)中進行了一系列的初始化操作侣诺,其中Dispatcher中文是分發(fā)器的意思殖演,和攔截器不同的是分發(fā)器不做事件處理,只做事件流向年鸳。他負責將每一次Requst進行分發(fā)趴久,壓棧到自己的線程池,并通過調(diào)用者自己不同的方式進行異步和同步處理搔确,那具體是怎么操作的呢彼棍?后面會講。ConnectionPool是一個連接池對象膳算,它可以用來管理連接對象座硕,從它的構(gòu)造方法中可以看到連接池的默認空閑連接數(shù)為5個,keepAlive時間為5分鐘涕蜂。

(二) Request源碼分析

public static class Builder {
    @Nullable HttpUrl url;
    String method;
    Headers.Builder headers;
    @Nullable RequestBody body;

    public Builder() {
      this.method = "GET";
      this.headers = new Headers.Builder();
    }
}

Builder()構(gòu)造函數(shù)中設(shè)置了默認的請求方法是GET方法华匾,Request類的build()方法是用來創(chuàng)建一個Request對象,將當前的Builder對象傳進去机隙,并完成了對象的賦值蜘拉。我們來看一下Request的構(gòu)造函數(shù):

Request(Builder builder) {
    this.url = builder.url;
    this.method = builder.method;
    this.headers = builder.headers.build();
    this.body = builder.body;
    this.tags = Util.immutableMap(builder.tags);
  }

將Builder類的相關(guān)屬性賦值給Request的相關(guān)屬性,這也是Builder模式的精髓有鹿。

(三) Call對象的創(chuàng)建:newCall()執(zhí)行分析

@Override public Call newCall(Request request) {
    return RealCall.newRealCall(this, request, false);
  }

可以看到newCall方法內(nèi)部調(diào)用了RealCall的newRealCall方法旭旭,Call是一個接口,RealCall是它的實現(xiàn)類葱跋,接下來我們?nèi)ewRealCall方法內(nèi)部看看

static RealCall newRealCall(OkHttpClient client, Request originalRequest, boolean forWebSocket) {
    // Safely publish the Call instance to the EventListener.
    RealCall call = new RealCall(client, originalRequest, forWebSocket);
    call.eventListener = client.eventListenerFactory().create(call);
    return call;
  }

在newRealCall方法里持寄,完成了RealCall對象的創(chuàng)建,并把它返回出去娱俺。至此稍味,Call對象已經(jīng)創(chuàng)建完畢,實際上創(chuàng)建的對象是Call的實現(xiàn)類RealCall 對象荠卷。

(四) Response對象的創(chuàng)建: call.execute()執(zhí)行分析

前面已經(jīng)說到 Call call = okHttpClient.newCall(request)拿到的是一個RealCall對象仲闽,所以我們直接去RealCall類的execute()方法看它的源碼

@Override public Response execute() throws IOException {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    timeout.enter();
    eventListener.callStart(this);
    try {
      client.dispatcher().executed(this);
      Response result = getResponseWithInterceptorChain();
      if (result == null) throw new IOException("Canceled");
      return result;
    } catch (IOException e) {
      e = timeoutExit(e);
      eventListener.callFailed(this, e);
      throw e;
    } finally {
      client.dispatcher().finished(this);
    }
  }

在同步代碼塊中,首先判斷excuted是不是true僵朗,它的含義是否有在執(zhí)行,如果是屑彻,拋出異常验庙,如果沒有執(zhí)行過,將excuted置為true社牲。eventListener.callStart(this)開啟事件監(jiān)聽,eventListener在RealCall對象創(chuàng)建的時候粪薛,也一起創(chuàng)建了。

接下來我們看看execute()的核心方法: client.dispatcher().executed(this)搏恤、 Response result = getResponseWithInterceptorChain()违寿、client.dispatcher().finished(this)

public Dispatcher dispatcher() {
    return dispatcher;
  }

  synchronized void executed(RealCall call) {
    runningSyncCalls.add(call);
  }

首先調(diào)用了Dispatcher的dispatcher()方法湃交,dispatcher()方法返回一個Dispatcher對象,緊接著調(diào)用了Dispatcher的executed方法藤巢,往runningSyncCalls對象中添加了一個 call對象搞莺,runningSyncCalls是一個存放同步請求的隊列,Dispatcher類中維護了3種類型的請求隊列:

  /** Ready async calls in the order they'll be run. */
  private final Deque<AsyncCall> readyAsyncCalls = new ArrayDeque<>();

  /** Running asynchronous calls. Includes canceled calls that haven't finished yet. */
  private final Deque<AsyncCall> runningAsyncCalls = new ArrayDeque<>();

  /** Running synchronous calls. Includes canceled calls that haven't finished yet. */
  private final Deque<RealCall> runningSyncCalls = new ArrayDeque<>();

  • readyAsyncCalls 是異步請求的就緒隊列
  • runningAsyncCalls 是異步請求的執(zhí)行隊列
  • runningSyncCalls 是同步請求的執(zhí)行隊列

調(diào)用完Dispatcher的execute()方法后掂咒,緊接著調(diào)用了getResponseWithInterceptorChain()方法才沧。

Response getResponseWithInterceptorChain() throws IOException {
    // Build a full stack of interceptors.
    List<Interceptor> interceptors = new ArrayList<>();
    interceptors.addAll(client.interceptors());
    interceptors.add(retryAndFollowUpInterceptor);
    interceptors.add(new BridgeInterceptor(client.cookieJar()));
    interceptors.add(new CacheInterceptor(client.internalCache()));
    interceptors.add(new ConnectInterceptor(client));
    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);
  }

該方法返回一個Response對象,我們可以看到,在方法的第一行中就創(chuàng)建了interceptors 集合绍刮,然后緊接著放進去很多攔截器對象温圆,此處使用了責任鏈設(shè)計模式,依次在攔截器中做相應的操作孩革。

在Response的excute方法的finally模塊中岁歉,最后調(diào)用了 client.dispatcher().finished(this)。我們點進去瞧一瞧

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

finished方法內(nèi)部調(diào)用了它的重載的方法膝蜈,并把同步請求的消息隊列對象和RealCall對象傳過去锅移,我們繼續(xù)往下看。

private <T> void finished(Deque<T> calls, T call) {
    Runnable idleCallback;
    synchronized (this) {
      if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
      idleCallback = this.idleCallback;
    }

    boolean isRunning = promoteAndExecute();

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

在同步代碼快中彬檀,將call對象從同步請求消息隊列中移除帆啃,如果移除出問題,就會拋出異常窍帝。 緊接著調(diào)用了promoteAndExecute方法:

 private boolean promoteAndExecute() {
    assert (!Thread.holdsLock(this));

    List<AsyncCall> executableCalls = new ArrayList<>();
    boolean isRunning;
    synchronized (this) {
      for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
        AsyncCall asyncCall = i.next();

        if (runningAsyncCalls.size() >= maxRequests) break; // Max capacity.
        if (asyncCall.callsPerHost().get() >= maxRequestsPerHost) continue; // Host max capacity.

        i.remove();
        asyncCall.callsPerHost().incrementAndGet();
        executableCalls.add(asyncCall);
        runningAsyncCalls.add(asyncCall);
      }
      isRunning = runningCallsCount() > 0;
    }

    for (int i = 0, size = executableCalls.size(); i < size; i++) {
      AsyncCall asyncCall = executableCalls.get(i);
      asyncCall.executeOn(executorService());
    }

    return isRunning;
  }

同步代碼塊有一個for循環(huán)努潘,去迭代readyAsyncCalls,也就是待準備消息隊列坤学。但是我們從前面一步步分析過來疯坤,并沒有往readyAsyncCalls添加過數(shù)據(jù),所以當前的for循環(huán)并不會執(zhí)行深浮,之后的一個for循環(huán)也不會執(zhí)行压怠,isRunning返回false.

promoteAndExecute()方法返回false。

private <T> void finished(Deque<T> calls, T call) {
    Runnable idleCallback;
    synchronized (this) {
      if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
      idleCallback = this.idleCallback;
    }

    boolean isRunning = promoteAndExecute();

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

并且idleCallback 已經(jīng)完成了初始化飞苇,所以會執(zhí)行 idleCallback的run()方法

總結(jié):通過以上的分析菌瘫,我們發(fā)現(xiàn)在同步請求中Dispatcher主要負責了兩件事,同步請求的保存和移除。
save.png

remove.png

至此布卡,okhttp3的同步請求源碼已經(jīng)分析完了雨让,接下來,我們看看okhttp3的異步請求源碼分析忿等。

異步請求

 //  構(gòu)建okHttpClient栖忠,相當于請求的客戶端,Builder設(shè)計模式
 OkHttpClient okHttpClient = new OkHttpClient.Builder().readTimeout(5, TimeUnit.SECONDS).build();
        // 構(gòu)建一個請求體,同樣也是Builder設(shè)計模式
        Request request = new Request.Builder().url("http://www.baidu.com").build();
        //  生成一個Call對象庵寞,該對象是接口類型狸相,后面會說
        Call call = okHttpClient.newCall(request);
        call.enqueue(new Callback() {
                    @Override
                    public void onFailure(Call call, IOException e) {
                        
                    }

                    @Override
                    public void onResponse(Call call, Response response) throws IOException {

                    }
                });

簡單的看下異步請求的幾個步驟:

1.通過Builder模式創(chuàng)建OkHttpClient對象和Request對象
2.調(diào)用OkHttpClient的newCall方法,獲取一個Call對象捐川,參數(shù)是Request
3.調(diào)用call對象的enqueue()方法

步驟1和步驟2跟同步請求的步驟一致脓鹃,主要看一下步驟3

異步請求源碼:call.enqueue() 源碼分析

異步請求和同步請求的步驟1和步驟2一致,都是在做準備工作属拾,并沒有發(fā)起請求将谊,所以這次我們直接忽略了步驟1和步驟2的分析,直接分析步驟3的源碼渐白,我們點開RealCall的enqueue方法:

@Override public void enqueue(Callback responseCallback) {
    synchronized (this) {
      if (executed) throw new IllegalStateException("Already Executed");
      executed = true;
    }
    captureCallStackTrace();
    eventListener.callStart(this);
    client.dispatcher().enqueue(new AsyncCall(responseCallback));
  }

我們看看它的核心代碼: client.dispatcher().enqueue(new AsyncCall(responseCallback))尊浓,
client.dispatcher()返回一個Dispatcher對象沒什么可講的,緊接著調(diào)用Dispatcher的enqueue方法纯衍,參數(shù)是AsyncCall對象栋齿,我們先看看AsyncCall是什么?

final class AsyncCall extends NamedRunnable {
    private final Callback responseCallback;
    private volatile AtomicInteger callsPerHost = new AtomicInteger(0);

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

    AtomicInteger callsPerHost() {
      return callsPerHost;
    }
  
  }

只截取了部分代碼襟诸,該類繼承自NamedRunnable,我們看看NamedRunnable:

public abstract class NamedRunnable implements Runnable {
  protected final String name;

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

  @Override public final void run() {
    String oldName = Thread.currentThread().getName();
    Thread.currentThread().setName(name);
    try {
      execute();
    } finally {
      Thread.currentThread().setName(oldName);
    }
  }

  protected abstract void execute();
}

NamedRunnable 實現(xiàn)了Runnable 接口瓦堵。

我們直接去Dispatcher的enqueue()看看做了哪些操作。

void enqueue(AsyncCall call) {
   synchronized (this) {
     readyAsyncCalls.add(call);

     // Mutate the AsyncCall so that it shares the AtomicInteger of an existing running call to
     // the same host.
     if (!call.get().forWebSocket) {
       AsyncCall existingCall = findExistingCallWithHost(call.host());
       if (existingCall != null) call.reuseCallsPerHostFrom(existingCall);
     }
   }
   promoteAndExecute();
 }

在同步代碼塊中歌亲,將當前的call請求添加到待準備消息隊列中去菇用,注意這里跟同步請求的區(qū)別,同步請求的時候陷揪,并沒有把當前的call添加到準備消息隊列中去惋鸥。然后又調(diào)用了 promoteAndExecute()方法,同步請求的時候也調(diào)用了promoteAndExecute()方法

private boolean promoteAndExecute() {
    assert (!Thread.holdsLock(this));

    List<AsyncCall> executableCalls = new ArrayList<>();
    boolean isRunning;
    synchronized (this) {
      for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
        AsyncCall asyncCall = i.next();

        if (runningAsyncCalls.size() >= maxRequests) break; // Max capacity.
        if (asyncCall.callsPerHost().get() >= maxRequestsPerHost) continue; // Host max capacity.

        i.remove();
        asyncCall.callsPerHost().incrementAndGet();
        executableCalls.add(asyncCall);
        runningAsyncCalls.add(asyncCall);
      }
      isRunning = runningCallsCount() > 0;
    }

    for (int i = 0, size = executableCalls.size(); i < size; i++) {
      AsyncCall asyncCall = executableCalls.get(i);
      asyncCall.executeOn(executorService());
    }

    return isRunning;
  }

此時,readyAsyncCalls不為空了悍缠,我們單獨的把這個for循環(huán)拎出來講:

for (Iterator<AsyncCall> i = readyAsyncCalls.iterator(); i.hasNext(); ) {
        AsyncCall asyncCall = i.next();

        if (runningAsyncCalls.size() >= maxRequests) break; // Max capacity.
        if (asyncCall.callsPerHost().get() >= maxRequestsPerHost) continue; // Host max capacity.

        i.remove();
        asyncCall.callsPerHost().incrementAndGet();
        executableCalls.add(asyncCall);
        runningAsyncCalls.add(asyncCall);
      }

這段代碼的含義是:把符合條件的call請求從readyAsyncCalls提升為runningAsyncCalls卦绣,我們看這段代碼中的兩個if語句,第一個if語句判斷當前異步請求的執(zhí)行隊列長度大于等于請求最大值飞蚓,如果滿足直接跳出for循環(huán)滤港,maxRequests的值為64,第二個if語句判斷當前執(zhí)行的異步請求隊列中相同主機的請求數(shù)是否大于等于maxRequestsPerHost(每個主機最大請求數(shù)趴拧,默認為5)溅漾,如果這兩個條件都不滿足的情況下,把從readyAsyncCalls取出來的call請求著榴,存到臨時的
executableCalls 隊列中去樟凄。

緊接著去遍歷executableCalls:

  for (int i = 0, size = executableCalls.size(); i < size; i++) {
      AsyncCall asyncCall = executableCalls.get(i);
      asyncCall.executeOn(executorService());
    }

從executableCalls獲取AsyncCall對象,并且調(diào)用它的executeOn方法兄渺,executeOn()方法參數(shù)是executorService(),我們看看executorService():

 public synchronized ExecutorService executorService() {
    if (executorService == null) {
      executorService = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60, TimeUnit.SECONDS,
          new SynchronousQueue<>(), Util.threadFactory("OkHttp Dispatcher", false));
    }
    return executorService;
  }

可以看出,該方法是一個同步方法挂谍,返回的是一個線程池對象叔壤,ThreadPoolExecutor()的第二個參數(shù)傳入了Integer的最大值,即線程池所能容納的最大線程數(shù)為Integer.MAX_VALUE口叙,雖然這里設(shè)置了很大的值炼绘,但是實際情況下并非會達到最大值,因為上面enqueue()方法中有做了判斷妄田。

回到 asyncCall.executeOn(executorService())這里俺亮,executorService返回了一個線程池對象,緊接著調(diào)用線程池對象的execute方法疟呐,execute()方法傳入實現(xiàn)了Runable接口的AsyncCall對象脚曾,前面在分析同步請求的時候,說了AsyncCall實現(xiàn)了Runable接口

實現(xiàn)Runnable.png

ok,現(xiàn)在我們要看看線程池做了什么操作启具,直接去NamedRunnable的run方法看看做了什么操作本讥。

public abstract class NamedRunnable implements Runnable {
  protected final String name;

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

  @Override public final void run() {
    String oldName = Thread.currentThread().getName();
    Thread.currentThread().setName(name);
    try {
      execute();
    } finally {
      Thread.currentThread().setName(oldName);
    }
  }

  protected abstract void execute();
}

execute()是一個抽象方法,所以我們直接去NamedRunnable的實現(xiàn)類AsyncCall的execute()方法看:

@Override protected void execute() {
      boolean signalledCallback = false;
      timeout.enter();
      try {
        Response response = getResponseWithInterceptorChain();
        if (retryAndFollowUpInterceptor.isCanceled()) {
          signalledCallback = true;
          responseCallback.onFailure(RealCall.this, new IOException("Canceled"));
        } else {
          signalledCallback = true;
          responseCallback.onResponse(RealCall.this, response);
        }
      } catch (IOException e) {
        e = timeoutExit(e);
        if (signalledCallback) {
          // Do not signal the callback twice!
          Platform.get().log(INFO, "Callback failure for " + toLoggableString(), e);
        } else {
          eventListener.callFailed(RealCall.this, e);
          responseCallback.onFailure(RealCall.this, e);
        }
      } finally {
        client.dispatcher().finished(this);
      }
    }

這段代碼才是真正執(zhí)行異步請求的邏輯鲁冯,getResponseWithInterceptorChain()返回Response對象拷沸,然后判斷retryAndFollowUpInterceptor是否取消回調(diào)CallBack接口的onFailure()或onResponse()方法,最后finally中薯演,和同步請求的處理一樣撞芍,調(diào)用了Dispatcher對象的finished()方法。

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

  private <T> void finished(Deque<T> calls, T call) {
    Runnable idleCallback;
    synchronized (this) {
      if (!calls.remove(call)) throw new AssertionError("Call wasn't in-flight!");
      idleCallback = this.idleCallback;
    }

    boolean isRunning = promoteAndExecute();

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

看完promoteAndExecute()方法的具體操作跨扮,我們發(fā)現(xiàn)序无,調(diào)用eneque的時候會把call請求添加到readyAsyncCalls(異步請求準備隊列)中,而readyAsyncCalls隊列中的請求什么時候執(zhí)行呢好港,看完promoteAndExecute的代碼就恍然大悟了愉镰。

總結(jié):異步請求中,我們先去遍歷異步請求的就緒隊列钧汹,并判斷異步請求的執(zhí)行隊列的隊列大小是否小于設(shè)置的最大數(shù)的時候丈探,如果條件滿足,把該請求添加到異步請求的執(zhí)行隊列中去拔莱,同時把該請求添加到臨時的異步請求的執(zhí)行隊列去中碗降。之后,遍歷這個臨時的異步請求的執(zhí)行隊列塘秦,去執(zhí)行AsyncCall的execute()方法讼渊。

0a5f3743d5bd285e679ea2eae5dcde5.png

Android的技術(shù)討論Q群:947460837

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市尊剔,隨后出現(xiàn)的幾起案子爪幻,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,816評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件挨稿,死亡現(xiàn)場離奇詭異仇轻,居然都是意外死亡,警方通過查閱死者的電腦和手機奶甘,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,729評論 3 385
  • 文/潘曉璐 我一進店門篷店,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人臭家,你說我怎么就攤上這事疲陕。” “怎么了钉赁?”我有些...
    開封第一講書人閱讀 158,300評論 0 348
  • 文/不壞的土叔 我叫張陵蹄殃,是天一觀的道長。 經(jīng)常有香客問我橄霉,道長窃爷,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,780評論 1 285
  • 正文 為了忘掉前任姓蜂,我火速辦了婚禮按厘,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘钱慢。我一直安慰自己逮京,他們只是感情好,可當我...
    茶點故事閱讀 65,890評論 6 385
  • 文/花漫 我一把揭開白布束莫。 她就那樣靜靜地躺著懒棉,像睡著了一般。 火紅的嫁衣襯著肌膚如雪览绿。 梳的紋絲不亂的頭發(fā)上策严,一...
    開封第一講書人閱讀 50,084評論 1 291
  • 那天,我揣著相機與錄音饿敲,去河邊找鬼妻导。 笑死,一個胖子當著我的面吹牛怀各,可吹牛的內(nèi)容都是我干的倔韭。 我是一名探鬼主播,決...
    沈念sama閱讀 39,151評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼瓢对,長吁一口氣:“原來是場噩夢啊……” “哼寿酌!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起硕蛹,我...
    開封第一講書人閱讀 37,912評論 0 268
  • 序言:老撾萬榮一對情侶失蹤醇疼,失蹤者是張志新(化名)和其女友劉穎硕并,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體僵腺,經(jīng)...
    沈念sama閱讀 44,355評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡鲤孵,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,666評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了辰如。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,809評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡贵试,死狀恐怖琉兜,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情毙玻,我是刑警寧澤豌蟋,帶...
    沈念sama閱讀 34,504評論 4 334
  • 正文 年R本政府宣布,位于F島的核電站桑滩,受9級特大地震影響梧疲,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜运准,卻給世界環(huán)境...
    茶點故事閱讀 40,150評論 3 317
  • 文/蒙蒙 一幌氮、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧胁澳,春花似錦该互、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至胰丁,卻和暖如春随橘,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背锦庸。 一陣腳步聲響...
    開封第一講書人閱讀 32,121評論 1 267
  • 我被黑心中介騙來泰國打工机蔗, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人酸员。 一個月前我還...
    沈念sama閱讀 46,628評論 2 362
  • 正文 我出身青樓蜒车,卻偏偏與公主長得像,于是被迫代替她去往敵國和親幔嗦。 傳聞我的和親對象是個殘疾皇子酿愧,可洞房花燭夜當晚...
    茶點故事閱讀 43,724評論 2 351