為什么AsyncTask調(diào)用execute方法后很久才執(zhí)行doInBackground方法??

最近在項(xiàng)目中遇到一個(gè)比較奇葩的問題, 就是AsyncTask執(zhí)行了execute方法之后, doInBackground方法很久才得到掉調(diào)用. 我們都知道doInBackground方法是在子線程中執(zhí)行的, 而execute方法執(zhí)行的線程(一般是在UI線程)肯定與doInBackground方法執(zhí)行的線程不同.
從execute方法執(zhí)行的線程切換到doInBackground方法執(zhí)行的線程為什么會耗時(shí)這么久??
毫無疑問這是一個(gè)線程調(diào)度的問題. 我們不應(yīng)該懷疑系統(tǒng)調(diào)度有問題, 那么剩下的只有我們自己調(diào)度的問題......

項(xiàng)目里有一個(gè)強(qiáng)制更新的功能, 如下圖:

強(qiáng)制更新-未點(diǎn)擊按鈕

點(diǎn)擊強(qiáng)制更新按鈕后會顯示進(jìn)度調(diào)(正常情況), 如下圖:


強(qiáng)制更新-下載中

點(diǎn)擊"強(qiáng)制更新"按鈕后, 按鈕被禁止點(diǎn)擊:
downloadApkBtn.setClickable(true);
但是按鈕沒有變灰, 按鈕上的文本也沒改成"正在下載..."
因此在某些手機(jī)上就出現(xiàn)這樣的情況:

點(diǎn)擊按鈕后, 如圖一, ProgressBar進(jìn)度不動, 而且再點(diǎn)擊按鈕就沒有反應(yīng)了(按鈕設(shè)置過selector, 是有按壓效果的), 給用戶感覺就是: 應(yīng)用卡頓死掉了

初步懷疑是網(wǎng)絡(luò)問題, 于是把網(wǎng)絡(luò)連等相關(guān)步驟所耗的時(shí)間打印出來. 數(shù)據(jù)顯示, 就算網(wǎng)絡(luò)非常慢, 也不會花費(fèi)十多秒的時(shí)間. (并且連接也有超時(shí)限制, 如果很久不能連接上, 那么就會超時(shí), 總會得到一個(gè)結(jié)果) 有問題的手機(jī), 點(diǎn)擊按鈕到進(jìn)度條動起來的時(shí)間有幾十秒, 甚至幾分鐘, 有的就一直卡著不動了. 對于用戶來說, 幾秒或者十幾秒不動, 就直接退出了, 或者......

在調(diào)試中發(fā)現(xiàn), 點(diǎn)擊按鈕后很久, doInBacground()方法中的日志輸出都沒打印出來. 因此可以得出結(jié)論: doInBackground()方法所在的線程沒有執(zhí)行.
于是我把execute()方法執(zhí)行到doInBackground()方法執(zhí)行之間的時(shí)間也打印出來, 代碼如下:

private volatile long start_time;
private void downloadApk() {
    start_time = System.currentTimeMillis();
    new DownloadApkTask().execute();
}

class DownloadApkTask extends AsyncTask<Void, Integer, Boolean> {
    protected Boolean doInBackground(Void... params) {
        Log.e("_ajk_", "schedule time cast: " + ((System.currentTimeMillis() - start_time) / 1000.0));
        //省略 ...... 
    }
}

多次執(zhí)行啟動->點(diǎn)擊強(qiáng)制更新按鈕->退出動作, 日志輸出如下:

macbook-stonedeMacBook-Pro:~ stone$ adb logcat *:E | grep schedule
E/_ajk_   (25685): schedule time cost: 0.014
E/_ajk_   (25685): schedule time cost: 21.618
E/_ajk_   (25685): schedule time cost: 27.859
E/_ajk_   (25685): schedule time cost: 0.004
E/_ajk_   (25685): schedule time cost: 38.368
E/_ajk_   (25685): schedule time cost: 10.722
E/_ajk_   (25685): schedule time cost: 1.66
E/_ajk_   (25685): schedule time cost: 45.632
E/_ajk_   (25685): schedule time cost: 25.374
E/_ajk_   (25685): schedule time cost: 23.161
E/_ajk_   (25685): schedule time cost: 28.733
E/_ajk_   (25685): schedule time cost: 44.852
E/_ajk_   (25685): schedule time cost: 62.171
E/_ajk_   (25685): schedule time cost: 93.201
E/_ajk_   (25685): schedule time cost: 95.367
E/_ajk_   (25685): schedule time cost: 108.974
E/_ajk_   (25685): schedule time cost: 122.609
E/_ajk_   (25685): schedule time cost: 129.158
E/_ajk_   (25685): schedule time cost: 134.632

為什么doInBackground()方法所在線程不能及時(shí)執(zhí)行呢?

查看AsyncTask類源碼, 發(fā)現(xiàn)AsyncTask里面是使用線程池來執(zhí)行異步任務(wù)的, AsyncTask有兩個(gè)方法來執(zhí)行移步任務(wù):

@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
    return executeOnExecutor(sDefaultExecutor, params);
}

@MainThread
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
        Params... params) {
    if (mStatus != Status.PENDING) {
        switch (mStatus) {
            case RUNNING:
                throw new IllegalStateException("Cannot execute task:"
                        + " the task is already running.");
            case FINISHED:
                throw new IllegalStateException("Cannot execute task:"
                        + " the task has already been executed "
                        + "(a task can be executed only once)");
        }
    }

    mStatus = Status.RUNNING;

    onPreExecute();

    mWorker.mParams = params;
    exec.execute(mFuture);

    return this;
}

execute()方法使用的是AsyncTask內(nèi)部定義的默認(rèn)線程池, executeOnExecutor()使用的是開發(fā)者自定義的線程池. 我們來看看AsyncTask內(nèi)部自定義的程池到底是什么?
AsyncTask內(nèi)部定義的默認(rèn)線程池是SerialExecutor, SerialExecutor及其相關(guān)變量的定義如下:

private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE = 1;

private static final ThreadFactory sThreadFactory = new ThreadFactory() {
    private final AtomicInteger mCount = new AtomicInteger(1);

    public Thread newThread(Runnable r) {
        return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
    }
};

private static final BlockingQueue<Runnable> sPoolWorkQueue =
        new LinkedBlockingQueue<Runnable>(128);

/**
 * An {@link Executor} that can be used to execute tasks in parallel.
 */
public static final Executor THREAD_POOL_EXECUTOR
        = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
                TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);

/**
 * An {@link Executor} that executes tasks one at a time in serial
 * order.  This serialization is global to a particular process.
 */
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();

private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

private static class SerialExecutor implements Executor {
    final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
    Runnable mActive;

    public synchronized void execute(final Runnable r) {
        mTasks.offer(new Runnable() {
            public void run() {
                try {
                    r.run();
                } finally {
                    scheduleNext();
                }
            }
        });
        if (mActive == null) {
            scheduleNext();
        }
    }

    protected synchronized void scheduleNext() {
        if ((mActive = mTasks.poll()) != null) {
            THREAD_POOL_EXECUTOR.execute(mActive);
        }
    }
}

從SerialExecutor這個(gè)名字可以看出, 它是一個(gè)順序執(zhí)行任務(wù)的執(zhí)行器. 實(shí)際上SerialExecutor并不去執(zhí)行任務(wù), 它的execute()方法會委托AsyncTask內(nèi)部定義的一個(gè)線程池去執(zhí)行任務(wù), 這個(gè)默認(rèn)線程池(THREAD_POOL_EXECUTOR)的定義上面已經(jīng)給出. THREAD_POOL_EXECUTOR是一個(gè)可同時(shí)處理處理器個(gè)數(shù)+1個(gè)任務(wù)的線程池. 然而使用execute()方法執(zhí)行AsyncTask時(shí), 并沒什么卵用, 因?yàn)镾erialExecutor的execute()方法會對異步任務(wù)進(jìn)行封裝, 使得任務(wù)順序執(zhí)行, 跟在同一個(gè)線程中執(zhí)行一樣, 從而失去并發(fā)處理特性. 如果有一個(gè)耗時(shí)任務(wù)正在執(zhí)行, 那么后續(xù)任務(wù)將會一直等待前面的任務(wù)完成. 因此:

AsyncTask雖然是異步任務(wù), 這個(gè)異步指的是與ui線程異步. 如果你想要并發(fā)執(zhí)行多個(gè)任務(wù), execute()方法不能實(shí)現(xiàn), executeOnExecutor()方法可以達(dá)到目的 (必須提供正確的Executor) ----- 異步跟并發(fā)并非同一個(gè)概念.

經(jīng)過上面的分析可以得知, 下載apk的異步任務(wù)之前肯定有一個(gè)耗時(shí)任務(wù)在執(zhí)行, 因此導(dǎo)致下載apk的異步任務(wù)一直處于等待隊(duì)列之中而得不到執(zhí)行. 那么如何進(jìn)行驗(yàn)證呢?

我們通過DDMS來查看線程狀態(tài), 看看在下載任務(wù)之前是有正在處理的任務(wù), 操作如下:
啟動DDMS, 在DDMS窗口左側(cè)的Devices視圖中選擇我們要調(diào)試的進(jìn)程, 然后點(diǎn)擊頂部的Update Threads按鈕, 這時(shí)我們可以在右側(cè)的Threads視圖中看到我們選中的進(jìn)程的所有線程信息, 如圖:

使用DDMS查看線程狀態(tài)

查看AsyncTask類的源碼, 我們知道AsyncTask默認(rèn)線程池中的工作線程的命名格式是AsyncTask #index (index大小為 1~線程池的coreSize), 因此我們看名稱為AsyncTask開頭的線程就行了 (不得不吐槽一下DDMS做的太爛, 居然不能按線程名排序或分類, 如果線程多了, 那就AsyncTask使用的線程中間就會夾雜其他線程, 這給觀察帶來不便), 選中某個(gè)線程后下面的狀態(tài)框中會輸出此線程的堆棧信息.

通過DDMS查看線程狀態(tài), 發(fā)現(xiàn)出問題時(shí)有一個(gè)AsyncTask線程處于Native狀態(tài), Native狀態(tài)是什么鬼? 關(guān)于線程狀態(tài)可參考 DDMS中線程狀態(tài)的說明, 此文對Native狀態(tài)的說明是:

native – executing native code – 執(zhí)行了原生代碼,這個(gè)對于 帶有消息隊(duì)列的線程是正常的狀態(tài),表示消息隊(duì)列沒有任何消息,線程在native 代碼中進(jìn)行無限循環(huán),直到消息隊(duì)列中出現(xiàn)新的消息,消息隊(duì)列才會返回Java 代碼處理消息。

呃, 線程在native代碼中進(jìn)行無限循環(huán)......

我猜想是native層導(dǎo)致某個(gè)task的運(yùn)行一直處于阻塞狀態(tài), 這樣后面的task也就無法執(zhí)行, 查看處于native狀態(tài)的線程的堆棧信息, 如下:

at libcore.io.Posix.recvfromBytes(Native Method)  
at libcore.io.Posix.recvfrom(Posix.java:141)  
at libcore.io.BlockGuardOs.recvfrom(BlockGuardOs.java:164)    
at libcore.io.IoBridge.recvfrom(IoBridge.java:550)    
at java.net.PlainSocketImpl.read(PlainSocketImpl.java:506)    
at java.net.PlainSocketImpl.access$000(PlainSocketImpl.java:46)   
at java.net.PlainSocketImpl$PlainSocketInputStream.read(PlainSocketImpl.java:240) 
at org.apache.http.impl.io.AbstractSessionInputBuffer.fillBuffer(AbstractSessionInputBuffer.java:103) 
at org.apache.http.impl.io.AbstractSessionInputBuffer.read(AbstractSessionInputBuffer.java:134)   
at org.apache.http.impl.io.ContentLengthInputStream.read(ContentLengthInputStream.java:174)   
at org.apache.http.impl.io.ContentLengthInputStream.read(ContentLengthInputStream.java:188)   
at org.apache.http.impl.io.ContentLengthInputStream.close(ContentLengthInputStream.java:121)  
at org.apache.http.conn.BasicManagedEntity.streamClosed(BasicManagedEntity.java:179)  
at org.apache.http.conn.EofSensorInputStream.checkClose(EofSensorInputStream.java:266)    
at org.apache.http.conn.EofSensorInputStream.close(EofSensorInputStream.java:213) 
at com.anjuke.android.newbroker.activity.AutoUpdateActivity$DownloadApkTask.closeInputStream(AutoUpdateActivity.java:345) 
at com.anjuke.android.newbroker.activity.AutoUpdateActivity$DownloadApkTask.doInBackground(AutoUpdateActivity.java:289)   
at com.anjuke.android.newbroker.activity.AutoUpdateActivity$DownloadApkTask.doInBackground(AutoUpdateActivity.java:202)   
at com.anjuke.android.newbroker.util.image.AsyncTask$2.call(AsyncTask.java:337)   
at java.util.concurrent.FutureTask.run(FutureTask.java:237)   
at com.anjuke.android.newbroker.util.image.AsyncTask$SerialExecutor$1.run(AsyncTask.java:279) 
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)    
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)    
at java.lang.Thread.run(Thread.java:848)  

在堆棧中發(fā)現(xiàn)了這個(gè)com.anjuke.android.newbroker.util.image.AsyncTask, 這個(gè)類其實(shí)是從這里考過來的, 查看源碼發(fā)現(xiàn)跟系統(tǒng)的區(qū)別不大, 只是對默認(rèn)線程池進(jìn)行了版本處理(android 11以下的版本使用單線程來處理異步任務(wù)), 代碼如下:

/**
 * An {@link Executor} that executes tasks one at a time in serial order.
 * This serialization is global to a particular process.
 */
public static final Executor SERIAL_EXECUTOR = Utils.hasHoneycomb() ? new SerialExecutor() :
        Executors.newSingleThreadExecutor(sThreadFactory);

根據(jù)堆棧信息可以得知, 關(guān)閉下載輸入流時(shí), 系統(tǒng)阻塞了, 這是非正常關(guān)閉輸入流, 它是這樣發(fā)生的:

啟動App, 點(diǎn)擊強(qiáng)制更新按鈕, 這時(shí)正在下載文件了, 然后退出退出App (用戶下載了一半, 可能不想下載了), 這時(shí)就會關(guān)閉輸入流. 這時(shí)的輸入流中的數(shù)據(jù)還沒讀完, 這樣關(guān)閉入流就會阻塞 (會不會等待數(shù)據(jù)讀取完畢在關(guān)閉??). 再啟動app時(shí), 再點(diǎn)擊強(qiáng)制更新按鈕, 這時(shí)啟動的下載任務(wù)會一直等待前一個(gè)下載任務(wù)關(guān)閉輸入流完成. --- 退出APP, APP的進(jìn)程并沒有殺掉( 除非用戶按HOME鍵手動劃掉應(yīng)用或在系統(tǒng)設(shè)置的應(yīng)用管理中強(qiáng)制結(jié)束進(jìn)程, 用戶一般不會這么做), 因此下一次啟動的APP和上一次啟動的APP會共用AsyncTask的線程池.

我們還是用數(shù)據(jù)說話, 我在程序中打印了關(guān)閉輸入流的時(shí)間, 代碼如下:

private void closeInputStream(InputStream inputStream) throws IOException {
    if (inputStream != null) {
        long startTime = System.currentTimeMillis();
        inputStream.close();
        Log.e("_close_", "close inputstream cost: " +((System.currentTimeMillis() - startTime) / 1000.0) + "s");
        inputStream = null;
    }
}

運(yùn)行調(diào)試, 輸出信息如下 (反復(fù)啟動同一個(gè)app, 進(jìn)程是同一個(gè) 從下面的日志中可以看出, 圓括號中的為進(jìn)程ID --- 殺過一次進(jìn)程):

macbook-stonedeMacBook-Pro:~ stone$ adb logcat *:E | grep _close_
E/_close_ (20394): close inputstream cost: 76.06s
E/_close_ (20394): close inputstream cost: 0.0s
E/_close_ (20394): close inputstream cost: 30.284s
E/_close_ (20394): close inputstream cost: 0.0s
E/_close_ (20394): close inputstream cost: 31.251s
E/_close_ (20394): close inputstream cost: 0.0s
E/_close_ (20394): close inputstream cost: 28.734s
E/_close_ (20394): close inputstream cost: 0.0s
E/_close_ (20394): close inputstream cost: 27.762s
E/_close_ (20394): close inputstream cost: 0.0s
E/_close_ (20394): close inputstream cost: 35.533s
E/_close_ (20394): close inputstream cost: 0.0s
E/_close_ (20394): close inputstream cost: 25.023s
E/_close_ (20394): close inputstream cost: 0.001s
E/_close_ (20394): close inputstream cost: 17.888s
E/_close_ (20394): close inputstream cost: 0.0s
E/_close_ (20394): close inputstream cost: 17.883s
E/_close_ (20394): close inputstream cost: 0.0s
E/_close_ (20394): close inputstream cost: 23.678s
E/_close_ (20394): close inputstream cost: 0.0s
E/_close_ (21037): close inputstream cost: 134.213s
E/_close_ (21037): close inputstream cost: 0.0s
E/_close_ (21037): close inputstream cost: 56.464s
E/_close_ (21037): close inputstream cost: 0.0s
E/_close_ (21037): close inputstream cost: 40.104s
E/_close_ (21037): close inputstream cost: 0.0s
E/_close_ (21037): close inputstream cost: 34.067s
E/_close_ (21037): close inputstream cost: 0.0s

由于doInBackground中多次調(diào)用了closeInputStream方法, finally塊中關(guān)閉輸入流時(shí)間為0. doInBackground方法的基本邏輯如下:

protected Boolean doInBackground(Void... params) {
    InputStream inputStream = null;
    // 省略 ...... 
    try {
        // 省略 ......
        if (inputStream != null) {
            while (!isCancelDownload && !isFinishing()) { 
                //download apk file .......
            }
        }
        closeInputStream(inputStream);
        return true;
    } catch (Exception e) {
        // 省略 ...... 
        return false;
    } finally {
        try {
            closeInputStream(inputStream);
        } catch (IOException e) {
            // 省略 ...... 
        }
    }
}

還有一個(gè)問題就是, 用戶啟動一次發(fā)現(xiàn)下載不了, 就有退出, 那么這一動作就會加入一個(gè)下載任務(wù), 如果用戶反復(fù)執(zhí)行這一動作, 那么AsyncTask的默認(rèn)線程池中就會堆積越來越多的下載任務(wù), 而execute()方法導(dǎo)致任務(wù)順序執(zhí)行, 這樣關(guān)閉輸入流的時(shí)間也會累積起來, 也就是啟動次數(shù)越多, 下載任務(wù)等待的時(shí)間就會越久. 由于AsyncTask是自定義的, 所以我可以更改源碼, 因此我在AsyncTask中加入了Log語句Log.e("_size_", "pending size: " + mTasks.size());, 輸出等待執(zhí)行的任務(wù)數(shù)量, 更完整的代碼如下:

@TargetApi(11)
private static class SerialExecutor implements Executor {
    final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
    Runnable mActive;

    public synchronized void execute(final Runnable r) {
        mTasks.offer(new Runnable() {
            public void run() {
                try {
                    r.run();
                } finally {
                    scheduleNext();
                }
            }
        });
        if (mActive == null) {
            scheduleNext();
        }
        Log.e("_size_", "pending size: " + mTasks.size());
    }

    protected synchronized void scheduleNext() {
        if ((mActive = mTasks.poll()) != null) {
            THREAD_POOL_EXECUTOR.execute(mActive);
        }
    }
}

多次執(zhí)行啟動-點(diǎn)擊強(qiáng)制更新按鈕-退出動作, log日志如下:

macbook-stonedeMacBook-Pro:~ stone$ adb logcat *:E | grep _size_
E/_size_  (21890): pending size: 0
E/_size_  (21890): pending size: 1
E/_size_  (21890): pending size: 2
E/_size_  (21890): pending size: 2
E/_size_  (21890): pending size: 3
E/_size_  (21890): pending size: 4
E/_size_  (21890): pending size: 5
E/_size_  (21890): pending size: 6
E/_size_  (21890): pending size: 7

日志說明上面的分析是正確的, 任務(wù)的確會積累在等待隊(duì)列中.

至此問題已經(jīng)分析完畢 !

解決辦法: 使用AsyncTask的executeOnExecutor()方法來執(zhí)行異步任務(wù),且為executeOnExcutor()方法提供一個(gè)合理的線程池, 或者直接用Thread來執(zhí)行下載任務(wù).

總結(jié):

  1. **AsyncTask的execute()方法是順序執(zhí)行異步任務(wù)的 (相當(dāng)于Executors.newSingleThreadExecutor()), 每次只執(zhí)行一個(gè)任務(wù). 如果要并發(fā)執(zhí)行多個(gè)異步任務(wù), 必須使用AsyncTask的executeOnExecutor()方法, 且必須提供正確的線程池. **

  2. 建議: 盡量不要使用AsynTask的execute()方法來執(zhí)行異步任務(wù). 如果要使用execute()方法來執(zhí)行異步任務(wù), 你必須確定這個(gè)異步任務(wù)無論正常執(zhí)行/或異常執(zhí)行都不會耗時(shí) (如果耗時(shí), 會導(dǎo)致后續(xù)異步任務(wù)得不到及時(shí)執(zhí)行), 且你并不關(guān)心它什么時(shí)候執(zhí)行 (因?yàn)槟銦o法確認(rèn)在你執(zhí)行異步任務(wù)之前別人是否已經(jīng)使用execute()方法執(zhí)行了一個(gè)耗時(shí)的異步任務(wù)).

如有錯(cuò)誤, 歡迎指正!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,646評論 6 533
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,595評論 3 418
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,560評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,035評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,814評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,224評論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,301評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,444評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,988評論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,804評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,998評論 1 370
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,544評論 5 360
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,237評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,665評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,927評論 1 287
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,706評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,993評論 2 374

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