Android 開發(fā)藝術(shù)探索筆記之十一 -- Android 的多線程和線程池

學(xué)習(xí)內(nèi)容

  • 線程基本概念
  • 線程的不同形式
    • AsyncTask
    • HandlerThread
    • IntentService
  • 線程池基礎(chǔ)

原文開篇部分

  • 主線程主要處理和界面相關(guān)的事情,而子線程則往往用于執(zhí)行耗時操作呈队。
  • 線程的多種形態(tài):
    • AsyncTask:底層封裝了線程池和 Handler乔遮,目的在于方便開發(fā)者在子線程中更新 UI皇拣。
    • HandlerThread:底層直接使用了線程,它是一個具有消息循環(huán)的線程,其內(nèi)部可以使用 Handler
    • IntentService:底層直接使用了線程诲祸,內(nèi)部采用 HandlerThread 來執(zhí)行任務(wù),完成后即退出而昨。它是類似后臺線程的服務(wù)救氯,不易被系統(tǒng)殺死從而保證后臺任務(wù)的執(zhí)行。而單純后臺線程的話歌憨,如果內(nèi)部沒有活動的四大組件着憨,優(yōu)先級低,容易被殺死务嫡。
  • 線程池:
    • 線程的創(chuàng)建和銷毀有相應(yīng)的開銷甲抖,因此采用線程池,線程池中會緩存一定數(shù)量的線程心铃,通過線程池可以避免因為頻繁創(chuàng)建和銷毀線程所帶來的系統(tǒng)開銷准谚。

主線程和子線程

基本介紹

  1. 主線程指進(jìn)程擁有的線程;子線程也叫工作線程去扣,除了主線程以外的線程都是子線程柱衔。
  2. 主線程的作用是運行四大組件以及處理它們和用戶的交互,而子線程的作用則是執(zhí)行耗時任務(wù)愉棱。
  3. Android 3.0 以后唆铐,網(wǎng)絡(luò)訪問必須在子線程中進(jìn)行,否則網(wǎng)絡(luò)訪問會失敗奔滑,并拋出 NetworkOnMainThreadException 異常艾岂,此舉是為了避免主線程由于被耗時任務(wù)所阻塞而出現(xiàn) ANR。

Android 中的線程形態(tài)

AsyncTask

1. 基本

  • 輕量級
  • 適合執(zhí)行后臺任務(wù)以及在主線程中訪問 UI朋其,但不適合執(zhí)行特別好使的后臺任務(wù)王浴。(特別耗時的,建議線程池)

2.參數(shù)

  • AsyncTask 是一個抽象的泛型類令宿,有三個參數(shù)叼耙,具體聲明如下

    public abstract class AsyncTask<Params, Progress, Result>

  • Params:表示參數(shù)的類型

  • Progress:表示后臺任務(wù)的執(zhí)行進(jìn)度的類型

  • Result:表示后臺任務(wù)的返回結(jié)果的類型

3.核心方法

  1. onPreExecute():在主線程中執(zhí)行。在異步任務(wù)之前調(diào)用此方法粒没,做一些準(zhǔn)備工作
  2. doInBackground(Params...params):
    1. 線程池中調(diào)用筛婉,用于執(zhí)行異步任務(wù),參數(shù)表明異步任務(wù)的輸入?yún)?shù)。
    2. 內(nèi)部可以通過 publishProgress 方法更新任務(wù)進(jìn)度爽撒,而 publishProgress 方法中會調(diào)用 onProgressUpdate方法
    3. 另外該方法返回結(jié)果給 onPostExecute 方法入蛆。
  3. onProgressUpdate(Progress...progress):在主線程中執(zhí)行,當(dāng)后臺任務(wù)的進(jìn)度發(fā)生改變時此方法被調(diào)用硕勿。用于更新界面中的進(jìn)度哨毁。
  4. onPostExecute(Result result):在主線程中執(zhí)行,異步任務(wù)執(zhí)行之后源武,此方法被調(diào)用扼褪,result 是后臺任務(wù)的返回值。用于在任務(wù)完成后給出提示粱栖。
  5. 補充
    1. 執(zhí)行順序:onPreExecute --> doInBackground --> onPostExecute话浇。
    2. onCanceled():在 主線程 中執(zhí)行,異步任務(wù)取消時闹究,該方法被調(diào)用幔崖,此時 onPoseExecute 方法不會被調(diào)用。

4.限制

  1. AsyncTask 的類必須在 主線程 中加載渣淤,即第一次訪問 AsyncTask 必須發(fā)生在主線程赏寇,Android 4.1 及以上版本已自動完成。
  2. AsyncTask 的對象必須在 主線程 中創(chuàng)建
  3. execute 方法必須在 UI 線程 調(diào)用
  4. 不要在程序中直接調(diào)用 onPreExecute价认、doInBackground嗅定、onProgressUpdate、onPostExecute 方法
  5. 一個 AsyncTask 對象只能 執(zhí)行一次刻伊,即只能調(diào)用一次 execute 方法露戒,否則報異常
  6. Android 1.6 以前,串行執(zhí)行任務(wù)捶箱;Android 1.6 時采用線程池處理并行任務(wù);Andorid 3.0 開始动漾,采用單個線程來串行執(zhí)行任務(wù)丁屎,但是可以通過 AsyncTask 的 executeOnExecutor 方法來并行執(zhí)行任務(wù)。

AsyncTask 的工作原理

1.分析

  • execute(Params... params) 入手分析旱眯,它會調(diào)用 **executeOnExecutor **方法:

    @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(&quot;Cannot execute task:&quot;
                                + " 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;
        }
    

    在 executeOnExecutor 方法中晨川,首先 onPreExecute 方法被調(diào)用,之后線程池開始執(zhí)行 exec.execute(mFuture)删豺,這里實際上調(diào)用的是 SerialExecutor.execute(final Runnable r) 方法:

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

    此處可以分析 AsyncTask 的排隊執(zhí)行的過程共虑。首先系統(tǒng)把 AsyncTask 的 Params 參數(shù)封裝成 FutureTask 對象,F(xiàn)utureTask 是一個并發(fā)類呀页,它充當(dāng) Runnable 的作用妈拌。接著這個 FutureTask 會交給 SerialExecutor 的 execute 方法處理。

    execute 方法首先會把該 FutureTask 對象插入到任務(wù)隊列 mTasks蓬蝶,如果此時沒有正在活動的 AsyncTask 任務(wù)尘分,就會執(zhí)行下一個 AsyncTask 任務(wù)猜惋;同時當(dāng)一個 AsyncTask 任務(wù)執(zhí)行完后,AsyncTask 會繼續(xù)執(zhí)行其他任務(wù)直到所有任務(wù)都執(zhí)行完為止培愁。(串行執(zhí)行

    AsyncTask 有兩個線程池(THREAD_POOL_EXECUTOR 和 SerialExecutor )和一個 Handler(InternalHandler)著摔。

    • SerialExecutor 用于任務(wù)的排隊
    • THREAD_POOL_EXECUTOR 用于真正的執(zhí)行任務(wù)
    • InternalHandler 用于將執(zhí)行環(huán)境從線程池切換到主線程

    在上面的分析中,我們看到 SerialExecutor 中調(diào)用了 FutrueTask 的 run 方法定续,而其 run 方法中會調(diào)用 mWorker 的 call 方法谍咆,call 方法定義在 AsyncTask 的構(gòu)造函數(shù)中

    mWorker = new WorkerRunnable<Params, Result>() {
                public Result call() throws Exception {
                    mTaskInvoked.set(true);
                    Result result = null;
                    try {
                        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                        //noinspection unchecked
                        result = doInBackground(mParams);
                        Binder.flushPendingCommands();
                    } catch (Throwable tr) {
                        mCancelled.set(true);
                        throw tr;
                    } finally {
                        postResult(result);
                    }
                    return result;
                }
            };
    

    能看到,call 方法中首先將 mTaskInvoked 設(shè)為 true私股,表明當(dāng)前任務(wù)已被調(diào)用卧波,然后執(zhí)行 AsyncTask 的 doInBackground 方法,接著講返回值傳遞給 postResult 方法庇茫,它的實現(xiàn)如下所示:

    private Result postResult(Result result) {
            @SuppressWarnings("unchecked")
            Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
                    new AsyncTaskResult<Result>(this, result));
            message.sendToTarget();
            return result;
        }
    

    上面的代碼中港粱,postResult 方法會通過 getHandler() 方法得到一個 Handler 對象,然后通過該 Handler 發(fā)送一個消息旦签;實際上 getHandler 返回了一個 mHandler查坪。

    private Handler getHandler() {
        return mHandler;
    }
    

    而AsyncTask 的構(gòu)造方法中,對 mHandler 做出如下設(shè)置:

    mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
                ? getMainHandler()
                : new Handler(callbackLooper);
    
    
    private static Handler getMainHandler() {
            synchronized (AsyncTask.class) {
                if (sHandler == null) {
                    sHandler = new InternalHandler(Looper.getMainLooper());
                }
                return sHandler;
            }
        }
    

    因此實際最后得到的 Handler 對象即為 sHandler宁炫,變相要求了 AsyncTask 的類必須在主線程中加載偿曙。這個 sHandler 的定義如下:

    private static class InternalHandler extends Handler {
            public InternalHandler(Looper looper) {
                super(looper);
            }
    
            @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
            @Override
            public void handleMessage(Message msg) {
                AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
                switch (msg.what) {
                    case MESSAGE_POST_RESULT:
                        // There is only one result
                        result.mTask.finish(result.mData[0]);
                        break;
                    case MESSAGE_POST_PROGRESS:
                        result.mTask.onProgressUpdate(result.mData);
                        break;
                }
            }
        }
    

    可以看到,sHandler 收到 MESSAGE_POST_RESULT 消息后會調(diào)用 AsyncTask 的 finish 方法羔巢。

     private void finish(Result result) {
            if (isCancelled()) {
                onCancelled(result);
            } else {
                onPostExecute(result);
            }
            mStatus = Status.FINISHED;
        }
    

    如果被取消執(zhí)行望忆,那么調(diào)用 onCancelled 方法,否則調(diào)用 onPostExecute 方法竿秆,此時 doInBackground 的返回值 result 就傳遞給了 onPostExecute 方法启摄。

    到此為止,AsyncTask 的整個工作流程就分析完畢了幽钢。

2.流程順序

AsyncTask.execute(Params... params) -> AsyncTask.executeOnExecutor(Executor exec,Params... params) (此方法中調(diào)用 onPreExecute)-> SerialExecutor.execute(final Runnable r) -> FutureTask r.run() -> WorkerRunnable<Params,Result> mWorker.call()(此方法中調(diào)用 doInBackground) -> AsyncTask.postResult(Result result) -> sHandler 發(fā)送消息 -> InternalHandler.handleMessage -> AsyncTask.finish(此方法中調(diào)用 onCancelled 或者 onPostExecute )

HandlerThread

介紹

  1. HandlerThread 繼承了 Thread歉备,是一種可以使用 Handler 的 Thread。
  2. 實現(xiàn)簡單:在 run 方法中通過 Looper.prepare() 來創(chuàng)建消息隊列匪燕,并通過 Looper.loop() 開啟消息循環(huán)蕾羊,這樣就可以創(chuàng)建使用 Handler 了。
  3. HandlerThread vs. 普通的 Thread:
    1. 普通 Thread 主要用于在 run 方法中執(zhí)行一個耗時任務(wù);
    2. HandlerThread 在內(nèi)部創(chuàng)建了消息隊列帽驯,外界需要通過 Handler 的消息方式來通知 HandlerThread 執(zhí)行一個具體的任務(wù)
  4. 使用場景之一:IntentService
  5. 建議:和 Looper 類似龟再,當(dāng)明確不再需要 HandlerThread 時,通過它的 quit 或者 quitSafely 方法來終止線程的執(zhí)行尼变。

IntentService

1.介紹

  1. IntentService 是一種特殊的 Service利凑,繼承了 Service 并且是一個 抽象類
  2. 用于執(zhí)行后臺耗時的任務(wù),任務(wù)執(zhí)行后它會自動停止截碴。同時因為其本質(zhì)是 Service梳侨,導(dǎo)致優(yōu)先級比普通的線程高,不容易被殺死日丹,所以適合執(zhí)行一些 高優(yōu)先級的后臺任務(wù)走哺。

2.原理

  1. IntentService 封裝了 HandlerThread 和 Handler,從源碼中可以清楚的看到:

    public abstract class IntentService extends Service {
        //...
        private final class ServiceHandler extends Handler {
            public ServiceHandler(Looper looper) {
                super(looper);
            }
    
            @Override
            public void handleMessage(Message msg) {
                onHandleIntent((Intent)msg.obj);
                stopSelf(msg.arg1);
            }
        }
        
        @Override
        public void onCreate() {
            // TODO: It would be nice to have an option to hold a partial wakelock
            // during processing, and to have a static startService(Context, Intent)
            // method that would launch the service & hand off a wakelock.
    
            super.onCreate();
            HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
            thread.start();
    
            mServiceLooper = thread.getLooper();
            mServiceHandler = new ServiceHandler(mServiceLooper);
        }
        
        //...
    }
    

    在第一次啟動 IntentService 時哲虾,onCreate 方法創(chuàng)建一個 HandlerThread丙躏,然后利用 Looper 來構(gòu)造一個 Handler 對象 mServiceHandler,這樣通過 mServiceHandler 發(fā)送的消息最后都會在 HandlerThread 中執(zhí)行束凑。

    當(dāng)每次啟動 IntentService 時晒旅,都會調(diào)用 onStartConmmand 方法,而其又會調(diào)用 onStart 方法汪诉,源碼如下:

    @Override
        public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
            onStart(intent, startId);
            return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
        }
    
    @Override
        public void onStart(@Nullable Intent intent, int startId) {
            Message msg = mServiceHandler.obtainMessage();
            msg.arg1 = startId;
            msg.obj = intent;
            mServiceHandler.sendMessage(msg);
        }
    

    可以看出废恋,它只是通過 mServiceHandler 發(fā)送了一條消息,這條消息包含了 Intent 對象扒寄,這個消息會在 HandlerThread 中處理鱼鼓。當(dāng) mServiceHandler 收到消息后,會將 Intent 對象傳遞給 onHandleIntent 方法该编,而 onHandleIntent 方法是一個抽象方法迄本,通過子類重寫該方法從而實現(xiàn)通過 Intent 對象解析外界啟動 IntentService 時傳入的參數(shù),并針對不同參數(shù)區(qū)分具體的后臺任務(wù)课竣。

    @WorkerThread
    protected abstract void onHandleIntent(@Nullable Intent intent);
    

    當(dāng) onHandlerIntent 結(jié)束后嘉赎,就會調(diào)用 stopSelf(int startId) 方法停止服務(wù),該方法會等待所有的消息都處理完畢后才會終止服務(wù)于樟。(stopSelf()方法會立即終止)

    作為補充公条,IntentService 是順序執(zhí)行后臺任務(wù)的,原因在于 IntentService 內(nèi)部是通過消息的方式向 HandlerThread 請求執(zhí)行任務(wù)隔披,而 Handler 的 Looper 是順序處理的赃份,因此 IntentService 也是順序執(zhí)行的。


Android 中的線程池

線程池的優(yōu)點

  1. 重用線程池中的線程奢米,避免因為線程的創(chuàng)建和銷毀所帶來的性能開銷
  2. 有效控制線程池的最大并發(fā)數(shù),避免大量的線程之間因互相搶占系統(tǒng)自ui按導(dǎo)致的阻塞現(xiàn)象
  3. 能夠?qū)€程進(jìn)行簡單的管理纠永,并提供定時指定以及指定間隔循環(huán)執(zhí)行等功能鬓长。

1.ThreadPoolExecutor

1.基本介紹

  1. 該類是線程池的真正實現(xiàn),Android 的線程池都是直接或者間接通過配置 ThreadPoolExecutor 來實現(xiàn)的尝江。

  2. 一個比較常用的構(gòu)造方法:

    public ThreadPoolExector(int corePoolSize,
                            int maximunPoolSize,
                             long keepAliveTime,
                             TimeUnit unit,
                             BlockingQueue<Runnable> workQueue,
                             ThreadFactory threadFactory)
    

    參數(shù)說明:

    • corePoolSize:線程池的核心線程數(shù)涉波,默認(rèn)情況下,核心線程會一直存活。如果設(shè)置 allowCoreThreadTimeOut 為 true啤覆,且等待時間超過 keepAliveTime 所制定的時長后苍日,核心線程會被終止。
    • maximunPoolSize:線程池能容納的最大線程數(shù)窗声,當(dāng)活動的線程達(dá)到這個數(shù)值之后相恃,后續(xù)任務(wù)會被阻塞。
    • keepAliveTime:非核心線程閑置時的超時時長笨觅,超過這個時長拦耐,非核心線程就會被回收。當(dāng)設(shè)置 allowCoreThreadTimeOut = true 時见剩,同樣會作用于核心線程杀糯。
    • unit:指定 keepAliveTime 參數(shù)的時間單位,這是一個枚舉苍苞。
    • workQueue:線程池中的任務(wù)隊列固翰,通過線程池的 execute 方法提交的 Runnable 對象會存儲在這個參數(shù)中。
    • threadFactory:線程工廠羹呵,為線程池提供創(chuàng)建新線程的功能功能骂际。它是個接口,只有一個方法:Thread newThread(Runnable r)

    不常用的參數(shù)

    • RejectedExecutionHandler handler:當(dāng)線程池?zé)o法執(zhí)行新任務(wù)時担巩,可能是由于任務(wù)隊列已滿或者時無法成功執(zhí)行任務(wù)方援,此時 ThreadPoolExecutor 會調(diào)用 handler 的 rejectdExecution 方法來通知調(diào)用者。

2.執(zhí)行任務(wù)的規(guī)則

  1. 線程池中的線程數(shù)量未達(dá)到核心線程的數(shù)量:直接啟動一個核心線程來執(zhí)行任務(wù)涛癌。
  2. 如果線程池中的線程數(shù)量已經(jīng)大導(dǎo)或者超過核心線程的數(shù)量犯戏,那么任務(wù)會被插入到任務(wù)隊列等待執(zhí)行
  3. 如果 2 中無法將任務(wù)插入到任務(wù)隊列中,這往往是由于任務(wù)隊列已滿拳话,此時如果線程數(shù)量未達(dá)到線程池規(guī)定的最大值先匪,那么會立刻啟動一個非核心線程來執(zhí)行任務(wù)。
  4. 如果 3 中線程數(shù)量已經(jīng)達(dá)到線程池規(guī)定的最大值弃衍,那么據(jù)拒絕執(zhí)行此任務(wù)呀非,此時上述不常用的參數(shù) handler 發(fā)揮作用。

3.AsyncTask 線程池的配置

(針對 THREAD_POOL_EXECUTOR 線程池)

  1. 核心線程數(shù)等于 CPU 核心數(shù) + 1
  2. 線程池的最大線程數(shù)為 CPU 核心數(shù)的 2 倍 + 1
  3. 核心線程無超時機制镜盯,非核心線程在閑置時的超時時間為 1 秒
  4. 任務(wù)隊列的容量為 128

2.線程池的分類

常見的 4 個線程池

  1. FixedThreadPool
    1. 線程池固定的線程池岸裙,當(dāng)線程處于空閑狀態(tài)時,它們并不會被回收速缆,除非線程池被關(guān)閉降允。當(dāng)所有的線程都處于活動狀態(tài)時,新任務(wù)都會處于等待狀態(tài)艺糜,直到有線程空閑出來剧董。
    2. 只有核心線程并且不會被回收幢尚,能夠更加快速的響應(yīng)外界的請求
  2. CachedThreadPool
    1. 線程數(shù)量不定的線程池翅楼,只有非核心線程尉剩,最大線程數(shù)為 Integer.MAX_VALUE。
    2. 當(dāng)線程池中的線程都處于活動狀態(tài)時毅臊,線程池會創(chuàng)建新的線程來處理新任務(wù)理茎,否則利用空閑的線程來處理新任務(wù)。線程池中的空閑線程具有超時機制褂微,為 60s功蜓。
    3. 任務(wù)隊列相當(dāng)于一個空集合,導(dǎo)致任何任務(wù)都會立即被執(zhí)行宠蚂,適合執(zhí)行大量耗時較少的任務(wù)式撼。當(dāng)整個線程池都處于限制狀態(tài)時,線程池中的縣城都會超時而被停止求厕。
  3. ScheduledThreadPool
    1. 核心線程數(shù)量固定著隆,非核心線程數(shù)沒有限制,并且非核心線程閑置的時候立即回收呀癣。
    2. 主要用于執(zhí)行定時任務(wù)和具有固定周期的重復(fù)任務(wù)
  4. SingleThreadExecutor
    1. 只有一個核心線程美浦,保證所有的任務(wù)都在一個線程中順序執(zhí)行。
    2. 意義在于不需要處理線程同步的問題项栏。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末浦辨,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子沼沈,更是在濱河造成了極大的恐慌流酬,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,723評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件列另,死亡現(xiàn)場離奇詭異芽腾,居然都是意外死亡,警方通過查閱死者的電腦和手機页衙,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評論 2 382
  • 文/潘曉璐 我一進(jìn)店門摊滔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人店乐,你說我怎么就攤上這事艰躺。” “怎么了眨八?”我有些...
    開封第一講書人閱讀 152,998評論 0 344
  • 文/不壞的土叔 我叫張陵描滔,是天一觀的道長。 經(jīng)常有香客問我踪古,道長含长,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,323評論 1 279
  • 正文 為了忘掉前任伏穆,我火速辦了婚禮拘泞,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘枕扫。我一直安慰自己陪腌,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,355評論 5 374
  • 文/花漫 我一把揭開白布烟瞧。 她就那樣靜靜地躺著诗鸭,像睡著了一般。 火紅的嫁衣襯著肌膚如雪参滴。 梳的紋絲不亂的頭發(fā)上强岸,一...
    開封第一講書人閱讀 49,079評論 1 285
  • 那天,我揣著相機與錄音砾赔,去河邊找鬼蝌箍。 笑死,一個胖子當(dāng)著我的面吹牛暴心,可吹牛的內(nèi)容都是我干的妓盲。 我是一名探鬼主播,決...
    沈念sama閱讀 38,389評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼专普,長吁一口氣:“原來是場噩夢啊……” “哼悯衬!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起檀夹,我...
    開封第一講書人閱讀 37,019評論 0 259
  • 序言:老撾萬榮一對情侶失蹤筋粗,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后击胜,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體亏狰,經(jīng)...
    沈念sama閱讀 43,519評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,971評論 2 325
  • 正文 我和宋清朗相戀三年偶摔,在試婚紗的時候發(fā)現(xiàn)自己被綠了暇唾。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,100評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡辰斋,死狀恐怖策州,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情宫仗,我是刑警寧澤够挂,帶...
    沈念sama閱讀 33,738評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站藕夫,受9級特大地震影響孽糖,放射性物質(zhì)發(fā)生泄漏枯冈。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,293評論 3 307
  • 文/蒙蒙 一办悟、第九天 我趴在偏房一處隱蔽的房頂上張望尘奏。 院中可真熱鬧,春花似錦病蛉、人聲如沸炫加。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽俗孝。三九已至,卻和暖如春魄健,著一層夾襖步出監(jiān)牢的瞬間赋铝,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評論 1 262
  • 我被黑心中介騙來泰國打工诀艰, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留柬甥,地道東北人。 一個月前我還...
    沈念sama閱讀 45,547評論 2 354
  • 正文 我出身青樓其垄,卻偏偏與公主長得像苛蒲,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子绿满,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,834評論 2 345

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