Android gradle打包涉及task源码解析(三)

文章序号

此篇文章将分析如下5个task。

:app:generateDebugResValues UP-TO-DATE
:app:generateDebugResources UP-TO-DATE
:app:mergeDebugResources UP-TO-DATE
:app:createDebugCompatibleScreenManifests UP-TO-DATE
:app:processDebugManifest

generateDebugResValues

  • 准备

在项目的app/gradle中添加如下代码

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            // 添加代码
            resValue "string", "AppName", "app_release"
        }
        // 添加代码
        debug {
            resValue "string", "AppName", "app_debug"
        }
    }

接着执行命令./gradlew generateDebugResValues

  • inputs&outputs
output file:/Users/zhengchao/Documents/AndroidSpace/OpenSpace/TasksPro/app/build/generated/res/resValues/debug

输出文件目录下面会生成generated.xml文件,文件内容如下:

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <!-- Automatically generated file. DO NOT MODIFY -->

    <!-- Values from build type: debug -->
    <string name="AppName" translatable="false">app_debug</string>

</resources>

生成的内容我们可以直接在xml中使用(android:text="@string/AppName"),也可以在代码中使用(getResources().getString(R.string.AppName);)。

  • 源码

https://android.googlesource.com/platform/tools/base/+/gradle_3.0.0/build-system/gradle-core/src/main/java/com/android/build/gradle/tasks/GenerateResValues.java

  • 主要代码逻辑

GenerateResValues.java中的generate()方法

    @TaskAction
    void generate() throws IOException, ParserConfigurationException {
        // 1、输出路径,就是我们output中的目录
        File folder = getResOutputDir();
        // 2、这里的getItems()就是获得我们在代码中设置的resValues
        List<Object> resolvedItems = getItems();

        if (resolvedItems.isEmpty()) {
            FileUtils.cleanOutputDir(folder);
        } else {
            // 3、在resolvedItems不等于空的时候,通过ResValueGenerator生成我们的generated.xml文件
            ResValueGenerator generator = new ResValueGenerator(folder);
            generator.addItems(getItems());
            generator.generate();
        }
    }

通过上面的分析,我们知道generateDebugResValues 任务就是把我们在gradle里面配置的resValue读取到,然后在/build/generated/res/resValues/debug目录下生成generate.xml文件,该文件里面的内容,可直接在xml文件和代码中使用,方便一些动态化的配置。

generateDebugResources

很遗憾该任务的相关逻辑没有找到,有知道的麻烦留言告知一下。

mergeDebugResources

执行命令./gradlew mergeDebugResources

  • inputs&outputs
input file:/Users/zhengchao/Documents/AndroidSpace/OpenSpace/TasksPro/app/build/generated/res/resValues/debug
input file:/Users/zhengchao/.gradle/caches/transforms-1/files-1.1/appcompat-v7-26.1.0.aar/a7cc521b4567369eba0ddb355f44a660/res
input file:/Users/zhengchao/.gradle/caches/transforms-1/files-1.1/constraint-layout-1.1.3.aar/604f3f8b356720eda7bfab425c06a06e/res
input file:/Users/zhengchao/.gradle/caches/transforms-1/files-1.1/support-media-compat-26.1.0.aar/500188dffd88c5be8587eb6372bbf06d/res
input file:/Users/zhengchao/.gradle/caches/transforms-1/files-1.1/support-compat-26.1.0.aar/9806df9e60c4aacc7f9f357a91ad2e92/res
input file:/Users/zhengchao/Documents/AndroidSpace/OpenSpace/TasksPro/app/build/generated/res/rs/debug
input file:/Users/zhengchao/Documents/AndroidSpace/OpenSpace/TasksPro/app/src/main/res
input file:/Users/zhengchao/Documents/AndroidSpace/OpenSpace/TasksPro/app/src/debug/res
---------------------------------------------------
output file:/Users/zhengchao/Documents/AndroidSpace/OpenSpace/TasksPro/app/build/intermediates/blame/res/debug
output file:/Users/zhengchao/Documents/AndroidSpace/OpenSpace/TasksPro/app/build/generated/res/pngs/debug
output file:/Users/zhengchao/Documents/AndroidSpace/OpenSpace/TasksPro/app/build/intermediates/incremental/mergeDebugResources
output file:/Users/zhengchao/Documents/AndroidSpace/OpenSpace/TasksPro/app/build/intermediates/res/merged/debug

根据输入文件路径,大致有这几种:

1、generateDebugResValues任务生成的resValues文件(generated/res/resValues/debug);

2、引用的aar包里面的资源(appcompat-v7-26.1.0.aar/a7cc521b4567369eba0ddb355f44a660/res,constraint-layout-1.1.3.aar/604f3f8b356720eda7bfab425c06a06e/res);

3、compileDebugRenderscript 任务生的Renderscript文件(generated/res/rs/debug);

4、项目中的res资源文件(TasksPro/app/src/main/resTasksPro/app/src/debug/res);

输入文件经过mergeDebugResources任务处理后,生产的文件有如下几种:

1、png图片集合(generated/res/pngs/debug);

2、merge后的资源集合(incremental/mergeDebugResources);

3、资源映射关系集合(intermediates/res/merged/debug);

4、merge操作的日志记录(intermediates/blame/res/debug);

  • 源码

https://android.googlesource.com/platform/tools/base/+/gradle_3.0.0/build-system/gradle-core/src/main/java/com/android/build/gradle/tasks/MergeResources.java

  • 主要代码逻辑

MergeResources.java->doFullTaskAction()方法

    @Override
    protected void doFullTaskAction() throws IOException, ExecutionException, JAXBException {
        // 1、得到ResourcePreprocessor子类对象
        ResourcePreprocessor preprocessor = getPreprocessor();

        // 2、得到task的output目录
        // this is full run, clean the previous output
        File destinationDir = getOutputDir();
        FileUtils.cleanOutputDir(destinationDir);

        // 3、得到inputs文件目录集合
        List<ResourceSet> resourceSets = getConfiguredResourceSets(preprocessor);

        // 4、生成ResourceMerger 对象
        // create a new merger and populate it with the sets.
        ResourceMerger merger = new ResourceMerger(minSdk);
        // 5、mergeingLog 记录
        MergingLog mergingLog =
                getBlameLogFolder() != null ? new MergingLog(getBlameLogFolder()) : null;

        // 6、resourceCompiler,实际是Appt工具对象
        try (QueueableResourceCompiler resourceCompiler =
                processResources
                        ? makeAapt(
                                aaptGeneration,
                                getBuilder(),
                                fileCache,
                                crunchPng,
                                variantScope,
                                getAaptTempDir(),
                                mergingLog)
                        : QueueableResourceCompiler.NONE) {

            for (ResourceSet resourceSet : resourceSets) {
                resourceSet.loadFromFiles(getILogger());
                merger.addDataSet(resourceSet);
            }

            MergedResourceWriter writer =
                    new MergedResourceWriter(
                            workerExecutorFacade,
                            destinationDir,
                            getPublicFile(),
                            mergingLog,
                            preprocessor,
                            resourceCompiler,
                            getIncrementalFolder(),
                            dataBindingLayoutProcessor,
                            mergedNotCompiledResourcesOutputDirectory,
                            pseudoLocalesEnabled,
                            getCrunchPng());
            // 7、执行merge resource 操作
            merger.mergeData(writer, false /*doCleanUp*/);

            if (dataBindingLayoutProcessor != null) {
                dataBindingLayoutProcessor.end();
            }

            // No exception? Write the known state.
            merger.writeBlobTo(getIncrementalFolder(), writer, false);
        } catch (MergingException e) {
            System.out.println(e.getMessage());
            merger.cleanBlob(getIncrementalFolder());
            throw new ResourceException(e.getMessage(), e);
        } finally {
            cleanup();
        }
    }

第一步:得到ResourcePreprocessor子类对象,实际是MergeResourcesVectorDrawableRenderer对象,该类又继承了VectorDrawableRenderer,该类如下:

/**
 * Generates PNG images (and XML copies) from VectorDrawable files.
*/
public class VectorDrawableRenderer implements ResourcePreprocessor {

通过说明,可以知道该类是通过VectorDrawable文件生产PNG图片,或者拷贝 xml文件。

第二步:得到task的output目录,实际上就是/Users/zhengchao/Documents/AndroidSpace/OpenSpace/TasksPro/app/build/intermediates/res/merged/debug目录;

第三步:得到inputs文件目录集合,调用getConfiguredResourceSets(preprocessor)方法,该方法就是得到inputs路径;

第四步:生成ResourceMerger 对象。该类实现了DataMerger抽象类,主要的merge逻辑均在此类里面实现,此处不再展开,有兴趣的自行阅读:https://android.googlesource.com/platform/tools/base/+/gradle_3.0.0/sdk-common/src/main/java/com/android/ide/common/res2/DataMerger.java

第五步:mergeingLog 记录,就是intermediates/blame/res/debug目录,记录操作日志。

第六步:resourceCompiler,实际是Appt工具对象。

第七步:执行merge resource 操作

mergeDebugResources任务是把依赖的库和工程中的资源进行merge操作。

createDebugCompatibleScreenManifests

  • 准备(gradle splits配置)

在项目的app/gradle android模块下添加如下代码:

splits {
        // Screen density split settings
        density {

            // Enable or disable the density split mechanism
            enable true

            // Exclude these densities from splits
            exclude "ldpi", "tvdpi", "xxhdpi", "xxxhdpi"
        }
    }

执行命令:./gradlew createDebugCompatibleScreenManifests

  • inputs&outputs
output file:/Users/zhengchao/Documents/AndroidSpace/OpenSpace/TasksPro/app/build/intermediates/manifests/density/debug

在output目录下会生成hdpi/AndroidManigest.xml,mdpi/AndroidManigest.xml,xhdpi/AndroidManigest.xml和output.json文件。Manifest文件内容如下:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="">

    <uses-sdk android:minSdkVersion="14"/>
    <compatible-screens>
    </compatible-screens>
</manifest>

output.json文件内容如下(经格式化处理的):

[{
    "outputType": {
        "type": "COMPATIBLE_SCREEN_MANIFEST"
    },
    "apkInfo": {
        "type": "FULL_SPLIT",
        "splits": [{
            "filterType": "DENSITY",
            "value": "xhdpi"
        }],
        "versionCode": 1
    },
    "path": "xhdpi/AndroidManifest.xml",
    "properties": {}
}, {
    "outputType": {
        "type": "COMPATIBLE_SCREEN_MANIFEST"
    },
    "apkInfo": {
        "type": "FULL_SPLIT",
        "splits": [{
            "filterType": "DENSITY",
            "value": "hdpi"
        }],
        "versionCode": 1
    },
    "path": "hdpi/AndroidManifest.xml",
    "properties": {}
}, {
    "outputType": {
        "type": "COMPATIBLE_SCREEN_MANIFEST"
    },
    "apkInfo": {
        "type": "FULL_SPLIT",
        "splits": [{
            "filterType": "DENSITY",
            "value": "mdpi"
        }],
        "versionCode": 1
    },
    "path": "mdpi/AndroidManifest.xml",
    "properties": {}
}]
  • 源码

https://android.googlesource.com/platform/tools/base/+/gradle_3.0.0/build-system/gradle-core/src/main/java/com/android/build/gradle/tasks/CompatibleScreensManifest.java

  • 主要代码逻辑
    public void generateAll() throws IOException {
        // 1、遍历所有的density,通过generate方法生产响应的manifest文件
        // process all outputs.
        outputScope.parallelForEach(
                VariantScope.TaskOutputType.COMPATIBLE_SCREEN_MANIFEST, this::generate);
        // 2、生成json文件
        // now write the metadata file.
        outputScope.save(
                ImmutableList.of(VariantScope.TaskOutputType.COMPATIBLE_SCREEN_MANIFEST),
                outputFolder);
    }
    @Nullable
    public File generate(ApkData apkData) throws IOException {
        String density = apkData.getFilter(com.android.build.OutputFile.FilterType.DENSITY);
        if (density == null) {
            return null;
        }

        StringBuilder content = new StringBuilder();
        content.append("<?xml version=\"1.0\" encoding=\"utf-8\"?>\n")
                .append("<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n")
                .append("    package=\"\">\n")
                .append("\n");
        if (minSdkVersion.get() != null) {
            content.append("    <uses-sdk android:minSdkVersion=\"")
                    .append(minSdkVersion.get())
                    .append("\"/>\n");
        }
        content.append("    <compatible-screens>\n");

        // convert unsupported values to numbers.
        density = convert(density, Density.XXHIGH, Density.XXXHIGH);

        for (String size : getScreenSizes()) {
            content.append(
                    "        <screen android:screenSize=\"").append(size).append("\" "
                    + "android:screenDensity=\"").append(density).append("\" />\n");
        }

        content.append(
                "    </compatible-screens>\n" +
                "</manifest>");


        File splitFolder = new File(outputFolder, apkData.getDirName());
        FileUtils.mkdirs(splitFolder);
        File manifestFile = new File(splitFolder, SdkConstants.ANDROID_MANIFEST_XML);

        Files.write(content.toString(), manifestFile, Charsets.UTF_8);
        return manifestFile;
    }

代码很简单第一步遍历density,然后生产相应的manifest文件。第二步生成output.json文件。

processDebugManifest

执行命令:./gradlew processDebugManifest

  • inputs&outputs
input file:/Users/chao.zheng/sunday/OpenSpace/TasksPro/app/build/intermediates/manifests/density/debug
input file:/Users/chao.zheng/sunday/OpenSpace/TasksPro/app/src/main/AndroidManifest.xml
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/appcompat-v7-26.1.0.aar/6b443e96f1af9aa241aaa70576c67a57/AndroidManifest.xml
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/constraint-layout-1.1.3.aar/f44da5c361a1f52801511229596f72e7/AndroidManifest.xml
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/animated-vector-drawable-26.1.0.aar/9c804d63d6f065a8f9945f9ad94fee0e/AndroidManifest.xml
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/support-vector-drawable-26.1.0.aar/4e56cc34abf77378e2b8d16ee237c82d/AndroidManifest.xml
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/support-v4-26.1.0.aar/3bf8586900bd31e222ef8b68bfd6e744/AndroidManifest.xml
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/support-media-compat-26.1.0.aar/267524a16ca7128dd9cef3c19f394439/AndroidManifest.xml
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/support-fragment-26.1.0.aar/77cf518e9868987a283f04cec221fefa/AndroidManifest.xml
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/support-core-utils-26.1.0.aar/8634ab1afa6a5a1a947a7bd163aba14f/AndroidManifest.xml
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/support-core-ui-26.1.0.aar/8902e2a864b44d47c26fbc80fdafe175/AndroidManifest.xml
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/support-compat-26.1.0.aar/3e4c87483eacfb4c962d7380a59a114d/AndroidManifest.xml
input file:/Users/chao.zheng/.gradle/caches/transforms-1/files-1.1/runtime-1.0.0.aar/ed085e7b9476f7a9fef4ffbb323166ba/AndroidManifest.xml
---------------------------------------------------
output file:/Users/chao.zheng/sunday/OpenSpace/TasksPro/app/build/intermediates/manifests/instant-run/debug
output file:/Users/chao.zheng/sunday/OpenSpace/TasksPro/app/build/intermediates/manifests/full/debug
output file:/Users/chao.zheng/sunday/OpenSpace/TasksPro/app/build/outputs/logs/manifest-merger-debug-report.txt

根据input file路径,可知输入分为三种类型:

1、createDebugCompatibleScreenManifests 任务生成的density规则(intermediates/manifests/density/debug);

2、项目的manifest文件(TasksPro/app/src/main/AndroidManifest.xml);

3、依赖的aar包的manifest和运行环境的manifest文件(appcompat-v7-26.1.0.aar/.../AndroidManifest.xml,runtime-1.0.0.aar/.../AndroidManifest.xml)。

根据output file路径,可知输出文件也为三种:

1、instant-run模式下的输出(manifests/instant-run/debug);

2、正常模式下的输出(manifests/full/debug);

3、merge manifest 的log记录(outputs/logs/manifest-merger-debug-report.txt);

  • 源码

https://android.googlesource.com/platform/tools/base/+/gradle_3.0.0/build-system/gradle-core/src/main/java/com/android/build/gradle/tasks/MergeManifests.java

  • 主要代码逻辑

MergeManifest.java中的doFullTaskAction()方法。

    protected void doFullTaskAction() throws IOException {
        // 1、生成compatibleScreenManifests集合
        // read the output of the compatible screen manifest.
        Collection<BuildOutput> compatibleScreenManifests =
                BuildOutputs.load(
                        VariantScope.TaskOutputType.COMPATIBLE_SCREEN_MANIFEST,
                        compatibleScreensManifest);
        // 2、重新设置packageName
        String packageOverride;
        if (packageManifest != null && !packageManifest.isEmpty()) {
            packageOverride =
                    ApplicationId.load(packageManifest.getSingleFile()).getApplicationId();
        } else {
            packageOverride = getPackageOverride();
        }

        @Nullable BuildOutput compatibleScreenManifestForSplit;

        // 3、根据splits生成ApkData集合
        List<ApkData> splitsToGenerate =
                ProcessAndroidResources.getApksToGenerate(
                        outputScope, supportedAbis, buildTargetAbi, buildTargetDensity);
        
        // 4、遍历splitsToGenerate集合
        // FIX ME : multi threading.
        for (ApkData apkData : splitsToGenerate) {
            // 5、指定分辨率的BuildOutput对象
            compatibleScreenManifestForSplit =
                    OutputScope.getOutput(
                            compatibleScreenManifests,
                            VariantScope.TaskOutputType.COMPATIBLE_SCREEN_MANIFEST,
                            apkData);
            // 6、正常的输出
            File manifestOutputFile =
                    FileUtils.join(
                            getManifestOutputDirectory(),
                            apkData.getDirName(),
                            SdkConstants.ANDROID_MANIFEST_XML);
            // 7、instantRun 模式输出
            File instantRunManifestOutputFile =
                    FileUtils.join(
                            getInstantRunManifestOutputDirectory(),
                            apkData.getDirName(),
                            SdkConstants.ANDROID_MANIFEST_XML);
            // 8、生成mergingReport对象
            MergingReport mergingReport =
                    getBuilder()
                            .mergeManifestsForApplication(
                                    getMainManifest(),
                                    getManifestOverlays(),
                                    computeFullProviderList(compatibleScreenManifestForSplit),
                                    getFeatureName(),
                                    packageOverride,
                                    apkData.getVersionCode(),
                                    apkData.getVersionName(),
                                    getMinSdkVersion(),
                                    getTargetSdkVersion(),
                                    getMaxSdkVersion(),
                                    manifestOutputFile.getAbsolutePath(),
                                    // no aapt friendly merged manifest file necessary for applications.
                                    null /* aaptFriendlyManifestOutputFile */,
                                    instantRunManifestOutputFile.getAbsolutePath(),
                                    ManifestMerger2.MergeType.APPLICATION,
                                    variantConfiguration.getManifestPlaceholders(),
                                    getOptionalFeatures(),
                                    getReportFile());
            XmlDocument mergedXmlDocument =
                    mergingReport.getMergedXmlDocument(MergingReport.MergedManifestKind.MERGED);
            ImmutableMap<String, String> properties =
                    mergedXmlDocument != null
                            ? ImmutableMap.of(
                                    "packageId",
                                    mergedXmlDocument.getPackageName(),
                                    "split",
                                    mergedXmlDocument.getSplitName(),
                                    SdkConstants.ATTR_MIN_SDK_VERSION,
                                    mergedXmlDocument.getMinSdkVersion())
                            : ImmutableMap.of();

            outputScope.addOutputForSplit(
                    VariantScope.TaskOutputType.MERGED_MANIFESTS,
                    apkData,
                    manifestOutputFile,
                    properties);
            outputScope.addOutputForSplit(
                    VariantScope.TaskOutputType.INSTANT_RUN_MERGED_MANIFESTS,
                    apkData,
                    instantRunManifestOutputFile,
                    properties);
        }
        // 9、存储full模式下的output.json文件
        outputScope.save(
                ImmutableList.of(VariantScope.TaskOutputType.MERGED_MANIFESTS),
                getManifestOutputDirectory());
        // 10、存储instantRun模式下的output.json文件
        outputScope.save(
                ImmutableList.of(VariantScope.TaskOutputType.INSTANT_RUN_MERGED_MANIFESTS),
                getInstantRunManifestOutputDirectory());
    }

第一步:从`createDebugCompatibleScreenManifests'任务的输出读取,生成compatibleScreenManifests集合。所以这里的值有三个(hdpi、mdpi、xhdpi)BuildOutput对象;

第二步:重新设置packageName。如果我们同时在Manifest.xml和gradle里面同时设置packageId,且id值不同,则会在此用gradle里面的package覆盖Manifest.xml里面的id值。

第三步:根据splits的配置,生成apkData集合splitsToGenerate对象;

第四步:遍历splitsToGenerate集合,生成不同的输出文件;

第五步:进入不同的split遍历,得到指定分辨率的BuildOutput对象;

第六步:正常的manifest输出文件。(/Users/chao.zheng/sunday/OpenSpace/TasksPro/app/build/intermediates/manifests/full/debug/universal/AndroidManifest.xml

第七步:instantRun模式下的Manifest输出。

第八步:生成mergingReport对象,调用的是AndroidBuilder中的mergeManifestsForApplication()方法,该方法如下:

    /** Invoke the Manifest Merger version 2. */
    public MergingReport mergeManifestsForApplication(
            @NonNull File mainManifest,
            @NonNull List<File> manifestOverlays,
            @NonNull List<? extends ManifestProvider> dependencies,
            @Nullable String featureName,
            String packageOverride,
            int versionCode,
            String versionName,
            @Nullable String minSdkVersion,
            @Nullable String targetSdkVersion,
            @Nullable Integer maxSdkVersion,
            @NonNull String outManifestLocation,
            @Nullable String outAaptSafeManifestLocation,
            @Nullable String outInstantRunManifestLocation,
            ManifestMerger2.MergeType mergeType,
            Map<String, Object> placeHolders,
            @NonNull List<Invoker.Feature> optionalFeatures,
            @Nullable File reportFile) {
            ...
            // 执行merge操作,调用的是ManifestMerger2类的merge()方法。
            MergingReport mergingReport = manifestMergerInvoker.merge();
            ...
    }

此任务的核心merge逻辑都在这一步,详细的细节有兴趣的同学自行看源码:https://android.googlesource.com/platform/tools/base/+/gradle_3.0.0/build-system/manifest-merger/src/main/java/com/android/manifmerger/ManifestMerger2.java?autodive=0%2F%2F%2F%2F

第九步:存储full模式下的output.json文件;

第十步:存储instantRun模式下的output.json文件

经过上面分析可知,processDebugManifest任务实际上做的是manifest文件的merge操作,将三种输入类型的manifest合成一个manifest文件,并生成相应操作日志文件。

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

推荐阅读更多精彩内容