我的Kotlin 學習之路(六)Kotlin之coroutines 框架的使用

Coroutine -> 協程
不同于線程晴埂,協程不占用CPU,它只占用內存來處理耗時操作寻定。Coroutine的原理有大牛的視頻已經介紹了儒洛,我就不搬門弄斧了,這一章主要講講Kotlinx.coroutines 這個庫的使用方法特姐,有關于這個的中文講解還不是很多晶丘!

我們依舊使用官方文檔中加載google天氣的例子來代替耗時操作

一、導入框架
compile 'org.jetbrains.kotlinx:kotlinx-coroutines-core:0.16'

二唐含、Request類

/**
 * 網絡請求類
 * Created by zr on 2017/5/23.
 */
class Request (val url:String){

    fun run():String{
        Log.e(javaClass.simpleName,"*******************************************")
        Log.e(javaClass.simpleName,"url = "+url)

        var forecastJsonUrl = URL(url).readText()

        Log.e(javaClass.simpleName,"json = "+forecastJsonUrl)
        return forecastJsonUrl
    }
}

三浅浮、上最簡單的協程


fun coroution(){
        launch(CommonPool){ //協程
            Request(url).run() //耗時操作在CommonPool線程池中
        }
        Log.e(TAG, "開始請求")
    }


結果
06-27 13:01:58.928 16919-16919/test.futsal.com.mykotlin E/MainActivity: 開始請求
06-27 13:01:58.928 16919-19406/test.futsal.com.mykotlin E/Request: *******************************************
06-27 13:01:58.928 16919-19406/test.futsal.com.mykotlin E/Request: url = http://api.openweathermap.org/data/2.5/forecast/daily?mode=json&units=metric&cnt=7&APPID=15646a06818f61f7b8d7823ca833e1ce&id=2038349
06-27 13:01:59.936 16919-19406/test.futsal.com.mykotlin E/Request: json = {"city":{"id":2038349,"name":"Beijing Shi","coord":{"lon":116.3971,"lat":39.9169},"country":"CN","population":0},"cod":"200","message":10.1232268,"cnt":7,"list":[{"dt":1498536000,"temp":{"day":31,"min":23.42,"max":33.85,"night":23.42,"eve":33.61,"morn":31},"pressure":996.6,"humidity":64,"weather":[{"id":800,"main":"Clear","description":"sky is clear","icon":"01d"}],"speed":1.83,"deg":141,"clouds":0},{"dt":1498622400,"temp":{"day":30.6,"min":19.7,"max":33.27,"night":26.23,"eve":32.46,"morn":19.7},"pressure":996.96,"humidity":61,"weather":[{"id":800,"main":"Clear","description":"sky is clear","icon":"01d"}],"speed":2.06,"deg":172,"clouds":0},{"dt":1498708800,"temp":{"day":30.33,"min":21.67,"max":33.34,"night":23.4,"eve":32.87,"morn":21.67},"pressure":997.1,"humidity":62,"weather":[{"id":802,"main":"Clouds","description":"scattered clouds","icon":"03d"}],"speed":1.63,"deg":146,"clouds":48},{"dt":1498795200,"temp":{"day":32.12,"min":19.89,"max":32.12,"night":19.89,"eve":26.48,"morn":25.23},"pressure":966.77,"humidity":0,"weather":[{"id":800,"main":"Clear","description":"sky is clear","icon":"01d"}],"speed":1.62,"deg":178,"clouds":27},{"dt":1498881600,"temp":{"day":31.48,"min":19.94,"max":31.48,"night":19.94,"eve":26.16,"morn":24.87},"pressure":966.47,"humidity":0,"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10d"}],"speed":1.54,"deg":167,"clouds":43},{"dt":1498968000,"temp":{"day":32.06,"min":20.86,"max":32.06,"night":20.86,"eve":27.1,"morn":25.03},"pressure":965.1,"humidity":0,"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10d"}],"speed":1.05,"deg":119,"clouds":13,"rain":0.38},{"dt":1499054400,"temp":{"day":29.36,"min":21.53,"max":29.36,"night":21.53,"eve":25.73,"morn":24.72},"pressure":965.4,"humidity":0,"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10d"}],"speed":1.35,"deg":136,"clouds":13,"rain":2.03}]}



CommonPool 是 Coroutine的分發(fā)類的對象

/*
 * Copyright 2016-2017 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package kotlinx.coroutines.experimental

import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicInteger
import kotlin.coroutines.experimental.CoroutineContext

/**
 * Represents common pool of shared threads as coroutine dispatcher for compute-intensive tasks.
 * It uses [java.util.concurrent.ForkJoinPool] when available, which implements efficient work-stealing algorithm for its queues, so every
 * coroutine resumption is dispatched as a separate task even when it already executes inside the pool.
 * When available, it wraps `ForkJoinPool.commonPool` and provides a similar shared pool where not.
 */
object CommonPool : CoroutineDispatcher() {
    private var usePrivatePool = false

    @Volatile
    private var _pool: ExecutorService? = null

    private inline fun <T> Try(block: () -> T) = try { block() } catch (e: Throwable) { null }

    private fun createPool(): ExecutorService {
        val fjpClass = Try { Class.forName("java.util.concurrent.ForkJoinPool") }
            ?: return createPlainPool()
        if (!usePrivatePool) {
            Try { fjpClass.getMethod("commonPool")?.invoke(null) as? ExecutorService }
                ?.let { return it }
        }
        Try { fjpClass.getConstructor(Int::class.java).newInstance(defaultParallelism()) as? ExecutorService }
            ?. let { return it }
        return createPlainPool()
    }

    private fun createPlainPool(): ExecutorService {
        val threadId = AtomicInteger()
        return Executors.newFixedThreadPool(defaultParallelism()) {
            Thread(it, "CommonPool-worker-${threadId.incrementAndGet()}").apply { isDaemon = true }
        }
    }

    private fun defaultParallelism() = (Runtime.getRuntime().availableProcessors() - 1).coerceAtLeast(1)

    @Synchronized
    private fun getOrCreatePoolSync(): ExecutorService =
        _pool ?: createPool().also { _pool = it }

    override fun dispatch(context: CoroutineContext, block: Runnable) =
        (_pool ?: getOrCreatePoolSync()).execute(block)

    // used for tests
    @Synchronized
    internal fun usePrivatePool() {
        shutdownAndRelease(0)
        usePrivatePool = true
    }

    // used for tests
    @Synchronized
    internal fun shutdownAndRelease(timeout: Long) {
        _pool?.apply {
            shutdown()
            if (timeout > 0)
                awaitTermination(timeout, TimeUnit.MILLISECONDS)
            _pool = null
        }
        usePrivatePool = false
    }

    override fun toString(): String = "CommonPool"
}

四、在主線程中操作

fun coroution() = runBlocking<Unit> { //runBlocking代表在主線程中
        launch(CommonPool){
            Request(url).run()
        }
        Log.e(TAG, "開始請求")
    }

五捷枯、帶返回值的協程

fun coroution()= runBlocking<Unit> {
        val job = async(CommonPool){ //async 替代了launch (defer已過時)
            Request(url).run()
        }
        job.join() // async()結束后才進行下面的代碼滚秩,否則掛起等待
        
        var result:String = job.await() //await()獲得async方法的返回值
        Log.e(TAG,"result = $result")

        result?.let {
            var forecastResult: ResponseClasses.ForecastResult
            forecastResult = Gson().fromJson(result, ResponseClasses.ForecastResult::class.java)
            longToast("成功")
            toolbar.title = forecastResult.city.name
            tv_main.text = ""
        }

    }


結果
06-27 13:17:19.214 21646-22256/test.futsal.com.mykotlin E/Request: *******************************************
06-27 13:17:19.215 21646-22256/test.futsal.com.mykotlin E/Request: url = http://api.openweathermap.org/data/2.5/forecast/daily?mode=json&units=metric&cnt=7&APPID=15646a06818f61f7b8d7823ca833e1ce&id=2038349
06-27 13:17:20.213 21646-22256/test.futsal.com.mykotlin E/Request: json = {"city":{"id":2038349,"name":"Beijing Shi","coord":{"lon":116.3971,"lat":39.9169},"country":"CN","population":0},"cod":"200","message":11.058764,"cnt":7,"list":[{"dt":1498536000,"temp":{"day":31,"min":23.42,"max":33.85,"night":23.42,"eve":33.61,"morn":31},"pressure":996.6,"humidity":64,"weather":[{"id":800,"main":"Clear","description":"sky is clear","icon":"01d"}],"speed":1.83,"deg":141,"clouds":0},{"dt":1498622400,"temp":{"day":30.6,"min":19.7,"max":33.27,"night":26.23,"eve":32.46,"morn":19.7},"pressure":996.96,"humidity":61,"weather":[{"id":800,"main":"Clear","description":"sky is clear","icon":"01d"}],"speed":2.06,"deg":172,"clouds":0},{"dt":1498708800,"temp":{"day":30.33,"min":21.67,"max":33.34,"night":23.4,"eve":32.87,"morn":21.67},"pressure":997.1,"humidity":62,"weather":[{"id":802,"main":"Clouds","description":"scattered clouds","icon":"03d"}],"speed":1.63,"deg":146,"clouds":48},{"dt":1498795200,"temp":{"day":32.12,"min":19.89,"max":32.12,"night":19.89,"eve":26.48,"morn":25.23},"pressure":966.77,"humidity":0,"weather":[{"id":800,"main":"Clear","description":"sky is clear","icon":"01d"}],"speed":1.62,"deg":178,"clouds":27},{"dt":1498881600,"temp":{"day":31.48,"min":19.94,"max":31.48,"night":19.94,"eve":26.16,"morn":24.87},"pressure":966.47,"humidity":0,"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10d"}],"speed":1.54,"deg":167,"clouds":43},{"dt":1498968000,"temp":{"day":32.06,"min":20.86,"max":32.06,"night":20.86,"eve":27.1,"morn":25.03},"pressure":965.1,"humidity":0,"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10d"}],"speed":1.05,"deg":119,"clouds":13,"rain":0.38},{"dt":1499054400,"temp":{"day":29.36,"min":21.53,"max":29.36,"night":21.53,"eve":25.73,"morn":24.72},"pressure":965.4,"humidity":0,"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10d"}],"speed":1.35,"deg":136,"clouds":13,"rain":2.03}]}
06-27 13:17:20.214 21646-21646/test.futsal.com.mykotlin E/MainActivity: result = {"city":{"id":2038349,"name":"Beijing Shi","coord":{"lon":116.3971,"lat":39.9169},"country":"CN","population":0},"cod":"200","message":11.058764,"cnt":7,"list":[{"dt":1498536000,"temp":{"day":31,"min":23.42,"max":33.85,"night":23.42,"eve":33.61,"morn":31},"pressure":996.6,"humidity":64,"weather":[{"id":800,"main":"Clear","description":"sky is clear","icon":"01d"}],"speed":1.83,"deg":141,"clouds":0},{"dt":1498622400,"temp":{"day":30.6,"min":19.7,"max":33.27,"night":26.23,"eve":32.46,"morn":19.7},"pressure":996.96,"humidity":61,"weather":[{"id":800,"main":"Clear","description":"sky is clear","icon":"01d"}],"speed":2.06,"deg":172,"clouds":0},{"dt":1498708800,"temp":{"day":30.33,"min":21.67,"max":33.34,"night":23.4,"eve":32.87,"morn":21.67},"pressure":997.1,"humidity":62,"weather":[{"id":802,"main":"Clouds","description":"scattered clouds","icon":"03d"}],"speed":1.63,"deg":146,"clouds":48},{"dt":1498795200,"temp":{"day":32.12,"min":19.89,"max":32.12,"night":19.89,"eve":26.48,"morn":25.23},"pressure":966.77,"humidity":0,"weather":[{"id":800,"main":"Clear","description":"sky is clear","icon":"01d"}],"speed":1.62,"deg":178,"clouds":27},{"dt":1498881600,"temp":{"day":31.48,"min":19.94,"max":31.48,"night":19.94,"eve":26.16,"morn":24.87},"pressure":966.47,"humidity":0,"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10d"}],"speed":1.54,"deg":167,"clouds":43},{"dt":1498968000,"temp":{"day":32.06,"min":20.86,"max":32.06,"night":20.86,"eve":27.1,"morn":25.03},"pressure":965.1,"humidity":0,"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10d"}],"speed":1.05,"deg":119,"clouds":13,"rain":0.38},{"dt":1499054400,"temp":{"day":29.36,"min":21.53,"max":29.36,"night":21.53,"eve":25.73,"morn":24.72},"pressure":965.4,"humidity":0,"weather":[{"id":500,"main":"Rain","description":"light rain","icon":"10d"}],"speed":1.35,"deg":136,"clouds":13,"rain":2.03}]}



六、取消

fun coroution()= runBlocking<Unit> {
        var result:String? = null
        val job = launch(CommonPool){
            result = Request(url).run()
        }
    job.cancel(Exception("Activity is onPause")) //取消并告知原因
    job.join()
    Log.e(TAG,"result = $result")
        result?.let {
            var forecastResult: ResponseClasses.ForecastResult
            forecastResult = Gson().fromJson(result, ResponseClasses.ForecastResult::class.java)
            longToast("成功")
            toolbar.title = forecastResult.city.name
            
            tv_main.text = ""
        }
    }

取消后的異常

QQ圖片20170627101735.png

七淮捆、async(CommonPool)的構造方法

/**
 * Creates new coroutine and returns its future result as an implementation of [Deferred].
 *
 * The running coroutine is cancelled when the resulting object is [cancelled][Job.cancel].
 * The [context] for the new coroutine must be explicitly specified.
 * See [CoroutineDispatcher] for the standard [context] implementations that are provided by `kotlinx.coroutines`.
 * The [context][CoroutineScope.context] of the parent coroutine from its [scope][CoroutineScope] may be used,
 * in which case the [Job] of the resulting coroutine is a child of the job of the parent coroutine.
 *
 * By default, the coroutine is immediately scheduled for execution.
 * Other options can be specified via `start` parameter. See [CoroutineStart] for details.
 * An optional [start] parameter can be set to [CoroutineStart.LAZY] to start coroutine _lazily_. In this case,,
 * the resulting [Deferred] is created in _new_ state. It can be explicitly started with [start][Job.start]
 * function and will be started implicitly on the first invocation of [join][Job.join] or [await][Deferred.await].
 *
 * @param context context of the coroutine
 * @param start coroutine start option
 * @param block the coroutine code
 */
public fun <T> async(
    context: CoroutineContext,
    start: CoroutineStart = CoroutineStart.DEFAULT,
    block: suspend CoroutineScope.() -> T
): Deferred<T> {
    val newContext = newCoroutineContext(context)
    val coroutine = if (start.isLazy)
        LazyDeferredCoroutine(newContext, block) else
        DeferredCoroutine<T>(newContext, active = true)
    coroutine.initParentJob(context[Job])
    start(block, coroutine, coroutine)
    return coroutine
}

試試 第二個參數 start: CoroutineStart
async(CommonPool, CoroutineStart.LAZY)
async(CommonPool, CoroutineStart.ATOMIC)
async(CommonPool, CoroutineStart.UNDISPATCHED)
再看CoroutineStart類

/*
 * Copyright 2016-2017 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package kotlinx.coroutines.experimental

import kotlinx.coroutines.experimental.CoroutineStart.*
import kotlinx.coroutines.experimental.intrinsics.startCoroutineUndispatched
import kotlin.coroutines.experimental.Continuation
import kotlin.coroutines.experimental.startCoroutine

/**
 * Defines start option for coroutines builders.
 * It is used in `start` parameter of [launch], [async], and [actor][kotlinx.coroutines.experimental.channels.actor]
 * coroutine builder functions.
 *
 * The summary of coroutine start options is:
 * * [DEFAULT] -- immediately schedules coroutine for execution according to its context;
 * * [LAZY] -- starts coroutine lazily, only when it is needed;
 * * [ATOMIC] -- atomically (non-cancellably) schedules coroutine for execution according to its context;
 * * [UNDISPATCHED] -- immediately executes coroutine until its first suspension point _in the current thread_.
 */
public enum class CoroutineStart {
    /**
     * Default -- immediately schedules coroutine for execution according to its context.
     *
     * If the [CoroutineDispatcher] of the coroutine context returns `true` from [CoroutineDispatcher.isDispatchNeeded]
     * function as most dispatchers do, then the coroutine code is dispatched for execution later, while the code that
     * invoked the coroutine builder continues execution.
     *
     * Note, that [Unconfined] dispatcher always returns `false` from its [CoroutineDispatcher.isDispatchNeeded]
     * function, so starting coroutine with [Unconfined] dispatcher by [DEFAULT] is the same as using [UNDISPATCHED].
     *
     * If coroutine [Job] is cancelled before it even had a chance to start executing, then it will not start its
     * execution at all, but complete with an exception.
     *
     * Cancellability of coroutine at suspension points depends on the particular implementation details of
     * suspending functions. Use [suspendCancellableCoroutine] to implement cancellable suspending functions.
     */
    DEFAULT,

    /**
     * Starts coroutine lazily, only when it is needed.
     *
     * See the documentation for the corresponding coroutine builders for details:
     * [launch], [async], and [actor][kotlinx.coroutines.experimental.channels.actor].
     *
     * If coroutine [Job] is cancelled before it even had a chance to start executing, then it will not start its
     * execution at all, but complete with an exception.
     */
    LAZY,

    /**
     * Atomically (non-cancellably) schedules coroutine for execution according to its context.
     * This is similar to [DEFAULT], but the coroutine cannot be cancelled before it starts executing.
     *
     * Cancellability of coroutine at suspension points depends on the particular implementation details of
     * suspending functions as in [DEFAULT].
     */
    ATOMIC,

    /**
     * Immediately executes coroutine until its first suspension point _in the current thread_ as if it the
     * coroutine was started using [Unconfined] dispatcher. However, when coroutine is resumed from suspension
     * it is dispatched according to the [CoroutineDispatcher] in its context.
     *
     * This is similar to [ATOMIC] in the sense that coroutine starts executing even if it was already cancelled,
     * but the difference is that it start executing in the same thread.
     *
     * Cancellability of coroutine at suspension points depends on the particular implementation details of
     * suspending functions as in [DEFAULT].
     */
    UNDISPATCHED;

    /**
     * Starts the corresponding block as a coroutine with this coroutine start strategy.
     *
     * * [DEFAULT] uses [startCoroutineCancellable].
     * * [ATOMIC] uses [startCoroutine].
     * * [UNDISPATCHED] uses [startCoroutineUndispatched].
     * * [LAZY] does nothing.
     */
    public operator fun <T> invoke(block: suspend () -> T, completion: Continuation<T>) =
        when (this) {
            CoroutineStart.DEFAULT -> block.startCoroutineCancellable(completion)
            CoroutineStart.ATOMIC -> block.startCoroutine(completion)
            CoroutineStart.UNDISPATCHED -> block.startCoroutineUndispatched(completion)
            CoroutineStart.LAZY -> Unit // will start lazily
        }

    /**
     * Starts the corresponding block with receiver as a coroutine with this coroutine start strategy.
     *
     * * [DEFAULT] uses [startCoroutineCancellable].
     * * [ATOMIC] uses [startCoroutine].
     * * [UNDISPATCHED] uses [startCoroutineUndispatched].
     * * [LAZY] does nothing.
     */
    public operator fun <R, T> invoke(block: suspend R.() -> T, receiver: R, completion: Continuation<T>) =
        when (this) {
            CoroutineStart.DEFAULT -> block.startCoroutineCancellable(receiver, completion)
            CoroutineStart.ATOMIC -> block.startCoroutine(receiver, completion)
            CoroutineStart.UNDISPATCHED -> block.startCoroutineUndispatched(receiver, completion)
            CoroutineStart.LAZY -> Unit // will start lazily
        }

    /**
     * Returns `true` when [LAZY].
     */
    public val isLazy: Boolean get() = this === LAZY
}


(我英語不好郁油,成人三級一直沒過,請包涵攀痊,但我不會瞎說的)
DEFAULT -> 立即執(zhí)行耗時操作
LAZY -> 在async 的 await()時才進行耗時操作 Starts coroutine lazily, only when it is needed.
ATOMIC -> 和DEFAULT很接近桐腌,但是不能取消 (non-cancellably)
UNDISPATCHED-> 只在當前線程執(zhí)行,不切換線程苟径,如果是在主線程案站,剛才的方法會拋出這樣的異常

QQ圖片20170627105748.png

我最后的代碼

val url = "http://api.openweathermap.org/data/2.5/forecast/daily?mode=json&units=metric&cnt=7&APPID=15646a06818f61f7b8d7823ca833e1ce&id=2038349"

fun coroution()= runBlocking<Unit> {
        val result = async(CommonPool){
            Request(url).run()
        }.await()

        Log.e(TAG,"result = $result")
        result?.let {
            var forecastResult: ResponseClasses.ForecastResult
            forecastResult = Gson().fromJson(result, ResponseClasses.ForecastResult::class.java)
            longToast("成功")
            toolbar.title = forecastResult.city.name
            tv_main.text = ""
            //加載adapter的略過
        }

    }

最后的樣子

QQ圖片20170627123059.png
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市棘街,隨后出現的幾起案子蟆盐,更是在濱河造成了極大的恐慌,老刑警劉巖遭殉,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件石挂,死亡現場離奇詭異,居然都是意外死亡险污,警方通過查閱死者的電腦和手機痹愚,發(fā)現死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來蛔糯,“玉大人里伯,你說我怎么就攤上這事〔趁疲” “怎么了疾瓮?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長飒箭。 經常有香客問我狼电,道長,這世上最難降的妖魔是什么弦蹂? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任肩碟,我火速辦了婚禮,結果婚禮上凸椿,老公的妹妹穿的比我還像新娘削祈。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布髓抑。 她就那樣靜靜地躺著咙崎,像睡著了一般。 火紅的嫁衣襯著肌膚如雪吨拍。 梳的紋絲不亂的頭發(fā)上褪猛,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天,我揣著相機與錄音羹饰,去河邊找鬼伊滋。 笑死,一個胖子當著我的面吹牛队秩,可吹牛的內容都是我干的笑旺。 我是一名探鬼主播,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼馍资,長吁一口氣:“原來是場噩夢啊……” “哼筒主!你這毒婦竟也來了?” 一聲冷哼從身側響起迷帜,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤物舒,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后戏锹,有當地人在樹林里發(fā)現了一具尸體冠胯,經...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年锦针,在試婚紗的時候發(fā)現自己被綠了荠察。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,785評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡奈搜,死狀恐怖悉盆,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情馋吗,我是刑警寧澤焕盟,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站宏粤,受9級特大地震影響脚翘,放射性物質發(fā)生泄漏。R本人自食惡果不足惜绍哎,卻給世界環(huán)境...
    茶點故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一来农、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧崇堰,春花似錦沃于、人聲如沸涩咖。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽檩互。三九已至,卻和暖如春蒋困,著一層夾襖步出監(jiān)牢的瞬間盾似,已是汗流浹背敬辣。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工雪标, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人溉跃。 一個月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓村刨,卻偏偏與公主長得像,于是被迫代替她去往敵國和親撰茎。 傳聞我的和親對象是個殘疾皇子嵌牺,可洞房花燭夜當晚...
    茶點故事閱讀 44,713評論 2 354

推薦閱讀更多精彩內容