安卓 gradle 编译流程

./gradlew installDebug 命令行启动*

创建项目的时候 IDE 在项目的根目录会生成 gradlew 可执行文件,gradlew
中很多逻辑是判断系统类型的,gradlew.bat 是
windows下的实现思路一样的。这里的主要的逻辑:

  1. 先定义了 java 程序执行需要的 CLASSPATH 目录

  2. 然后把 JAVACMD 设置为 java 的入口文件

  3. 最后把开发者定义的 JAVA_OPTS、DEFAULT_JVM_OPTS、进程名都拼接为一串参数执行

  4. 文件最后一行的 exec 开始打包,可以替换为 echo 看具体执行的命令是啥

gradle-wrapper.jar 是 gradle 的入口引导

上文中提到的 CLASSPATH 是 gradle 的入口 jar 实际目录在项目根目录
./gradle/wrapper/ 下,并不是 gradle 的编译主逻辑,根据 gradlew
可执行文件指定的入口类 org.gradle.wrapper.GradleWrapperMain

在里面找到了 main 函数,主要逻辑是:

  1. 加载项目目录下的 gradle.properties 到环境变量

  2. 解析同目录下的 gradle-wrapper.properties 用 Install 下载指定的url 到本地

  3. BootstrapMainStarter 加载真正的 org.gradle.launcher.GradleMain
    进行打包,如果看源码去目录
    ~/.gradle/wrapper/dists/gradle-6.7.1-bin/bwlcbys1h7rz3272sye1xwiv6/gradle-6.7.1/lib

这样设计的好处应该是把 gradle 版本和项目解耦,方便替换 gradle
的版本,如果想自定义 gradle 版本或者因为国内访问 url
超时,可以直接替换配置文件的 distributionUrl

GradleUserHomeLookup 里主要声明了默认目录 ~/.gradle

WrapperExecutor 并不是线程池,gradle 代码特色是只要切换一层逻辑就会有
xxxExecutor,Install 和 BootstrapMainStarter 实际是串行的

这里使用 org.gradle.debug=true 打断点不生效,因为这里是纯 java 进程没有处理任何
gradle 参数,应该使用 java
-agentlib:jdwp=transport=dt_socket,server=y,address=5006 -classpath
./gradle/wrapper/gradle-wrapper.jar org.gradle.wrapper.GradleWrapperMain
installdebug 和 jdb -attach
localhost:5006

两个命令行进行断点调试

启动 Daemon 进程

  1. GradleMain.main-> ProcessBootstrap.run 这是一个通用的替换类加载器的反射

  2. org.gradle.launcher.Main.run-> .doAction

  3. DefaultCommandLineActionFactory.convert->WithLogging.execute

  4. ExceptionReportingAction.execute

  5. NativeServicesInitializingAction.execute

  6. DefaultCommandLineActionFactory.ParseAndBuildAction.execute

    1. DefaultCommandLineActionFactory.BuiltInActions.createAction
    2. BuildActionsFactory.createAction 区分是否要使用守护进程执行
    3. RunBuildAction.run 使用的 BuildActionExecuter 是上一步提供的
    4. DaemonClient.execute通过 DefaultDaemonConnector 连接 ExecutorFactory
      执行池,只管 dispatch 发送参数,DefaultDaemonStarter 才开始处理 gradle
      任务,这里判断 org.gradle.daemon.debug 添加了调试功能,端口固定 5005
  7. DefaultExecActionFactory.newExec->DefaultExecHandleBuilder

  8. DefaultExecHandle.start

  9. ProcessBuilder.start 开启了新的线程

  10. GradleDaemon.main > ProcessBootstrap.run 是新进程的入口,这里可以用
    org.gradle.debug=true 打断点调试了

  11. DaemonMain.doAction 创建 Daemon 和 DaemonServices,同时在 DaemonServices
    的构造方法初始化 GlobalScopeServices 内部使用 DefaultServiceLocator 加载
    META-INF/services/”配置的所有 PluginServiceRegistry,确定了 gradle
    能处理哪些任务

  12. DaemonServerConnector
    的实现类DaemonTcpServerConnector(TcpIncomingConnector).start()
    当每次执行./gradlew installDebug 的时候 TcpIncomingConnector 会收到
    ConnectCompletion 再包装成 SynchronizedDispatchConnection(SocketConnection)
    分发给 DefaultIncomingConnectionHandler.handle 切换线程转到
    ConnectionWorker.run ,经过DefaultDaemonConnection.receive 读取到Command
    实际类型为
    org.gradle.launcher.daemon.protocol.Build(ExecuteBuildAction(StartParameterInternal(DefaultTaskExecutionRequest(args))))
    里面包含里命令的字符串,然后handleCommand
    处理数据。这是一个死循环,这个进程前面的逻辑只执行一次

  13. DaemonCommandExecuter.executeCommand-> DaemonCommandExecution. proceed

  14. 顺序执行下面的 action,都是在 DaemonServices.createDaemonCommandActions 创建
    Daemon的时候准备好的,实例创建一次执行多次。前面都是验证是否可以执行的,最后一个
    ExecuteBuild 触发 BuildExecuter。这种一层套一层的设计思路在 gradle
    源码中十分普遍

    1. HandleStop
    2. HandleInvalidateVirtualFileSystem
    3. HandleCancel
    4. HandleReportStatus
    5. ReturnResult
    6. StartBuildOrRespondWithBusy
    7. EstablishBuildEnvironment
    8. LogToClient
    9. LogAndCheckHealth
    10. ForwardClientInput
    11. RequestStopIfSingleUsedDaemon
    12. ResetDeprecationLogger
    13. WatchForDisconnection
    14. ExecuteBuild
  15. 继续往下走主流程,又是一层套一层的执行逻辑,代码风格上 16
    点像装饰,这条像代理。我猜不是同一个开发写的,经过同一个老板 review
    的,代码风格不一样但是执行逻辑完全一样

    1. SetupLoggingActionExecuter
    2. SessionFailureReportingActionExecuter
    3. StartParamsValidatingActionExecuter
    4. GradleThreadBuildActionExecuter
    5. SessionScopeLifecycleBuildActionExecuter
      这前面的都只创建一个实例,在LauncherServices$ToolingGlobalScopeServices.createBuildExecuter
      创建,所有的参数保存在BuildSessionState 中
    6. SubscribableBuildActionExecuter
      后面的每次执行到任务创建LauncherServices$ToolingBuildSessionScopeServices.
      createBuildExecuter
    7. ContinuousBuildActionExecuter
    8. BuildTreeScopeLifecycleBuildActionExecuter ,所有的参数保存在 BuildTreeState
    9. InProcessBuildActionExecuter 由 DefaultIncludedBuildRegistry.createRootBuild
      方法把前面收到的 ExecuteBuildAction 转换为
      GradleBuildController(DefaultGradleLauncher(BuildScopeServices(DefaultServiceRegistry.OwnServices(BuildDefinition(StartParameter)))))
  16. LauncherServices.createActionExecuter 创建了下面这些Runner

    1. RunAsBuildOperationBuildActionRunner
    2. BuildCompletionNotifyingBuildActionRunner
    3. FileSystemWatchingBuildActionRunner
    4. ValidatingBuildActionRunner
    5. BuildOutcomeReportingBuildActionRunner
    6. ChainingBuildActionRunner
    7. ExecuteBuildActionRunner

ServiceRegistry 和 PluginServiceRegistry 所有逻辑的起始

  1. ServiceRegistry 的主要实现类有
    DaemonServices、DaemonClientServices、ConnectorServiceRegistry
    都是随进程存活

    1. findProviderMethods 把当前实现类的所有方法转成 FactoryMethodService 存到
      allServices.providersByType<返回类型,FactoryMethodService > 里。
    2. get(Class) 直接拿到上面的 FactoryMethodService extends SingletonService
      如果第一次调用 create 一个实例,再次调用返回缓存。因为这个 Daemon
      进程不销毁,无论多少次构建任务都只创建一次实例
    3. getFactory(Class) 和上面一点很像,支持创建出来的是个池子,比如
      ConnectorServiceRegistry
  2. PluginServiceRegistry 的主要实现类有
    DependencyServices、LauncherServices、ExecutionServices、MavenPublishServices、XcodeServices
    分别对应了 gradle
    的不同能力,每种能力对不同的生命周期可以添加不同的处理方式,生命周期从长到短依次为:

    1. registerGlobalServices 全局进程内唯一 GlobalScopeServices.configure 执行
    2. registerGradleUserHomeServices 不是安卓项目目录, 是 gradle 的目录,和
      Global 基本一样,正常不会搞多个 gradle 目录,在
      BuildSessionState(GradleUserHomeScopeServiceRegistry) 根据传入的传入的 file
      目录缓存在 map 里,GradleUserHomeScopeServices.configure 执行
    3. registerBuildSessionServices 每一次执行命令 ,在 BuildSessionState
      构造方法同时初始化 BuildSessionScopeServices.configure 执行,相比
      GradleUserHome 不会缓存
    4. registerBuildTreeServices 每个命令的任务树 在 BuildTreeState
      构造方法同时初始化 BuildTreeScopeServices.configure 执行
    5. registerBuildServices 任务树的 build 阶段,随 DefaultGradleLauncher
      初始化执行 BuildScopeServices 并注册 ServiceRegistration
    6. registerGradleServices 随 GradleScopeServices 初始化执行
    7. registerSettingsServices 随 DefaultSettings 初始化的 SettingsScopeServices
      内部执行,DefaultGradleLauncher.prepareSettings 阶段执行
    8. registerProjectServices 随 DefaultProject 初始化的 ProjectScopeServices
      内部执行,DefaultGradleLauncher.prepareProjects 阶段执行,需要注意每个
      android 项目下的 module 和 DefaultProject 一一对应,每个 build.gradle
      都对应不同的 DefaultProject,根据层级有父子关系

gradle 里的 execute 大部分不是创建线程执行,只是因为当前逻辑需要分层。可能是要加 log,可能是为了抓
Exception 不让进程崩溃,也可能是为了统计执行时间等等

gradle 里的接口定义的十分多,为有能力的开发者提供了足够的扩展性,现有的实现类一般都以
Default+接口名

GradleBuildController.execute -> DefaultGradleLauncher.doClassicBuildStages 分为 4 个主要步骤

  1. 初始化 setting.gradle 根据 include 的目录层级创建树状关系的
    DefaultProjectDescriptor

    1. prepareSettings
    2. BuildOperationFiringSettingsPreparer$LoadBuild.run
    3. DefaultSettingsPreparer.prepareSettings
    4. DefaultSettingsLoader.findAndLoadSettings
    5. BuildOperationSettingsProcessor.process
    6. RootBuildCacheControllerSettingsProcessor.process
    7. SettingsEvaluatedCallbackFiringSettingsProcessor.process
    8. ScriptEvaluatingSettingsProcessor.process
      1. 创建 setting.gradle 对应的 DefaultSettings :SettingsFactory.createSettings
        -> DependencyInjectingInstantiator.newInstance ->
        ClassBuilderImpl.generate
      2. 为 DefaultSettings 赋值 :applySettingsScript ->
        BuildOperationScriptPlugin.apply ->
        DefaultScriptPluginFactory$ScriptPluginImpl.apply 解析 gradle
        文件,主要是赋值 rootProjectDescriptor
  2. 每一个 build.gradle 初始化一个 DefaultProject

    1. prepareProjects
    2. BuildOperationFiringProjectsPreparer$ConfigureBuild.run
    3. BuildTreePreparingProjectsPreparer.prepareProjects -> 这里初始化了 buildSrc 自定义插件
      1. 创建 build.gradle 对应的 DefaultProject :NotifyingBuildLoader.load ->
        ProjectPropertySettingBuildLoader.load ->
        InstantiatingBuildLoader.createProjects -> ProjectFactory.createProject ->
        DependencyInjectingInstantiator.newInstance -> ClassBuilderImpl.generate 和
        settings.gradle 同级目录的 build.gradle 是 rootProject,在
        InstantiatingBuildLoader.createProjects -> createChildProjectsRecursively
        每一个 module 都会创建一个 childProject
      2. 解析 build.gradle 为 DefaultProject 赋值:prepareProjects ->
        DefaultProjectsPreparer.prepareProjects ->
        TaskPathProjectEvaluator.configureHierarchy -> DefaultProject.evaluate ->
        LifecycleProjectEvaluator$EvaluateProject.run ->
        这里解析plugin,包含安卓的和自定义的,所有的需要的 task 都在这里添加到
        project 里,ConfigureActionsProjectEvaluator.evaluate 包含三个步骤
        1. PluginsProjectConfigureActions.execute
          1. HelpTasksAutoApplyAction.execute
          2. KotlinScriptingModelBuildersRegistrationAction.execute
          3. BuildInitAutoApplyAction.execute
          4. WrapperPluginAutoApplyAction.execute
        2. BuildScriptProcessor.execute -> BuildOperationScriptPlugin.apply ->
          DefaultScriptPluginFactory$ScriptPluginImpl.apply
        3. DelayedConfigurationActions
  3. 构建任务树生成 TaskGraph

    1. prepareTaskExecution
    2. BuildOperationFiringTaskExecutionPreparer.prepareForTaskExecution
    3. BuildOperationFiringTaskExecutionPreparer$CalculateTaskGraph.run ->
      populateTaskGraph
    4. DefaultTaskExecutionPreparer.prepareForTaskExecution ->
      DefaultBuildConfigurationActionExecuter.select
      1. ExcludedTaskFilteringBuildConfigurationAction.configure
        过滤配置的不需要执行的 task
      2. DefaultTasksBuildExecutionAction.configure 如果没带任何参数执行默认的 task
      3. TaskNameResolvingBuildConfigurationAction.configure ->
        DefaultTaskExecutionGraph(DefaultExecutionPlan).addEntryTasks -> doAddNodes
        -> LocalTaskNode.resolveDependencies -> getDependencies ->
        TaskDependencyResolver.resolveDependenciesFor ->
        CompositeResolvedArtifactSet.visitDependencies ->
        FailureCollectingTaskDependencyResolveContext.add 判断是否为
        TaskDependencyContainer 递归添加所有依赖的 task
      4. DefaultTaskExecutionGraph.populate
      5. DefaultExecutionPlan.determineExecutionPlan
        根据依赖关系生成任务先后执行的队列,可以说是 gradle 的核心
  4. 执行构建任务

    1. runWork
    2. BuildOperationFiringBuildWorkerExecutor.execute
    3. BuildOperationFiringBuildWorkerExecutor$ExecuteTasks.run
    4. DeprecateUndefinedBuildWorkExecutor.execute
    5. IncludedBuildLifecycleBuildWorkExecutor.execute
    6. DefaultBuildWorkExecutor.execute
      1. DryRunBuildExecutionAction.execute 使用 --dry-run 参数调过所有 task
        的执行(调试用)
      2. SelectedTaskExecutionAction.execute 执行 DefaultTaskExecutionGraph
    7. DefaultTaskExecutionGraph.execute -> executeWithServices
    8. DefaultPlanExecutor.process
    9. DefaultPlanExecutor.startAdditionalWorkers
      开启多线程后面的方法都是多线程执行的
    10. DefaultPlanExecutor$ExecutorWorker.run -> executeNextNode 拆分每一个 Node
    11. DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute
    12. DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute
      1. WorkNodeExecutor.execute 如果需要同步执行 实现接口 SelfExecutingNode
      2. LocalTaskNodeExecutor.execute
    13. EventFiringTaskExecuter.execute 返回值 TaskExecuterResult 放到 context 里
    14. CatchExceptionTaskExecuter
    15. SkipOnlyIfTaskExecuter.execute 跳过 enable=false 的任务
    16. SkipTaskWithNoActionsExecuter.execute 跳过不包含任何 action 的任务
    17. ResolveTaskExecutionModeExecuter.execute
    18. FinalizePropertiesTaskExecuter.execute
    19. CleanupStaleOutputsExecuter.execute
    20. ExecuteActionsTaskExecuter.execute -> executeIfValid
    21. DefaultWorkExecutor.execute 返回值 return new TaskExecuterResult 参数放到 context 里,下面的这堆都从 ExecutionGradleServices.createWorkExecutor 里创建
      1. LoadExecutionStateStep.execute 这里开始切换了代码风格,Task 不放参数里
      2. MarkSnapshottingInputsStartedStep.execute
      3. SkipEmptyWorkStep.execute 调过没有输入的 task
      4. ValidateStep.execute
      5. CaptureStateBeforeExecutionStep.execute
      6. ResolveCachingStateStep.execute
      7. MarkSnapshottingInputsFinishedStep.execute
      8. ResolveChangesStep.execute
      9. SkipUpToDateStep.execute 检查是否有变化的文件,调过没有变化的task
      10. RecordOutputsStep.execute
      11. StoreExecutionStateStep.execute
      12. CacheStep.execute -> executeWithoutCache
      13. BroadcastChangingOutputsStep.execute
      14. SnapshotOutputsStep.execute
      15. CreateOutputsStep.execute
      16. TimeoutStep.execute -> executeWithoutTimeout 检查是否配置了这个任务的超时时间
      17. CancelExecutionStep.execute
      18. ResolveInputChangesStep.execute
      19. CleanupOutputsStep.execute
      20. ExecuteStep.execute -> executeWithPreviousOutputFiles
      21. ExecuteActionsTaskExecuter$TaskExecution.execute -> executeWithPreviousOutputFiles -> executeActions 反射到 task 的实现类执行

下载 dependencies 依赖库

  1. prepareProjects 解析 build.gradle 阶段
    DefaultScriptPluginFactory$ScriptPluginImpl.apply 继续往下看

  2. DefaultPluginRequestApplicator.applyPlugins ->
    defineScriptHandlerClassScope

  3. DefaultScriptHandler.getScriptClassPath

  4. DefaultScriptClassPathResolver.resolveClassPath

  5. CompositeBuildClassPathInitializer.execute

  6. DefaultConfiguration$ConfigurationArtifactCollection.getArtifacts ->
    ensureResolved

    1. DefaultConfiguration$ConfigurationFileCollection.getSelectedArtifacts ->
      resolveToStateOrLater -> resolveExclusively -> resolveGraphIfRequired ->
      ErrorHandlingConfigurationResolver((ShortCircuitEmptyConfigurationResolver(DefaultConfigurationResolver(DefaultArtifactDependencyResolver)))).resolveGraph
      -> DependencyGraphBuilder.attachToTargetRevisionsSerially ->
      EdgeState.attachToTargetConfigurations -> calculateTargetConfigurations ->
      ComponentState.getMetadata -> resolve -> ClientModuleResolver. resolve ->
      RepositoryChainComponentMetaDataResolver.resolveModule -> findBestMatch
      配置了多个仓库会挨个遍历找到第一个 Resolved 的
    2. DefaultLenientConfiguration.select
    3. DefaultVisitedArtifactResults.select
    4. CompositeResolvedArtifactSet.of
  7. DefaultLenientConfiguration.visitArtifacts

  8. ParallelResolveArtifactSet$VisitingSet.visit

  9. ParallelResolveArtifactSet$VisitingSet$StartVisitAction.execute

  10. CompositeResolvedArtifactSet.startVisit

  11. ArtifactBackedResolvedVariant$SingleArtifactSet.startVisit
    如果需要下载到本地

  12. DefaultBuildOperationQueue.add 内部有线程池 下面的调用都为异步执行

  13. DownloadArtifactFile.run

  14. DefaultResolvedArtifact.getFile

  15. DefaultArtifactSet$LazyArtifactSource.create

  16. RepositoryChainArtifactResolver.resolveArtifact ->

  17. getLocalAccess() 加载本地缓存

    1. CachingModuleComponentRepository$LocateInCacheRepositoryAccess.resolveArtifact -> resolveArtifactFromCache 返回值 cache 是 null 或 isMissing
      都会走到下载依赖
    2. InMemoryModuleArtifactCache.lookup
    3. DefaultModuleArtifactCache.lookup -> super.lookup ->
      getPersistentCache().get(key);
    4. WritableArtifactCacheLockingManager$CacheLockingPersistentCache.useCache
    5. DefaultCacheFactory$ReferenceTrackingCache($DirCacheReference(DefaultPersistentDirectoryStore(DefaultCacheAccess))).useCache
    6. DefaultMultiProcessSafePersistentIndexedCache(BTreePersistentIndexedCache($HeaderBlock.getRoot().load().StateCheckBlockStore(StateCheckBlockStore(FreeListBlockStore(CachingBlockStore(FileBackedBlockStore(FileBackedBlockStore$BlockImpl))))).read())).get
      主要逻辑是读 module-artifact.bin 缓存找到确切的缓存文
  18. getRemoteAccess() 如果本地缓存没读到 下载依赖并缓存

    1. CachingModuleComponentRepository$ResolveAndCacheRepositoryAccess.resolveArtifact
    2. ExternalResourceResolver$RemoteRepositoryAccess.resolveArtifact
    3. DefaultExternalResourceArtifactResolver.resolveArtifact ->
      downloadStaticResource -> downloadByUrl
    4. DefaultCacheAwareExternalResourceAccessor.getResource ->
      ProducerGuard$AdaptiveProducerGuard.guardByKey
    5. BuildOperationFiringExternalResourceDecorator.getMetaData
    6. AccessorBackedExternalResource(DefaultExternalResourceConnector(HttpResourceAccessor)).getMetaData
    7. HttpClientHelper.performHead -> performRawHead -> performRequest ->
      executeGetOrHead -> performHttpRequest -> getClient().execute
      这里使用了HttpClient 开启了同步下载流程

安卓编译流程-是 gradle 的一种插件

  1. JavaCompile 只编译改过的类

    1. IncrementalInputsTaskAction.doExecute
    2. JavaCompile.performIncrementalCompilation -> performCompilation
    3. CompileJavaBuildOperationReportingCompiler.execute
    4. IncrementalResultStoringCompiler.execute
    5. SelectiveCompiler.execute ->
      JavaRecompilationSpecProvider.provideRecompilationSpec ->
      JavaRecompilationSpecProvider.processOtherChanges ->
      SourceFileChangeProcessor.processChange ->
      PreviousCompilation.getDependents -> ClassSetAnalysis.getRelevantDependents
      根据上次编译结果有依赖关系的类都会重新编译
    6. ModuleApplicationNameWritingCompiler.execute
    7. AnnotationProcessorDiscoveringCompiler.execute
    8. NormalizingJavaCompiler.execute -> delegateAndHandleErrors
    9. JdkJavaCompiler.execute
    10. ResourceCleaningCompilationTask.call
    11. AnnotationProcessingCompileTask.call
    12. IncrementalCompileTask.call
    13. JavacTaskImpl. call 这里就是 jdk 的 javac
  2. 执行自定义插件
    ………… 后面还有很多 Android build tools 流程,未完待续

编译流程的运用

  1. 自定义插件

    1. AnnoationProcessor 是 javac 的一种代码注入技术,在 JavaCompile 期间处理。比如ButterKnife

      1. 定义 javax.annotation.processing.AbstractProcessor 的实现类
      2. 添加 src/main/resources/META-INF/services/javax.annotation.processing.Processor
      3. 添加 src/main/resources/META-INF/gradle/incremental.annotation.processors
      4. 引用 annotationProcessor project(path: ':compiler')
      5. 简单的Demo https://github.com/dingshaoran/WMRouter
    2. GradlePlugin 安卓编译流程的一部分 后面也会讲一下具体实现流程

  2. 提高编译速度

    1. gradle.properties 里添加配置
      org.gradle.unsafe.configuration-cache=true。DefaultGradleLauncher.doBuildStages
      里判断了 DefaultConfigurationCache.canLoad -> isConfigurationCacheEnabled
      -> startParameter.isEnabled -> isConfigurationCache
    2. 用 nexus 搭建自己的 maven 仓库,把 repositories
      所有的地址都配进去,不直接使用多个 repo 防止
      RepositoryChainComponentMetaDataResolver.findBestMatch 遍历多个 repo
    3. gradle.properties 里添加配置 org.gradle.caching=true
    4. 遵循 Gradle 的编码原则,相比普通的 javac gradle 实现了增量编译,如果不按照 gradle 规则会影响打包速度 ⁣https://docs.gradle.org/7.1/userguide/java_plugin.html#sec:incremental_compile

带着问题看博客理解的更透彻

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

推荐阅读更多精彩内容