CMAKE與Android NDK開發(fā)

在Android Studio 2.2開始旭愧,正式支持cmake編譯颅筋,在與android studio結(jié)合之前,cmake就已經(jīng)作為一個(gè)廣泛使用的構(gòu)建系統(tǒng)输枯,應(yīng)用在許多項(xiàng)目中议泵。通過cmake與ndk,我們可以將c/c++源碼編譯成動(dòng)/靜態(tài)庫桃熄、可執(zhí)行程序等先口,非常的方便。

認(rèn)識(shí)CMake

在使用cmake之前瞳收,我們需要先了解一下cmake碉京,最直接的了解方式是官網(wǎng)。當(dāng)然還有tutorial最好需要看一下螟深,這樣你就能大概理解cmake的一些用法谐宙。如果你還不了解c/c++的編譯過程,請自行百度學(xué)習(xí)界弧,不在本文敘述范圍內(nèi)凡蜻。

CMake的基本操作

在看過官網(wǎng)的資料和tutorial之后搭综,我們需要?jiǎng)邮謱?shí)操一下,如果使用cmake進(jìn)行編譯划栓,這樣我們才能更好的掌握cmake的一些用法兑巾。對于我們后續(xù)cmake與ndk的結(jié)合有莫大的幫助。我們這里通過clion作為ide進(jìn)行demo的學(xué)習(xí)忠荞。

創(chuàng)建可執(zhí)行程序

# 要求最低的cmake版本
cmake_minimum_required(VERSION 3.14)

# 工程名字 工程語言
project(cmakedemo C)

# 設(shè)置cmake c 的標(biāo)準(zhǔn)c99
set(CMAKE_C_STANDARD 99)

# 將main.c文件加入到可執(zhí)行程序cmakedemo中
add_executable(cmakedemo main.c)

add_executable第一個(gè)參數(shù)是生成可執(zhí)行程序名稱蒋歌,第二個(gè)參數(shù)是源碼文件,如果有多個(gè)源碼文件钻洒,依次加入奋姿,用空格隔開。最后會(huì)生成一個(gè)cmakedemo可執(zhí)行程序素标。當(dāng)然称诗,我們?nèi)粘J褂眠^程中,更多的是將源碼編譯成動(dòng)/靜態(tài)庫头遭。
目錄結(jié)構(gòu)如下:

目錄結(jié)構(gòu)

多源文件編譯

# 要求最低的cmake版本
cmake_minimum_required(VERSION 3.14)

# 工程名字 工程語言
project(cmakedemo C)

# 設(shè)置cmake c 的標(biāo)準(zhǔn)c99
set(CMAKE_C_STANDARD 99)

# 方法一:將多個(gè)文件加入到可執(zhí)行程序中編譯
# 將main.c MathFunctions.c文件加入到可執(zhí)行程序cmakedemo中
#add_executable(cmakedemo2 main.c MathFunctions.c)

# 方法二:采用目錄形式
# 將當(dāng)前目錄下的文件寓免,都保存在DIR_SRCS變量中
aux_source_directory(. DIR_SRCS)
# 將變量代表的文件路徑加入到可執(zhí)行程序中編譯
add_executable(cmakedemo2 ${DIR_SRCS})

CMakeLists.txt文件中,我們有兩種方式將多個(gè)源碼文件加入到編譯计维,方法一將MathFunction.c文件放在add_executable最后袜香,并用空格隔開。方法二中鲫惶,我們用了一個(gè)aux_source_directory蜈首,第一個(gè)參數(shù)表示搜尋的目錄,第二個(gè)參數(shù)DIR_SRCS表示將目錄下的文件欠母,表示成變量欢策,并在下面應(yīng)用。最后在add_executable中加入給變量${DIR_SRCS}赏淌。
目錄結(jié)構(gòu)如下:

目錄結(jié)構(gòu)

多級(jí)目錄

我們源碼的目錄結(jié)構(gòu)踩寇,不會(huì)前兩個(gè)例子中,都在同一級(jí)目錄下六水,經(jīng)常我們的源碼有多級(jí)目錄俺孙。這時(shí)候我們應(yīng)該怎么編譯呢?多級(jí)目錄我們也有兩種編譯方式掷贾。

# 要求最低的cmake版本
cmake_minimum_required(VERSION 3.14)

# 工程名字 工程語言
project(cmakedemo C)

# 設(shè)置cmake c 的標(biāo)準(zhǔn)c99
set(CMAKE_C_STANDARD 99)

## 方法一:
## 將當(dāng)前目錄下的文件睛榄,都保存在DIR_SRCS變量中
#aux_source_directory(. DIR_SRCS)
## 將math目錄下的文件,都保存在DIR_MATH_SRCS變量中
#aux_source_directory(./math DIR_MATH_SRCS)
## 將變量代表的文件路徑加入到可執(zhí)行程序中編譯
#add_executable(cmakedemo3 ${DIR_SRCS} ${DIR_MATH_SRCS})

# 方法二:
# 將當(dāng)前目錄下的文件想帅,都保存在DIR_SRCS變量中
aux_source_directory(. DIR_SRCS)
# 將math目錄加入編譯
add_subdirectory(math)
# 將變量代表的文件路徑加入到可執(zhí)行程序中編譯
add_executable(cmakedemo3 ${DIR_SRCS})
# 添加鏈接庫
target_link_libraries(cmakedemo3 MathFunctions)

## 方法三:
## 將當(dāng)前目錄下的源碼懈费,都保存在DIR_SRCS變量中
#aux_source_directory(. DIR_SRCS)
## 將math目錄下的文件,都保存在DIR_MATH_SRCS變量中
#aux_source_directory(./math DIR_MATH_SRCS)
## 將DIR_MATH_SRCS保存的文件博脑,都編譯進(jìn)入靜態(tài)庫libMathFunctions.a
#add_library(MathFunctions ${DIR_MATH_SRCS})
## 將變量代表的文件路徑加入到可執(zhí)行程序中編譯
#add_executable(cmakedemo3 ${DIR_SRCS})
## 添加鏈接庫
#target_link_libraries(cmakedemo3 MathFunctions)

方法一將來自目錄中的源文件憎乙,保存成DIR_MATH_SRCS變量,然后在add_executable中應(yīng)用即可叉趣,這樣就把多級(jí)目錄下的源碼都加入到構(gòu)建中泞边。方法二使用add_subdirectorymath子目錄加入編譯,這時(shí)候math中的CMakeLists.txt文件和源碼也將作為一個(gè)編譯子目錄進(jìn)行處理疗杉。target_link_libraries指定cmakedemo3可執(zhí)行程序?qū)㈡溄?code>MathFunctions庫阵谚,MathFunctions庫將在math子目錄中生成。
math子目錄CMakeLists.txt如下:

aux_source_directory(. DIR_MATH_SRCS)
add_library(MathFunctions ${DIR_MATH_SRCS})

add_library表示將默認(rèn)生成libMathFunctions.a靜態(tài)庫烟具。
目錄結(jié)構(gòu)如下:

目錄結(jié)構(gòu)

方法三是將子目錄中的源碼梢什,都編譯成libMathFuntions.a,最后同樣的將靜態(tài)庫鏈接到目標(biāo)可執(zhí)行程序中,與方法二的區(qū)別是在通過一個(gè)CMakeLists.txt文件朝聋,就可以將靜態(tài)庫的編譯包含在內(nèi)嗡午,無需像方法二一樣在./math目錄下寫一份CMakeLists.txt文件用于專門編譯靜態(tài)庫。方法二和方法三各自有各自的好處冀痕,方法二更適合單模塊編譯荔睹,可以將某個(gè)目錄下的源文件作為一個(gè)模塊來編譯,適合龐大的目錄結(jié)構(gòu)與模塊層級(jí)編譯言蛇。

自定義編譯選項(xiàng)

# 要求最低的cmake版本
cmake_minimum_required(VERSION 3.14)

# 工程名字 工程語言
project(cmakedemo C)

# 設(shè)置cmake c 的標(biāo)準(zhǔn)c99
set(CMAKE_C_STANDARD 99)

# 加入一個(gè)配置頭文件僻他,用于處理 CMake 對源碼的設(shè)置
configure_file(
        "${PROJECT_SOURCE_DIR}/config.h.in"
        "${PROJECT_BINARY_DIR}/config.h"
)

# 設(shè)置USE_LOCALMATH打開
option(USE_LOCALMATH "TRUE USE LOCAL MATH LIBRARY" OFF)

if (USE_LOCALMATH)
    include_directories("${PROJECT_SOURCE_DIR}/math")
    add_subdirectory(math)

endif (USE_LOCALMATH)

aux_source_directory(. DIR_SRCS)

# 將變量代表的文件路徑加入到可執(zhí)行程序中編譯
add_executable(cmakedemo4 ${DIR_SRCS})
# 添加鏈接庫
if (USE_LOCALMATH)
    target_link_libraries(cmakedemo4 MathFunctions)
endif (USE_LOCALMATH)

在這里我們加入了一個(gè)config.h.in文件,這個(gè)文件腊尚,主要用來預(yù)定義宏吨拗,通過config.h.in,在編譯之后可以生成config.h文件婿斥。config.h.in文件內(nèi)容如下:

#cmakedefine USE_LOCALMATH

這里我們還使用到了option劝篷,主要是為了在進(jìn)行編譯時(shí),在CMakeLists.txt同級(jí)目錄下受扳,通過ccmake .携龟,來進(jìn)行USE_LOCALMATH變量的選擇,是否打開

開關(guān)變量

我們看到勘高,最后有一個(gè)USE_LOCALMATH變量峡蟋,可以用過enter鍵來選擇ON或者OFF,如果是ON华望,那么在生成的config.h中蕊蝗,預(yù)定義宏被打開,如下:

#define USE_LOCALMATH

main.c中赖舟,我們就能夠使用該宏定義了

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "config.h"
#ifdef USE_LOCALMATH
#include "math/MathFunctions.h"
#endif

int main(int argc, char *argv[]) {
    if (argc < 3) {
        printf("Usage: %s base exponent \n", argv[0]);
        return 1;
    }
    double base = atof(argv[1]);
    int exponent = atoi(argv[2]);
#ifdef USE_LOCALMATH
    printf("Now we use our own Math library. \n");
    double result = power(base, exponent);
#else
    printf("Now we use the standard library. \n");
    double result = pow(base, exponent);
#endif

    printf("%g ^ %d is %g\n", base, exponent, result);
    return 0;
}

目錄結(jié)構(gòu)如下:


目錄結(jié)構(gòu)

環(huán)境檢查

我們有時(shí)候需要在編譯過程中蓬戚,檢查系統(tǒng)的環(huán)境,是否支持某些函數(shù)宾抓,這個(gè)例子中子漩,我們檢查是否編譯環(huán)境自帶pow函數(shù)豫喧,如果自帶pow函數(shù),則使用pow函數(shù)幢泼,如果沒有則使用自定義的power函數(shù)紧显。

# 要求最低的cmake版本
cmake_minimum_required(VERSION 3.14)

# 工程名字 工程語言
project(cmakedemo C)

# 設(shè)置cmake c 的標(biāo)準(zhǔn)c99
set(CMAKE_C_STANDARD 99)

# 檢查系統(tǒng)是否支持 pow 函數(shù)
include(${CMAKE_ROOT}/Modules/CheckFunctionExists.cmake)
# 如果pow函數(shù)存在,則定義HAVE_POW宏缕棵,這個(gè)宏可以在下面的if條件中使用孵班,也可以在config.h.in中預(yù)定義cmakedefine HAVE_POW
check_function_exists(pow HAVE_POW)

# 加入一個(gè)配置頭文件,用于處理 CMake 對源碼的設(shè)置
configure_file(
        "${PROJECT_SOURCE_DIR}/config.h.in"
        "${PROJECT_BINARY_DIR}/config.h"
)

# 如果宏未定義招驴,則引入自定義的power函數(shù)
if (!HAVE_POW)
    include_directories("${PROJECT_SOURCE_DIR}/math")
    add_subdirectory(math)
endif (!HAVE_POW)

aux_source_directory(. DIR_SRCS)

# 將變量代表的文件路徑加入到可執(zhí)行程序中編譯
add_executable(cmakedemo6 ${DIR_SRCS})
# 添加鏈接庫
if (!HAVE_POW)
    target_link_libraries(cmakedemo4 MathFunctions)
endif (!HAVE_POW)

首先在頂層 CMakeLists.txt 文件中添加 CheckFunctionExists.cmake 宏篙程,并調(diào)用 check_function_exists命令測試鏈接器是否能夠在鏈接階段找到 pow 函數(shù)。如果找到pow函數(shù)别厘,則定義HAVE_POW宏虱饿,當(dāng)然,在config.h.in中需要預(yù)定義HAVE_POW宏丹允,如下:

#cmakedefine USE_LOCALMATH
#cmakedefine HAVE_POW
#cmakedefine HAVE_LOCALPOWER

隨后在生成的config.h中郭厌,就會(huì)定義上該宏,如下:

/* #undef USE_LOCALMATH */
#define HAVE_POW
/* #undef HAVE_LOCALPOWER */

這時(shí)候雕蔽,就可以在源碼中使用宏了折柠。

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "config.h"
#ifdef USE_LOCALMATH
#include "math/MathFunctions.h"
#endif

int main(int argc, char *argv[]) {
    if (argc < 3) {
        printf("Usage: %s base exponent \n", argv[0]);
        return 1;
    }
    double base = atof(argv[1]);
    int exponent = atoi(argv[2]);
#ifndef HAVE_POW
    printf("Now we use our own Math library. \n");
    double result = power(base, exponent);
#else
    printf("Now we use the standard library. \n");
    double result = pow(base, exponent);
#endif

    printf("%g ^ %d is %g\n", base, exponent, result);

#ifdef HAVE_LOCALPOWER
    printf("HAVE_LOCALPOWER . \n");
#elif defined(HAVE_POW)
    printf("HAVE_POW . \n");
#endif
    return 0;
}

這里有個(gè)注意點(diǎn)check_function_exists需要在configure_file定義config.h.in之前調(diào)用,否則對于config.h.in中預(yù)定義的宏無效批狐。同樣的扇售,在CMakeLists.txt中也能夠使用HAVE_POW宏來判斷是否引入math目錄,是否將math作為子目錄加入編譯嚣艇,最后是否鏈接MathFunctions靜態(tài)庫歧斟。

添加版本號(hào)

在應(yīng)用程序中奢方,維護(hù)庫或者可執(zhí)行程序的版本號(hào)是一個(gè)好的習(xí)慣,配合changelog,能夠很直觀的看到庫的更新迭代過程仇矾。在cmake中我們怎樣添加版本號(hào)管理呢织咧?

# 要求最低的cmake版本
cmake_minimum_required(VERSION 3.14)

# 工程名字 工程語言
project(cmakedemo C)

# 設(shè)置cmake c 的標(biāo)準(zhǔn)c99
set(CMAKE_C_STANDARD 99)

set(VERSION_MAJOR 1)
set(VERSION_MINOR 0)

config.h.in文件中八秃,添加預(yù)定義

#define VERSION_MAJOR @VERSION_MAJOR@
#define VERSION_MINOR @VERSION_MINOR@

這樣薛闪,在生成的config.h文件中就有VERSION_MAJORVERSION_MINOR的定義,在代碼中使用如下:

printf("major version %d , minor version %d \n", VERSION_MAJOR, VERSION_MINOR);

編譯動(dòng)靜態(tài)庫

上面我們都是生成可執(zhí)行程序吱抚,如果我們想要生成動(dòng)態(tài)庫或者靜態(tài)庫應(yīng)該怎么做呢百宇?

動(dòng)態(tài)庫

aux_source_directory(. DIR_MATH_SRCS)
add_library(MathFunctions SHARED ${DIR_MATH_SRCS})

生成動(dòng)態(tài)庫如下:


動(dòng)態(tài)庫

靜態(tài)庫

aux_source_directory(. DIR_MATH_SRCS)
add_library(MathFunctions STATIC ${DIR_MATH_SRCS})

生成靜態(tài)庫如下:

靜態(tài)庫

主要區(qū)別是在add_library時(shí)指定STATIC/SHARED參數(shù)即可。

基本操作總結(jié)

通過以上基本操作秘豹,我們了解了如何生成可執(zhí)行程序携御,生成動(dòng)/靜態(tài)庫,如何添加版本號(hào)、如何進(jìn)行環(huán)境檢查啄刹、如何預(yù)定義宏涮坐、如何對多級(jí)目錄進(jìn)行編譯。對于cmake鸵膏,我們已經(jīng)有了一個(gè)大概的了解膊升,后續(xù)繼續(xù)講一下在android中如何與cmake配合使用,來完成我們的目標(biāo)谭企。

CMake與Android

在android平臺(tái)中,系統(tǒng)已經(jīng)為我們內(nèi)置了很多的原生api供我們鏈接調(diào)用评肆,不同的系統(tǒng)api债查,android為我們提供了不同的庫,具體可以參考Android NDK 原生 API瓜挽。這些預(yù)構(gòu)建的庫盹廷,已經(jīng)存在在android平臺(tái)上了,我們無需將他們打包到apk中久橙,因?yàn)镹DK庫已經(jīng)是cmake搜索路徑的一部分俄占,所以找到提供庫的名字,鏈接到所需庫即可淆衷。那我們要怎么做才能使用這些庫呢缸榄?

find_library用法

添加find_library()命令到你的cmake構(gòu)建腳本用于定位ndk庫路徑,并且將路徑存儲(chǔ)變量中祝拯。你可以在腳本的其他地方使用這個(gè)變量甚带,下面例子是查找android平臺(tái)的log庫,將路徑存儲(chǔ)在log-lib變量中佳头。

find_library( # Defines the name of the path variable that stores the
              # location of the NDK library.
              log-lib

              # Specifies the name of the NDK library that
              # CMake needs to locate.
              log )

接下來我們需要將ndk庫鹰贵,鏈接到我們的目標(biāo)程序或者目標(biāo)庫中:

# Links your native library against one or more other native libraries.
target_link_libraries( # Specifies the target library.
                       native-lib

                       # Links the log library to the target library.
                       ${log-lib} )

這里target_link_libraries含義是將${log-lib} 路徑的ndk庫,鏈接到native-lib.so中

添加預(yù)構(gòu)建的動(dòng)態(tài)庫

添加一個(gè)預(yù)先構(gòu)建的庫康嘉,類似于為CMake指定另一個(gè)本地構(gòu)建庫碉输。然而因?yàn)閹煲呀?jīng)預(yù)構(gòu)建,你需要使用IMPORTED
告訴cmake亭珍,你需要引入庫到你的構(gòu)建工程中敷钾。

add_library( imported-lib
             SHARED
             IMPORTED )

這里只是指定了引入一個(gè)動(dòng)態(tài)庫,并且動(dòng)態(tài)庫名稱存儲(chǔ)在本地變量imported-lib中块蚌。接著需要設(shè)置該動(dòng)態(tài)庫imported-lib的屬性闰非,首先指定具體庫的位置

set_target_properties( # Specifies the target library.
                       imported-lib

                       # Specifies the parameter you want to define.
                       PROPERTIES IMPORTED_LOCATION

                       # Provides the path to the library you want to import.
                       imported-lib/src/${ANDROID_ABI}/libimported-lib.so )

這里set_target_properties()定義了一個(gè)imported-lib庫的屬性IMPORTED_LOCATION,指定了該庫的在本地操作系統(tǒng)中的位置峭范,這樣财松,結(jié)合上面add_library我們就完整的在cmake中引入了一個(gè)動(dòng)態(tài)庫,并且存儲(chǔ)在imported-lib本地變量中,待后續(xù)使用辆毡。
當(dāng)然菜秦,我們引入了動(dòng)態(tài)庫還不夠,編譯時(shí)舶掖,經(jīng)常還需要用到動(dòng)態(tài)庫的頭文件球昨,那么頭文件該怎么引入呢?

include_directories(imported-lib/include/)

include_directories中是頭文件在操作系統(tǒng)中的相對路徑或者絕對路徑眨攘,相對路徑是對于當(dāng)前CMakeLists.txt的位置而定主慰。

添加預(yù)構(gòu)建的靜態(tài)庫

添加預(yù)構(gòu)建的靜態(tài)庫,與動(dòng)態(tài)庫類似鲫售,只是在add_libraryset_target_properties中有所不同

add_library(imported-static-lib STATIC IMPORTED)
set_target_properties(imported-static-lib PROPERTIES IMPORTED_LOCATION imported-lib/src/${ANDROID_ABI}/libimported-lib.a)

主要區(qū)別是靜態(tài)庫在add_library中是STATIC共螺,而動(dòng)態(tài)庫是SHARED,靜態(tài)庫會(huì)編譯進(jìn)目標(biāo)動(dòng)態(tài)庫中情竹,而動(dòng)態(tài)庫藐不,最后編譯完apk后,通過APK Analyzer查看秦效,在apk的lib/${ANDROID_ABI}/目錄下雏蛮,有你所鏈接的動(dòng)態(tài)庫。

編譯過程構(gòu)建靜態(tài)庫

在編譯過程中阱州,可能會(huì)存在整個(gè)c工程會(huì)很龐大挑秉,例如筆者目前工作中的一個(gè)工程源碼就很龐大,有多個(gè)不同的模塊贡耽,組件衷模,多級(jí)目錄。那這種情況下我們可以將某些組件蒲赂,先編譯成靜態(tài)庫阱冶,然后將靜態(tài)庫參與最終目標(biāo)動(dòng)態(tài)庫的編譯。參考CMake基本操作->多級(jí)目錄章節(jié)滥嘴,有三種方法可以參考木蹬。

多工程編譯

多工程編譯類似于CMake基本操作->多級(jí)目錄章節(jié)中的方法二,這里就不重新講若皱。參考示例如下:

# Sets lib_src_DIR to the path of the target CMake project.
set( lib_src_DIR ../gmath )

# Sets lib_build_DIR to the path of the desired output directory.
set( lib_build_DIR ../gmath/outputs )
file(MAKE_DIRECTORY ${lib_build_DIR})

# Adds the CMakeLists.txt file located in the specified directory
# as a build dependency.
add_subdirectory( # Specifies the directory of the CMakeLists.txt file.
                  ${lib_src_DIR}

                  # Specifies the directory for the build outputs.
                  ${lib_build_DIR} )

# Adds the output of the additional CMake build as a prebuilt static
# library and names it lib_gmath.
add_library( lib_gmath STATIC IMPORTED )
set_target_properties( lib_gmath PROPERTIES IMPORTED_LOCATION
                       ${lib_build_DIR}/${ANDROID_ABI}/lib_gmath.a )
include_directories( ${lib_src_DIR}/include )

# Links the top-level CMake build output against lib_gmath.
target_link_libraries( native-lib ... lib_gmath )

CMake與Android結(jié)合總結(jié)

本章主要講解了cmake與android和結(jié)合镊叁,如何在android中使用cmake,cmake如何使用android平臺(tái)自帶的系統(tǒng)庫走触,構(gòu)建動(dòng)/靜態(tài)庫的過程晦譬,以及多工程編譯,這里已經(jīng)基本滿足我們?nèi)粘DK開發(fā)過程中遇到的大部分情況互广。

CMake與Gradle

未完待續(xù)......

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末敛腌,一起剝皮案震驚了整個(gè)濱河市卧土,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌像樊,老刑警劉巖尤莺,帶你破解...
    沈念sama閱讀 221,820評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異生棍,居然都是意外死亡颤霎,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,648評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門涂滴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來友酱,“玉大人,你說我怎么就攤上這事柔纵〈馕郏” “怎么了?”我有些...
    開封第一講書人閱讀 168,324評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵首量,是天一觀的道長。 經(jīng)常有香客問我进苍,道長加缘,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,714評(píng)論 1 297
  • 正文 為了忘掉前任觉啊,我火速辦了婚禮拣宏,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘杠人。我一直安慰自己勋乾,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,724評(píng)論 6 397
  • 文/花漫 我一把揭開白布嗡善。 她就那樣靜靜地躺著辑莫,像睡著了一般。 火紅的嫁衣襯著肌膚如雪罩引。 梳的紋絲不亂的頭發(fā)上各吨,一...
    開封第一講書人閱讀 52,328評(píng)論 1 310
  • 那天,我揣著相機(jī)與錄音袁铐,去河邊找鬼揭蜒。 笑死,一個(gè)胖子當(dāng)著我的面吹牛剔桨,可吹牛的內(nèi)容都是我干的屉更。 我是一名探鬼主播,決...
    沈念sama閱讀 40,897評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼洒缀,長吁一口氣:“原來是場噩夢啊……” “哼瑰谜!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,804評(píng)論 0 276
  • 序言:老撾萬榮一對情侶失蹤似舵,失蹤者是張志新(化名)和其女友劉穎脚猾,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體砚哗,經(jīng)...
    沈念sama閱讀 46,345評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡龙助,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,431評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蛛芥。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片提鸟。...
    茶點(diǎn)故事閱讀 40,561評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖仅淑,靈堂內(nèi)的尸體忽然破棺而出称勋,到底是詐尸還是另有隱情,我是刑警寧澤涯竟,帶...
    沈念sama閱讀 36,238評(píng)論 5 350
  • 正文 年R本政府宣布赡鲜,位于F島的核電站,受9級(jí)特大地震影響庐船,放射性物質(zhì)發(fā)生泄漏银酬。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,928評(píng)論 3 334
  • 文/蒙蒙 一筐钟、第九天 我趴在偏房一處隱蔽的房頂上張望揩瞪。 院中可真熱鬧,春花似錦篓冲、人聲如沸李破。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,417評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽嗤攻。三九已至,卻和暖如春瞭恰,著一層夾襖步出監(jiān)牢的瞬間屯曹,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,528評(píng)論 1 272
  • 我被黑心中介騙來泰國打工惊畏, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留恶耽,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,983評(píng)論 3 376
  • 正文 我出身青樓颜启,卻偏偏與公主長得像偷俭,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子缰盏,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,573評(píng)論 2 359

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