Kodien原理详解

一个依赖注入框架,无非就是注入与获取实例。那么kodein是怎么注入的?

注入

val kodein = Kodein {
    bind<Die>() with singleton { Die(instance()) } 

    bind<Random>() with singleton { Random(instance) } 
    constant(tag "max") with 5 
}

可以看到首先要初始化Kodien环境,

KodeinImpl

internal open class KodeinImpl internal constructor(private val _container: KodeinContainerImpl) : Kodein {

    @Suppress("unused")
    private constructor(builder: KodeinMainBuilderImpl, runCallbacks: Boolean) : this(KodeinContainerImpl(builder.containerBuilder, builder.externalSources, builder.fullDescriptionOnError, runCallbacks))

    constructor(allowSilentOverride: Boolean = false, init: Kodein.MainBuilder.() -> Unit) : this(newBuilder(allowSilentOverride, init), true)

    companion object {
        private fun newBuilder(allowSilentOverride: Boolean = false, init: Kodein.MainBuilder.() -> Unit) = KodeinMainBuilderImpl(allowSilentOverride).apply(init)

        fun withDelayedCallbacks(allowSilentOverride: Boolean = false, init: Kodein.MainBuilder.() -> Unit): Pair<Kodein, () -> Unit> {
            val kodein = KodeinImpl(newBuilder(allowSilentOverride, init), false)
            return kodein to { kodein._container.initCallbacks?.invoke() ; Unit }
        }
    }

    final override val container: KodeinContainer by lazy {
        if (_container.initCallbacks != null)
            throw IllegalStateException("Kodein has not been initialized")
        _container
    }

}

这里会内部构建好一个Kodien.Builder,调用初始化函数后,传入一个container

这里KodienBuilder通过调用bind返回一个binder,供上层去绑定一个实例

Binders

所以我们看到builder其实提供了一个bind方法,对外暴露接口获取binder。

inner class TypeBinder<T : Any> internal constructor(val type: TypeToken<out T>, val tag: Any?, val overrides: Boolean?) : Kodein.Builder.TypeBinder<T> {
    internal val containerBuilder get() = this@KodeinBuilderImpl.containerBuilder

    override infix fun <C, A> with(binding: KodeinBinding<in C, in A, out T>) = containerBuilder.bind(Kodein.Key(binding.contextType, binding.argType, type, tag), binding, moduleName, overrides)
}

inner class DirectBinder internal constructor(private val _tag: Any?, private val _overrides: Boolean?) : Kodein.Builder.DirectBinder {
    override infix fun <C, A, T: Any> from(binding: KodeinBinding<in C, in A, out T>) {
        if (binding.createdType == UnitToken) {
            throw IllegalArgumentException("Using `bind() from` with a *Unit* ${binding.factoryName()} is most likely an error. If you are sure you want to bind the Unit type, please use `bind<Unit>() with ${binding.factoryName()}`.")
        }
        containerBuilder.bind(Kodein.Key(binding.contextType, binding.argType, binding.createdType, _tag), binding, moduleName, _overrides)
    }
}

inner class ConstantBinder internal constructor(private val _tag: Any, private val _overrides: Boolean?) : Kodein.Builder.ConstantBinder {
    @Suppress("FunctionName")
    override fun <T: Any> With(valueType: TypeToken<out T>, value: T) = Bind(tag = _tag, overrides = _overrides) from InstanceBinding(valueType, value)
}

然后我们来看看binder可以绑定哪些binding

Bindings

/**
 * @param C The type of the context used by the retriever.
 * @param A The type of argument used to create or retrieve an instance.
 * @param T The type of instance this factory creates or retrieves.
*/
interface Binding<C, A, T: Any> {

    fun getFactory(kodein: BindingKodein<C>, key: Kodein.Key<C, A, T>): (A) -> T
}

Binding是什么,就是根据key返回一个构造器,这个构造器接受一个参数返回一个实例

Factory

class Factory<C, A, T: Any>(override val contextType: TypeToken<in C>, override val argType: TypeToken<in A>, override val createdType: TypeToken<out T>, private val creator: BindingKodein<C>.(A) -> T) : KodeinBinding<C, A, T> {

    override fun factoryName() = "factory"

    override fun getFactory(kodein: BindingKodein<C>, key: Kodein.Key<C, A, T>): (A) -> T = { arg -> this.creator(kodein, arg) }
}

Multiton

class Multiton<C, A, T: Any>(override val scope: Scope<C>, override val contextType: TypeToken<in C>, override val argType: TypeToken<in A>, override val createdType: TypeToken<out T>, refMaker: RefMaker? = null, val sync: Boolean = true, private val creator: SimpleBindingKodein<C>.(A) -> T) : KodeinBinding<C, A, T> {
    private val _refMaker = refMaker ?: SingletonReference

    private val _scopeId = Any()


    override fun getFactory(kodein: BindingKodein<C>, key: Kodein.Key<C, A, T>): (A) -> T {
        val registry = scope.getRegistry(kodein.context)
        return { arg ->
            @Suppress("UNCHECKED_CAST")
            registry.getOrCreate(ScopeKey(_scopeId, arg), sync) { _refMaker.make { BindingContextedKodein(kodein, kodein.context).creator(arg) } } as T
        }
    }

    override val copier = KodeinBinding.Copier { Multiton(scope, contextType, argType, createdType, _refMaker, sync, creator) }
}

首先通过scope.getRegistry(kodein.context)拿到registry,啥意思,拿到一个存储实例的registry,这里可以和生命周期(context)绑定。

registry.getOrCreate(ScopeKey(_scopeId, arg), sync) { _refMaker.make { BindingContextedKodein(kodein, kodein.context).creator(arg) } } as T

对于scopeKey(是data class),可以看到如果arg一样是不会create的,如果不存在的话,通过creator()函数创建一个

Provider

class Provider<C, T: Any>(override val contextType: TypeToken<in C>, override val createdType: TypeToken<out T>, val creator: NoArgBindingKodein<C>.() -> T) : NoArgKodeinBinding<C, T> {
    override fun factoryName() = "provider"

    override fun getFactory(kodein: BindingKodein<C>, key: Kodein.Key<C, Unit, T>): (Unit) -> T = { NoArgBindingKodeinWrap(kodein).creator() }
}

Singleton

class Singleton<C, T: Any>(override val scope: Scope<C>, override val contextType: TypeToken<in C>, override val createdType: TypeToken<out T>, refMaker: RefMaker? = null, val sync: Boolean = true, val creator: NoArgSimpleBindingKodein<C>.() -> T) : NoArgKodeinBinding<C, T> {
    @Suppress("UNCHECKED_CAST")
    private val _refMaker = refMaker ?: SingletonReference
    private val _scopeKey = ScopeKey(Any(), Unit)

    /**
     * @see [KodeinBinding.getFactory]
     */
    override fun getFactory(kodein: BindingKodein<C>, key: Kodein.Key<C, Unit, T>): (Unit) -> T {
        val registry = scope.getRegistry(kodein.context)
        return {
            @Suppress("UNCHECKED_CAST")
            registry.getOrCreate(_scopeKey, sync) { _refMaker.make { NoArgBindingKodeinWrap(BindingContextedKodein(kodein, kodein.context)).creator() } } as T
        }
    }

    override val copier = KodeinBinding.Copier { Singleton(scope, contextType, createdType, _refMaker, sync, creator) }
}

InstanceBinding

class InstanceBinding<T: Any>(override val createdType: TypeToken<out T>, val instance: T) : NoArgKodeinBinding<Any?, T> {
    override fun factoryName() = "instance"
    override val contextType = AnyToken

    /**
     * @see [KodeinBinding.getFactory]
     */
    override fun getFactory(kodein: BindingKodein<Any?>, key: Kodein.Key<Any?, Unit, T>): (Unit) -> T = { this.instance }


}

就返回一个已经初始化好的instance

Eager

class EagerSingleton<T: Any>(builder: KodeinContainer.Builder, override val createdType: TypeToken<out T>, val creator: NoArgSimpleBindingKodein<Any?>.() -> T) : NoArgKodeinBinding<Any?, T> {

    override val contextType = AnyToken

    @Volatile private var _instance: T? = null
    private val _lock = Any()

    private fun getFactory(kodein: BindingKodein<Any?>): (Unit) -> T {
        return { _ ->
            synchronizedIfNull(
                    lock = _lock,
                    predicate = this@EagerSingleton::_instance,
                    ifNotNull = { it },
                    ifNull = {
                        NoArgBindingKodeinWrap(kodein).creator().also { _instance = it }
                    }
            )
        }
    }


    init {
        val key = Kodein.Key(AnyToken, UnitToken, createdType, null)
        builder.onReady { getFactory(BindingKodeinImpl(this, key, null, 0)).invoke(Unit) }
    }

    override val copier = KodeinBinding.Copier { builder -> EagerSingleton(builder, createdType, creator) }
}

builder.onReady,当kodein容器??准备好时就通过getFactory初始化实例

这里我们暂时知道bindings就是返回一个函数,这个函数传入参数能返回我们需要的实例T。

那么binderbinding是怎么结合起来的,就通过一个初始化好的KodeinContainerBuilder去对bindings进行配置。

KodeinContainerBuilderImpl

配置bindings

internal class KodeinContainerBuilderImpl(
        allowOverride: Boolean,
        silentOverride: Boolean,
        internal val bindingsMap: MutableMap<Kodein.Key<*, *, *>, MutableList<KodeinDefining<*, *, *>>>,
        internal val callbacks: MutableList<DKodein.() -> Unit>,
        internal val translators: MutableList<ContextTranslator<*, *>>
)

配置能否override,持有bindingMap,

Bind

当开始绑定时,会将绑定关系放入bindingMap

val bindings = bindingsMap.getOrPut(key) { newLinkedList() }
bindings.add(0, KodeinDefining(binding, fromModule))

Extend

配置可以从已有的kodeinContainer中继承

  override fun extend(container: KodeinContainer, allowOverride: Boolean, copy: Set<Kodein.Key<*, *, *>>) {
        checkMatch(allowOverride)

        container.tree.bindings.forEach { (key, bindings) ->
            if (!allowOverride)
                checkOverrides(key, null)

            val newBindings = if (key in copy) {
                newLinkedList<KodeinDefining<*, *, *>>().also {
                    bindings.mapTo(it) { KodeinDefining(it.binding.copier?.copy(this@KodeinContainerBuilderImpl) ?: it.binding, it.fromModule) }
                }
            }
            else {
                newLinkedList<KodeinDefining<*, *, *>>(bindings)
            }

            bindingsMap[key] = newBindings
        }

        translators += container.tree.registeredTranslators
    }

获取实例

前面bb了这么多,那怎么获取实例呢?

private val viewModel: AlbumListViewModel by instance()

fun <T : Any> KodeinAware.Instance(type: TypeToken<out T>, tag: Any? = null): KodeinProperty<T> =
        KodeinProperty(kodeinTrigger, kodeinContext) { ctx, _ -> kodein.container.provider(Kodein.Key(ctx.anyType, UnitToken, type, tag), ctx.value).invoke() }

这里返回一个lazy属性,

KodeinProperty

kodein使用lazy能力。

通过定义 provideDelegate 操作符,可以扩展创建属性实现所委托对象的逻辑:
解释

interface LazyDelegate<out V> {
    /** @suppress */
    operator fun provideDelegate(receiver: Any?, prop: KProperty<Any?>): Lazy<V>
}


class KodeinProperty<out V>(internal val trigger: KodeinTrigger?, val originalContext: KodeinContext<*>, private val get: (KodeinContext<*>, String) -> V) : LazyDelegate<V> {

    override fun provideDelegate(receiver: Any?, prop: KProperty<Any?>): Lazy<V> = lazy {
        @Suppress("UNCHECKED_CAST")
        val context = if (receiver != null && originalContext === AnyKodeinContext) KodeinContext(TTOf(receiver) as TypeToken<in Any>, receiver) else originalContext
        get(context, prop.name) } .also { trigger?.properties?.add(it)
    }

}

class KodeinPropertyMap<in I, out O>(private val base: KodeinProperty<I>, private val map: (I) -> O) : LazyDelegate<O> {

    override fun provideDelegate(receiver: Any?, prop: KProperty<Any?>): Lazy<O> = lazy { map(base.provideDelegate(receiver, prop).value) }.also { base.trigger?.properties?.add(it) }

}

翻译一下:
假设一个var prop: Type by KodeinProperty() =>

class C {
    // calling "provideDelegate" to create the additional "delegate" property
    private val prop$delegate = KodeinProperty().provideDelegate(this, this::prop)
    val prop: Type
        get() = prop$delegate.getValue(this, this::prop)
}

这里返回了lazy,即当这个属性被用到时,会调用get方法

那我们再来看看这段代码:

fun <A, T : Any> KodeinAware.Factory(argType: TypeToken<in A>, type: TypeToken<out T>, tag: Any? = null): KodeinProperty<(A) -> T> =
        KodeinProperty(kodeinTrigger, kodeinContext) { ctx, _ -> kodein.container.factory(Kodein.Key(ctx.anyType, argType, type, tag), ctx.value) }

看源码可以看到Factory&Instance的不同,前者lazy返回一个N阶函数,后者lazy直接返回一个实例。

所以很简单,就是通过kodein.container.factory去获取。

KodeinContainerImpl

获取factory,什么是工厂,就是别人传入一个A(arg)返回自己需要的实例。

tree.find(key)

首先在树中根据键值寻找,kodein在绑定关系的时候初始化了一个containerimpl, 初始化了一颗树,

tree.find(key, 0).let {
    if (it.size == 1) {
        val (_, definition, translator) = it[0]
        node?.check(key, 0)
        val kContext = translator?.toKContext(context) ?: KodeinContext(key.contextType, context) as KodeinContext<Any>
        key as Kodein.Key<Any, A, T>
        val bindingKodein = bindingKodein(key, kContext, definition.tree, overrideLevel)
        return definition.binding.getFactory(bindingKodein, key)
    }
}

如果找到一个(有且一个),拿到definition,即binding的定义。检查是否有循环依赖

bindingKodein
val bindingKodein = bindingKodein(key, kContext, definition.tree, overrideLevel)
return definition.binding.getFactory(bindingKodein, key)

这里会创建一个bindingKodein的东西,在bindingsgetFactory,为什么要传入这么一个东西呢?

NoArgBindingKodeinWrap(BindingContextedKodein(kodein, kodein.context)).creator()

我们知道在实例化一个类时,这个类可能还会依赖其他类,绑定可能如下:

bind<AlbumDetailViewModel>() with scoped<Fragment>(AndroidLifecycleScope).singleton {
    KotlinViewModelProvider.of(context) { AlbumDetailViewModel(instance(), instance()) }
}

在里面还需要instance()实例化,那为什么不能直接用kodein的环境呢,因为按官网说法,还可以进行override:

val testModule = Kodein.Module(name = "test") {
    bind<Logger>(overrides = true) with singleton { FileLoggerWrapper("path/to/file", overriddenInstance()) } 
}

所以其实bindingKodein也是kodein环境:

internal open class BindingKodeinImpl<out C, out A, out T: Any> internal constructor(
        override val dkodein: DKodein,
        private val _key: Kodein.Key<C, A, T>,
        override val context: C,
        private val _overrideLevel: Int
) : DKodein by dkodein, BindingKodein<C> {
    override fun overriddenFactory(): (Any?) -> Any = container.factory(_key, context, _overrideLevel + 1) as (Any?) -> Any
    override fun overriddenFactoryOrNull(): ((Any?) -> Any)? = container.factoryOrNull(_key, context, _overrideLevel + 1) as ((Any?) -> Any)?
}

它包括什么,它包括DKodien, Dkodien又是一个新的概念,

If you don’t want to use delegated properties, Kodein has you covered. Most of the features available to Kodein are available to DKodein (D is for Direct). DKodein allows you to directly get a new instance or dependency

它就比kodein少了delegate属性,因此也没有receiver & lazy的概念。

override fun overriddenFactory(): (Any?) -> Any = container.factory(_key, context, _overrideLevel + 1) as (Any?) -> Any

正常绑定时使用的是container.factory(0),这里会使用+1表明使用override

循环依赖 node.check

val kodein = Kodein {
    bind<Die>() with singleton { Die(instance()) } 

    bind<Random>() with singleton { Random(instance) } 
    constant(tag "max") with 5 
}
org.kodein.di.Kodein$DependencyLoopException: Dependency recursion:
     bind<View>()
    ╔╩>bind<Presenter>()
    ║  ╚>bind<View>()
    ╚════╝

回顾一下,
,当我们调用by instance时会发生什么,会返回一个函数,函数执行时会调用
kodein.container.provider(Kodein.Key(ctx.anyType, UnitToken, type, tag), ctx.value).invoke(),即调用creator,即Die(instance()),这里调用instance()时,背后调用DKodeinBaseImpl.container.provider.invoke(),那这里的container是谁呢,先看DKodeinBaseImpl是谁,就是调用by instancecontainer,所以这里container都是同一个,很正常能看出循环依赖。

我们看,在找factory时

private tailrec fun recursiveCheck(node: Node, searchedKey: Kodein.Key<*, *, *>, searchedOverrideLevel: Int): Boolean {
    return if (node.key == searchedKey && node.overrideLevel == searchedOverrideLevel)
        false
    else if (node.parent == null)
        true
    else
        recursiveCheck(node.parent, searchedKey, searchedOverrideLevel)
}

开始NodeDie, 然后NodeRandom,然后 searchKeydie, 因此在递归上去时node -> parent又是die,导致循环依赖。

KodeinTreeImpl

一个缓存:

private val _cache: MutableMap<Kodein.Key<*, *, *>, Triple<Kodein.Key<*, *, *>, List<KodeinDefinition<*, *, *>>, ContextTranslator<*, *>?>>
private typealias BoundTypeTree = MutableMap<TypeChecker, ContextTypeTree>

private typealias ContextTypeTree = MutableMap<TypeChecker.Down, ArgumentTypeTree>

private typealias ArgumentTypeTree = MutableMap<TypeChecker.Down, TagTree>

private typealias TagTree = MutableMap<Any?, Kodein.Key<*, *, *>>

其中因为kodien可以override,所以会有definition lists:

val kodein = Kodein {
    bind<API>() with singleton { APIImpl() }
    /* ... */
    bind<API>(overrides = true) with singleton { OtherAPIImpl() }
}

寻找过程

  • _cache[key]先从key中寻找
  • _cache[anyContextKey]寻找anyContextKey 当然translator.contextType = key.contextType

Inject or Retrieve

inject

class Controller(private val ds: DataSource) {
    /*...*/
}
val controller by kodein.newInstance { Controller(instance()) }

这里会走到Controller(instance()), 在instance里会自己去取实例。

retrive

class Controller(override val kodein: Kodein): KodeinAware {
    private val ds: DataSource by instance()
}

这两种方式,一种是自己就是一个kodein = KodeinAware 内部会去取实例,另一种是自己由外部注入。

SubTypes

val kodein = Kodein {
    bind<Controller>().subtypes() with { type ->
        when (type.jvmType) { 
            is MySpecialController::class.java -> singleton { MySpecialController() }
            else -> provider { myControllerSystem.getController(type.jvmType) }
        }
    }
}

看到这里问题,我绑定的是T,这个T的子类也会找到:

data class Down(override val type: TypeToken<*>) : TypeChecker() {
    val isAny = (type == AnyToken)
    override fun check(other: TypeToken<*>) = isAny || type.isAssignableFrom(other)
}

Contexted & Scoped

kodien中,所有binder with binding都会带一个scope&context:对于默认的是AnyToken&NoScope

override val contextType = AnyToken

override val scope: Scope<Any?> get() = NoScope() // Recreating a new NoScope every-time *on purpose*!

那么,假设我们想要绑到一个具体的context&scope时,可以按如下这么写:

val kodein = Kodein {
    bind<Writer>() with contexted<Request>.provider { context.response.writer } 
}
val kodein = Kodein {
    bind<Session>() with scoped(requestScope).singleton { context.openSession() } 
}

val session: Session by kodein.on(context = request).instance()

Scopes are derived from the context variable.

这里context<Request> & scoped(requestScope)会返回一个新的builder,绑定新的context&scope, 当使用kodein.on时会创建一个新的kodein,kodeinContext是传入的request,我们再来重看一下getFactory:

override fun getFactory(kodein: BindingKodein<C>, key: Kodein.Key<C, A, T>): (A) -> T {
    val registry = scope.getRegistry(kodein.context)
    return { arg ->
        @Suppress("UNCHECKED_CAST")
        registry.getOrCreate(ScopeKey(_scopeId, arg), sync) { _refMaker.make { BindingContextedKodein(kodein, kodein.context).creator(arg) } } as T
    }
}

我们举个scope的例子:

open class AndroidLifecycleScope private constructor(private val newRegistry: () -> ScopeRegistry) : Scope<LifecycleOwner> {

    companion object multiItem: AndroidLifecycleScope(::StandardScopeRegistry)

    object singleItem: AndroidLifecycleScope(::SingleItemScopeRegistry)

    private val map = HashMap<LifecycleOwner, ScopeRegistry>()

    override fun getRegistry(context: LifecycleOwner): ScopeRegistry {
        return synchronizedIfNull(
                lock = map,
                predicate = { map[context] },
                ifNotNull = { it },
                ifNull = {
                    val registry = newRegistry()
                    map[context] = registry
                    context.lifecycle.addObserver(object : LifecycleObserver {
                        @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
                        fun onDestroy() {
                            context.lifecycle.removeObserver(this)
                            registry.clear()
                            map.remove(context)
                        }
                    })
                    registry
                }
        )
    }
}

TypedReference

p05ut8ksb6.png

讲的挺好

  • TypeVariable(类型变量):比如List<T>中的T等
  • WildcardType( 泛型表达式类型):例如List< ? extends Number>这种
  • ParameterizedType(参数化类型):就是我们平常所用到的泛型List、Map(注意和TypeVariable的区别)
  • GenericArrayType(数组类型):并不是我们工作中所使用的数组String[] 、byte[](这种都属于Class),而是带有泛型的数组,即T[] 泛型数组
private fun Type._checkIsReified(disp: Any) {
    when (val jvmType = javaType) {
        is Class<*> -> {}
        is ParameterizedType -> for (arg in jvmType.actualTypeArguments) arg._checkIsReified(disp)
        is GenericArrayType -> jvmType.genericComponentType._checkIsReified(disp)
        is WildcardType -> {
            for (arg in jvmType.lowerBounds)
                arg._checkIsReified(disp)
            for (arg in jvmType.upperBounds)
                arg._checkIsReified(disp)
        }
        is TypeVariable<*> -> throw IllegalArgumentException("$disp uses a type variable named ${jvmType.name}, therefore, the bound value can never be retrieved.")
        else -> throw IllegalArgumentException("Unknown type ${jvmType.javaClass} $jvmType")
    }
}

反正就是一定要具体化泛型,不要有<T>这种

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,482评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,377评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,762评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,273评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,289评论 5 373
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,046评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,351评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,988评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,476评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,948评论 2 324
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,064评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,712评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,261评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,264评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,486评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,511评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,802评论 2 345