從AsyncTask聊到JobIntentService

一.AsyncTask分析

AsyncTask本質(zhì)是上只是一個框架,內(nèi)部封裝了Thread+Handler機(jī)制,不需要控制線程和Handler,可以輕松實(shí)現(xiàn)后臺計算,發(fā)布進(jìn)度和結(jié)果到主線程救巷。需要注意的是
1.The task can be executed only once
2.The task instance must be created on the UI thread
3.execute must be invoked on the UI thread

1.AsyncTask用例如下:

 * private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
 *     protected Long doInBackground(URL... urls) {
 *         int count = urls.length;
 *         long totalSize = 0;
 *         for (int i = 0; i < count; i++) {
 *             totalSize += Downloader.downloadFile(urls[i]);
 *             publishProgress((int) ((i / (float) count) * 100));
 *             // Escape early if cancel() is called
 *             if (isCancelled()) break;
 *         }
 *         return totalSize;
 *     }
 *
 *     protected void onProgressUpdate(Integer... progress) {
 *         setProgressPercent(progress[0]);
 *     }
 *
 *     protected void onPostExecute(Long result) {
 *         showDialog("Downloaded " + result + " bytes");
 *     }
 * }

new DownloadFilesTask().execute(url1, url2, url3);

AsyncTask內(nèi)部流程如下:

When an asynchronous task is executed, the task goes through 4 steps:

    1. onPreExecute, invoked on the UI thread before the task is executed. This step is normally used to setup the task, for instance by showing a progress bar in the user interface.
    1. doInBackground, invoked on the background thread immediately after onPreExecute() finishes executing. This step is used to perform background computation that can take a long time. The parameters of the asynchronous task are passed to this step. The result of the computation must be returned by this step and will be passed back to the last step. This step can also use publishProgress to publish one or more units of progress. These values are published on the UI thread, in the onProgressUpdate step.
    1. onProgressUpdate, invoked on the UI thread after a call to publishProgress. The timing of the execution is undefined. This method is used to display any form of progress in the user interface while the background computation is still executing. For instance, it can be used to animate a progress bar or show logs in a text field.
    1. onPostExecute, invoked on the UI thread after the background computation finishes. The result of the background computation is passed to this step as a parameter.

基本如下操作:AsyncTask框架有三個泛型三個,分別是輸入的多個參數(shù)句柠、處理中的進(jìn)度浦译、處理后的結(jié)果;excute操作時, onPreExecute在主線程做執(zhí)行前操作溯职,doInBackground做后臺操作精盅,在操作的同時發(fā)布Progress進(jìn)度,回調(diào)在onProgressUpdate中谜酒,這是主線程叹俏,doInBackground return的結(jié)果回調(diào)在onPostExecute中。實(shí)例化的AsyncTask通過execute

2.在基于這樣的大致框架下僻族,我們?nèi)绾巫孕袑?shí)現(xiàn)以上功能粘驰?

本質(zhì)上就是在execute時,起一個工作線程述么,執(zhí)行后臺計算蝌数。工作線程執(zhí)行前,調(diào)用onPreExecute度秘;通過主線程looper起一個Handler顶伞,用于主線程與工作線程的通信。執(zhí)行在publishProgress方法中handler.sendmessage剑梳,在handler.handleMessage調(diào)用onProgressUpdate,在工作線程結(jié)束后handler.sendmessage唆貌,計算結(jié)果放在obj屬性,handler.handleMessage調(diào)用onPostExecute即可垢乙。

3.源碼分析

AsyncTask框架內(nèi)處理后臺任務(wù)锨咙,是要起線程的。其內(nèi)部線程創(chuàng)建是通過線程池的追逮,即單例的線程池蓖租,在第一次AsyncTask類加載時,便會初始化(static方法塊)羊壹;在初始化類時,具體走構(gòu)造器時齐婴,會初始化Handler與一個FutureTask(為什么要用FutureTask執(zhí)行異步任務(wù)油猫,是為了獲得異步計算的結(jié)果,而且FutureTask也是一個可取消的異步計算柠偶,Runable或者繼承Thread做不到這些特性)

這里拓展一下情妖,談一下線程創(chuàng)建
Java線程創(chuàng)建本身都是通過實(shí)例化Thread去做的睬关,至于如何實(shí)例化Thead有以下幾種方式(如何實(shí)現(xiàn)run內(nèi)部邏輯代碼)

  • 繼承Thread類實(shí)現(xiàn)多線程
 *     class PrimeThread extends Thread {
 *         long minPrime;
 *         PrimeThread(long minPrime) {
 *             this.minPrime = minPrime;
 *         }
 *
 *         public void run() {
 *             // compute primes larger than minPrime
 *             &nbsp;.&nbsp;.&nbsp;.
 *         }
 *     }
 *     PrimeThread p = new PrimeThread(143);
 *     p.start();
  • 實(shí)現(xiàn)Runnable()接口實(shí)現(xiàn)多線程,而后同樣覆寫run()
 *     class PrimeRun implements Runnable {
 *         long minPrime;
 *         PrimeRun(long minPrime) {
 *             this.minPrime = minPrime;
 *         }
 *
 *         public void run() {
 *             // compute primes larger than minPrime
 *             &nbsp;.&nbsp;.&nbsp;.
 *         }
 *     }
 *     PrimeRun p = new PrimeRun(143);
 *     new Thread(p).start();
  • 實(shí)現(xiàn)Callable接口(本質(zhì)也是Runnable方式毡证,不同的是有后臺計算的返回值)
 *     class PrimeRun implements Callable<String> {
 *
 *         public String call() throws Exception{
 *             // compute primes larger than minPrime
 *             &nbsp;.&nbsp;.&nbsp;.
 *             return "demo";
 *         }
 *     }
 *     PrimeRun callable = new PrimeRun();
 *     new Thread(new FutureTask<>(callable)).start();

FutureTask is a cancellable asynchronous computation,即FutureTask是一個可取消的異步計算电爹,本身實(shí)現(xiàn)了Runnable、Future接口料睛,實(shí)現(xiàn)了Runnable的run方法(有了線程異步的能力)丐箩,實(shí)現(xiàn)了Future接口的get,cancel恤煞,isCancelled屎勘,isDone方法(有了可取消的異步計算結(jié)果的能力)
AsyncTask有三個狀態(tài)如下:

/**
     * Indicates the current status of the task. Each status will be set only once
     * during the lifetime of a task.
     */
    public enum Status {
        /**
         * Indicates that the task has not been executed yet.
         */
        PENDING,
        /**
         * Indicates that the task is running.
         */
        RUNNING,
        /**
         * Indicates that {@link AsyncTask#onPostExecute} has finished.
         */
        FINISHED,
    }

如備注所說,這個狀態(tài)的功能就是為了讓每個任務(wù)只執(zhí)行一次居扒。其中PENDING指的是任務(wù)還未被執(zhí)行概漱,RUNNING指的是任務(wù)執(zhí)行運(yùn)行中,F(xiàn)INISHED指的是onPostExecute方法已結(jié)束

返回到AsyncTask構(gòu)造器講解喜喂,源碼如下:

    public AsyncTask(@Nullable Looper callbackLooper) {
        mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
            ? getMainHandler()
            : new Handler(callbackLooper);

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

        mFuture = new FutureTask<Result>(mWorker) {
            @Override
            protected void done() {
                try {
                    postResultIfNotInvoked(get());
                } catch (InterruptedException e) {
                    android.util.Log.w(LOG_TAG, e);
                } catch (ExecutionException e) {
                    throw new RuntimeException("An error occurred while executing doInBackground()",
                            e.getCause());
                } catch (CancellationException e) {
                    postResultIfNotInvoked(null);
                }
            }
        };
    }

這里初始化了FutureTask,異步執(zhí)行的是doInBackground瓤摧,現(xiàn)將mTaskInvoked愿子變量置為true,代表任務(wù)已被調(diào)用玉吁。doInBackground結(jié)束后照弥,send一個MESSAGE_POST_RESULT的Message,handler接受到消息诈茧,執(zhí)行finish方法产喉,任務(wù)結(jié)束后執(zhí)行postResultIfNotInvoked,該方法會檢查mTaskInvoked是否為false敢会,即任務(wù)未被調(diào)用的話曾沈,也會send一個MESSAGE_POST_RESULT的Message
finish方法源碼如下:

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

isCancelled的原子變量為true的情況要么是用戶調(diào)用cancle方法,要么doInBackground發(fā)生異常鸥昏。finsh方法判斷AsyncTask是否可取消塞俱,如果可取消,調(diào)用onCancelled吏垮,不能取消則執(zhí)行onPostExecute障涯。總是將AsyncTask的狀態(tài)置為Finished膳汪。
以上都初始化好了之后唯蝶,當(dāng)用戶調(diào)用excute方法,excute方法會調(diào)用executeOnExecutor遗嗽,不僅要傳入params可變參數(shù)粘我,還有傳入執(zhí)行器,為什么要傳入執(zhí)行器呢痹换?其實(shí)AsyncTask本質(zhì)是一個線程征字,不過是加了Handler的線程都弹,AsyncTask內(nèi)部維護(hù)了一個線程池,當(dāng)用戶起了多個AsyncTask匙姜,這個時候畅厢,就要考慮AsyncTask內(nèi)部線程該如何調(diào)用;系統(tǒng)默認(rèn)的是串行執(zhí)行器氮昧,源碼如下:

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

由源碼可知框杜,系統(tǒng)默認(rèn)對于多個AsyncTask采用了串行執(zhí)行器,即一個線程執(zhí)行完之后執(zhí)行雙端隊(duì)列的下一個線程

executeOnExecutor源碼如下:

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

在excute執(zhí)行方法內(nèi)可知郭计,AsyncTask為了只執(zhí)行一個Task霸琴,通過狀態(tài)判斷,只要狀態(tài)執(zhí)行中昭伸,或者已結(jié)束梧乘,就拋出異常。之后將狀態(tài)置為RUNNING狀態(tài)庐杨,在調(diào)用onPreExecute选调,將可變參數(shù)傳給workrunbale對象中,之后通過執(zhí)行器執(zhí)行在構(gòu)造器時已初始化的futuretask的線程灵份。

4.重談AsyncTask.cancle方法

AsyncTask類cancle方法仁堪,除了將原子變量mCancelled置為true,還執(zhí)行了FutureTask的cancle方法填渠,F(xiàn)utureTask的cancle源碼如下:

    public boolean cancel(boolean mayInterruptIfRunning) {
        if (!(state == NEW &&
              U.compareAndSwapInt(this, STATE, NEW,
                  mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
            return false;
        try {    // in case call to interrupt throws exception
            if (mayInterruptIfRunning) {
                try {
                    Thread t = runner;
                    if (t != null)
                        t.interrupt();
                } finally { // final state
                    U.putOrderedInt(this, STATE, INTERRUPTED);
                }
            }
        } finally {
            finishCompletion();
        }
        return true;
    }

其實(shí)canle方法最終調(diào)用了線程的interrupt方法弦聂;線程的interrupt()方法,根據(jù)字面意思氛什,很容易將該方法理解為中斷線程莺葫。其實(shí)Thread.interrupt()并不會中斷線程的運(yùn)行,它的作用僅僅是為線程設(shè)定一個狀態(tài)而已枪眉,即標(biāo)明線程是中斷狀態(tài)捺檬,這樣線程的調(diào)度機(jī)制或我們的代碼邏輯就可以通過判斷這個狀態(tài)做一些處理,比如sleep()方法會拋出異常贸铜,或是我們根據(jù)isInterrupted()方法判斷線程是否處于中斷狀態(tài)堡纬,然后做相關(guān)的邏輯處理。

由以上分析可知蒿秦,當(dāng)用戶調(diào)用AsyncTask的cancle方法時烤镐,onBackgroud所做的耗時任務(wù),只要正在進(jìn)行中棍鳖,依然還會進(jìn)行职车。所以onBackgroud內(nèi)還要判斷AsyncTask.isCancle方法

AsyncTask內(nèi)部是用Thread+Handler實(shí)現(xiàn),那與HandlerThred甚是相似,它與AsyncTask有什么不同呢悴灵?

5.與HandlerThread區(qū)別

HandlerThread本身繼承自Thread的類,這個衍生類是方便子線程創(chuàng)建Handler骂蓖,用于子線程與主線程之間积瞒,子線程與子線程之間的通信。
使用如下:

        /**
          * 步驟①:創(chuàng)建HandlerThread實(shí)例對象
          * 傳入?yún)?shù) = 線程名字登下,作用 = 標(biāo)記該線程
          */
        mHandlerThread = new HandlerThread("handlerThread");
        /**
         * 步驟②:啟動線程
         */
        mHandlerThread.start();
 
        /**
         * 步驟③:創(chuàng)建工作線程Handler & 復(fù)寫handleMessage()
         * 作用:關(guān)聯(lián)HandlerThread的Looper對象茫孔、實(shí)現(xiàn)消息處理操作 & 與其他線程進(jìn)行通信
         * 注:消息處理操作(HandlerMessage())的執(zhí)行線程 = mHandlerThread所創(chuàng)建的工作線程中執(zhí)行
         */
 
        workHandler = new Handler(mHandlerThread.getLooper()){
            @Override
            public void handleMessage(Message msg)
            {
          });

上面使用可知HandlerThread框架免去了Looper的初始化,與loop操作被芳。
其run方法如下:

    @Override
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }

在Handler構(gòu)造器可以傳入目標(biāo)線程不為空的Looper缰贝, 其中g(shù)etLooper源碼如下:

public Looper getLooper() {
        if (!isAlive()) {
            return null;
        }
        
        // If the thread has been started, wait until the looper has been created.
        synchronized (this) {
            while (isAlive() && mLooper == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                }
            }
        }
        return mLooper;
    }

這里要注意一點(diǎn)時,HandlerThread框架為了避免在獲取Looper時畔濒,出現(xiàn)為空的情況剩晴,在mLooper的set,與get操作加了同步鎖侵状;

由以上分析可知AsyncTask框架內(nèi)部Handler機(jī)制中子線程是生產(chǎn)者赞弥,主線程是消費(fèi)者。而HandlerThread框架內(nèi)部Handler機(jī)制中目標(biāo)子線程是消費(fèi)者趣兄,其他線程(可能是主線程)是生產(chǎn)者

三.延展分析IntentService

IntentService源碼實(shí)現(xiàn)很簡單绽左,其是Service的衍生類,本身Service組件是運(yùn)行在主線程中艇潭,為了讓Service處理耗時任務(wù)拼窥,在onCreate起了一個HandlerThread與Handler(解決子線程與主線程的通信),在onStartCommand/onStart生命周期中蹋凝,Handler將Intent包裹在message的obj屬性里鲁纠,send消息,通知到子線程中處理耗時任務(wù)仙粱,任務(wù)結(jié)束后結(jié)束Service房交。無需多講,源碼如下:

    private volatile ServiceHandler mServiceHandler;
    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);
    }

    @Override
    public void onStart(@Nullable Intent intent, int startId) {
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        mServiceHandler.sendMessage(msg);
    }

    /**
     * You should not override this method for your IntentService. Instead,
     * override {@link #onHandleIntent}, which the system calls when the IntentService
     * receives a start request.
     * @see android.app.Service#onStartCommand
     */
    @Override
    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
        onStart(intent, startId);
        return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
    }

但是伐割,但是候味,但是 安卓源碼對IntentService有如下備注:


Android O對應(yīng)用在后臺運(yùn)行時可以執(zhí)行的操作施加了限制,稱為后臺執(zhí)行限制(Background Execution Limits)隔心,這可以大大減少應(yīng)用的內(nèi)存使用和耗電量白群,提高用戶體驗(yàn)。后臺執(zhí)行限制分為兩個部分:后臺服務(wù)限制(Background Service Limitations)硬霍、廣播限制(BroadcastLimitations)帜慢。該文章詳解:android O 對Service的限制【Background Execution Limits】對此做了詳解.

對于Android8.0后臺執(zhí)行限制,官方推薦JobIntentService類,用法如下:

public class SimpleJobIntentService extends JobIntentService {
    /**
     * Unique job ID for this service.
     */
    static final int JOB_ID = 1000;

    /**
     * Convenience method for enqueuing work in to this service.
     */
    static void enqueueWork(Context context, Intent work) {
        enqueueWork(context, SimpleJobIntentService.class, JOB_ID, work);
    }

    @Override
    protected void onHandleWork(@NonNull Intent intent) {
        // We have received work to do.  The system or framework is already
        // holding a wake lock for us at this point, so we can just go.
        Log.i("SimpleJobIntentService", "Executing work: " + intent);
        String label = intent.getStringExtra("label");
        if (label == null) {
            label = intent.toString();
        }
        toast("Executing: " + label);
        for (int i = 0; i < 5; i++) {
            Log.i("SimpleJobIntentService", "Running service " + (i + 1)
                    + "/5 @ " + SystemClock.elapsedRealtime());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
            }
        }
        Log.i("SimpleJobIntentService", "Completed service @ " + SystemClock.elapsedRealtime());
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        toast("All work complete");
    }

    @SuppressWarnings("deprecation")
    final Handler mHandler = new Handler();

    // Helper for showing tests
    void toast(final CharSequence text) {
        mHandler.post(new Runnable() {
            @Override public void run() {
                Toast.makeText(SimpleJobIntentService.this, text, Toast.LENGTH_SHORT).show();
            }
        });
    }
}

由于Android O的后臺限制,創(chuàng)建后臺服務(wù)需要使用JobScheduler來由系統(tǒng)進(jìn)行調(diào)度任務(wù)的執(zhí)行粱玲,而使用JobService的方式比較繁瑣躬柬,8.0及以上提供了JobIntentService幫助開發(fā)者更方便的將任務(wù)交給JobScheduler調(diào)度,其本質(zhì)是Service后臺任務(wù)在他的OnhandleWork()中進(jìn)行抽减,子類重寫該方法即可允青。使用較簡單。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末卵沉,一起剝皮案震驚了整個濱河市颠锉,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌史汗,老刑警劉巖琼掠,帶你破解...
    沈念sama閱讀 219,490評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異停撞,居然都是意外死亡瓷蛙,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,581評論 3 395
  • 文/潘曉璐 我一進(jìn)店門怜森,熙熙樓的掌柜王于貴愁眉苦臉地迎上來速挑,“玉大人,你說我怎么就攤上這事副硅±驯Γ” “怎么了?”我有些...
    開封第一講書人閱讀 165,830評論 0 356
  • 文/不壞的土叔 我叫張陵恐疲,是天一觀的道長腊满。 經(jīng)常有香客問我,道長培己,這世上最難降的妖魔是什么碳蛋? 我笑而不...
    開封第一講書人閱讀 58,957評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮省咨,結(jié)果婚禮上肃弟,老公的妹妹穿的比我還像新娘。我一直安慰自己零蓉,他們只是感情好笤受,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,974評論 6 393
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著敌蜂,像睡著了一般箩兽。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上章喉,一...
    開封第一講書人閱讀 51,754評論 1 307
  • 那天汗贫,我揣著相機(jī)與錄音身坐,去河邊找鬼。 笑死落包,一個胖子當(dāng)著我的面吹牛部蛇,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播咐蝇,決...
    沈念sama閱讀 40,464評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼搪花,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了嘹害?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤吮便,失蹤者是張志新(化名)和其女友劉穎笔呀,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體髓需,經(jīng)...
    沈念sama閱讀 45,847評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡许师,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,995評論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了僚匆。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片微渠。...
    茶點(diǎn)故事閱讀 40,137評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖咧擂,靈堂內(nèi)的尸體忽然破棺而出逞盆,到底是詐尸還是另有隱情,我是刑警寧澤松申,帶...
    沈念sama閱讀 35,819評論 5 346
  • 正文 年R本政府宣布云芦,位于F島的核電站,受9級特大地震影響贸桶,放射性物質(zhì)發(fā)生泄漏舅逸。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,482評論 3 331
  • 文/蒙蒙 一皇筛、第九天 我趴在偏房一處隱蔽的房頂上張望琉历。 院中可真熱鬧,春花似錦水醋、人聲如沸旗笔。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,023評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽换团。三九已至,卻和暖如春宫蛆,著一層夾襖步出監(jiān)牢的瞬間艘包,已是汗流浹背的猛。 一陣腳步聲響...
    開封第一講書人閱讀 33,149評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留想虎,地道東北人卦尊。 一個月前我還...
    沈念sama閱讀 48,409評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像舌厨,于是被迫代替她去往敵國和親岂却。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,086評論 2 355

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