手把手教你如何在Android下進(jìn)行JNI開(kāi)發(fā)(入門(mén))

在進(jìn)行Android開(kāi)發(fā)的過(guò)程中昌渤,我們必定會(huì)遇到視頻圖像處理、高強(qiáng)度密集運(yùn)算憔四、特殊算法等場(chǎng)景膀息,這時(shí)我們就不得不需要去接觸一些C/C++代碼,進(jìn)行JNI開(kāi)發(fā)了赵。下面我將從Android.mk和CMake這兩種方式教大家如何進(jìn)行開(kāi)發(fā)潜支。文章結(jié)尾將給出演示的項(xiàng)目代碼,如果你能耐心地仔細(xì)看完柿汛,相信你一定能掌握如何在Android下進(jìn)行JNI開(kāi)發(fā)冗酿。


使用Android.mk進(jìn)行JNI開(kāi)發(fā)

1.編寫(xiě)native接口和C/C++代碼

定義native接口

package com.xuexiang.jnidemo;

public class JNIApi {

    public native String stringFromJNI();
}

編寫(xiě)C/C++代碼

extern "C" JNIEXPORT jstring
JNICALL
Java_com_xuexiang_jnidemo_JNIApi_stringFromJNI(
        JNIEnv *env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

2.編寫(xiě)Android.mk

模版如下:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := native-lib

LOCAL_SRC_FILES := native-lib.cpp

## 導(dǎo)入logcat日志庫(kù)
LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog

include $(BUILD_SHARED_LIBRARY)

說(shuō)明:

  • LOCAL_PATH := $(call my-dir) :指向當(dāng)前目錄的地址,包含該.mk

  • include $(CLEAR_VARS):清理掉所有以LOCAL_開(kāi)頭的內(nèi)容,這句話是必須的络断,因?yàn)槿绻械淖兞慷际侨值牟锰妫械目煽氐木幾g文件都需要在一個(gè)單獨(dú)的GNU中被解析并執(zhí)行。

  • LOCAL_MODULE:調(diào)用的庫(kù)名貌笨,用來(lái)區(qū)分android.mk中的每一個(gè)模塊弱判。文件名必須是唯一的,不能有空格锥惋。注意昌腰,這里編譯器會(huì)為你自動(dòng)加上一些前綴lib和后綴.so,來(lái)保證文件是一致的膀跌。

  • LOCAL_SRC_FILES:變量必須包含一個(gè)C遭商、C++或者java源文件的列表,這些會(huì)被編譯并聚合到一個(gè)模塊中,文件之間可以用空格或Tab鍵進(jìn)行分割,換行請(qǐng)用"\"

  • LOCAL_LDLIBS:定義需要鏈接的庫(kù)捅伤。一般用于鏈接那些存在于系統(tǒng)目錄下本模塊需要鏈接的庫(kù)(比如這里的logcat庫(kù))劫流。

  • include $(BUILD_SHARED_LIBRARY):來(lái)生成一個(gè)動(dòng)態(tài)庫(kù)libnative-lib.so

3.編寫(xiě)Application.mk

# APP_ABI := armeabi armeabi-v7a arm64-v8a x86
APP_ABI := all
APP_OPTIM := release

## 引用靜態(tài)庫(kù)
APP_STL := stlport_static
#NDK_TOOLCHAIN_VERSION=4.8
#APP_PLATFORM := android-14

說(shuō)明:

  • APP_ABI:定義編譯so文件的CPU型號(hào)徒仓,all為所有類(lèi)型懂讯。也可以指定特定類(lèi)型的CPU型號(hào)摄咆,直接使用空格隔開(kāi)岖沛。

  • APP_OPTIM:優(yōu)化選項(xiàng)姊途,非必填许布。其值可以為'release'或'debug'.此變量用來(lái)修改優(yōu)先等級(jí).默認(rèn)情況下為release.在release模式下聂使,將編譯生成被優(yōu)化了的二進(jìn)制的機(jī)器碼趣席,而debug模塊用來(lái)生成便于調(diào)試的未被優(yōu)化的二進(jìn)制機(jī)器碼粮彤。

  • APP_STL:選擇支持的C++標(biāo)準(zhǔn)庫(kù)根穷。在默認(rèn)情況下,NDK通過(guò)Androoid自帶的最小化的C++運(yùn)行庫(kù)(system/lib/libstdc++.so)來(lái)提供標(biāo)準(zhǔn)C++頭文件.然而导坟,NDK提供了可供選擇的C++實(shí)現(xiàn)屿良,你可以通過(guò)此變量來(lái)選擇使用哪個(gè)或鏈接到你的程序。

APP_STL := stlport_static    --> static STLport library
APP_STL := stlport_shared    --> shared STLport library
APP_STL := system            --> default C++ runtime library

比如惫周,這里我們使用到了#include <string>尘惧,就需要設(shè)置stlport_static

4.設(shè)置項(xiàng)目根目錄的local.properties文件

因?yàn)锳ndroid Studio 2.2以后推薦使用CMake進(jìn)行JNI開(kāi)發(fā),因此需要修改一下參數(shù)進(jìn)行兼容递递。

android.useDeprecatedNdk=true

5.編譯C/C++代碼生成so文件

cd 到j(luò)ni(存放Android.mk的目錄)下喷橙,執(zhí)行ndk-build即可啥么。

執(zhí)行成功后,將會(huì)在jni的同級(jí)目錄下生成libsobj文件夾贰逾,存放的是編譯好的so文件悬荣。

6.在模塊的build.gradle中設(shè)置so文件路徑

sourceSets {
    main {
        jni.srcDirs = []
        jniLibs.srcDirs = ['src/main/libs']
    }
}

至此完成了Android.mk的設(shè)置,下面我們就可以愉快地進(jìn)行jni開(kāi)發(fā)了疙剑!


上面介紹的Android.mk都可以在Eclispe和Android Studio下進(jìn)行編譯開(kāi)發(fā)氯迂,可以說(shuō)是一種比較傳統(tǒng)的做法。下面我將介紹Android Studio著重推薦的CMake方式進(jìn)行JNI開(kāi)發(fā)言缤。

使用CMake進(jìn)行JNI開(kāi)發(fā)

開(kāi)發(fā)環(huán)境

JNI:Java Native Interface(Java 本地編程接口)嚼蚀,一套編程規(guī)范,它提供了若干的 API 實(shí)現(xiàn)了 Java 和其他語(yǔ)言的通信(主要是 C/C++)管挟。Java 可以通過(guò) JNI 調(diào)用本地的 C/C++ 代碼轿曙,本地的 C/C++ 代碼也可以調(diào)用 java 代碼。Java 通過(guò) C/C++ 使用本地的代碼的一個(gè)關(guān)鍵性原因在于 C/C++ 代碼的高效性哮独。

在 Android Studio 下拳芙,進(jìn)行JNI的開(kāi)發(fā),需要準(zhǔn)備以下內(nèi)容:

  • Android Studio 2.2以上皮璧。

  • NDK:這套工具集允許為 Android 使用 C 和 C++ 代碼舟扎。

  • CMake:一款外部構(gòu)建工具,可與 Gradle 搭配使用來(lái)構(gòu)建原生庫(kù)悴务。如果只計(jì)劃使用 ndk-build睹限,則不需要此組件。

  • LLDB:一種調(diào)試程序讯檐,Android Studio 使用它來(lái)調(diào)試原生代碼羡疗。

image

創(chuàng)建支持C++的項(xiàng)目

新建支持C++的項(xiàng)目

在新建項(xiàng)目時(shí),勾上Include C++ support就行了:

image

在向?qū)У?Customize C++ Support 部分别洪,有下列自定義項(xiàng)目可供選擇:

  • C++ Standard:使用下拉列表選擇使用哪種 C++ 標(biāo)準(zhǔn)叨恨。選擇 Toolchain Default 會(huì)使用默認(rèn)的 CMake 設(shè)置。
  • Exceptions Support:如果希望啟用對(duì) C++ 異常處理的支持挖垛,請(qǐng)選中此復(fù)選框痒钝。如果啟用此復(fù)選框,Android Studio 會(huì)將 -fexceptions 標(biāo)志添加到模塊級(jí) build.gradle文件的 cppFlags中痢毒,Gradle 會(huì)將其傳遞到 CMake送矩。
  • Runtime Type Information Support:如果希望支持 RTTI,請(qǐng)選中此復(fù)選框哪替。如果啟用此復(fù)選框栋荸,Android Studio 會(huì)將 -frtti 標(biāo)志添加到模塊級(jí) build.gradle文件的 cppFlags中,Gradle 會(huì)將其傳遞到 CMake。
image

支持C++的項(xiàng)目目錄

image
  • src/main/cpp下存放的我們編寫(xiě)供JNI調(diào)用的C++源碼晌块。

  • CMakeLists.txt文件是CMake的配置文件,通常他包含的內(nèi)容如下:

# TODO 設(shè)置構(gòu)建本機(jī)庫(kù)文件所需的 CMake的最小版本
cmake_minimum_required(VERSION 3.4.1)

# TODO 添加自己寫(xiě)的 C/C++源文件
add_library( native-lib
             SHARED
             src/main/cpp/native-lib.cpp )

# TODO 依賴(lài) NDK中的庫(kù)
find_library( log-lib
              log )

# TODO 將目標(biāo)庫(kù)與 NDK中的庫(kù)進(jìn)行連接
target_link_libraries( native-lib
                       ${log-lib} )

build.gradle的配置

android {
    ...
    defaultConfig {
        ...
        externalNativeBuild {
            cmake {
                // 默認(rèn)是 “ cppFlags "" ”
                // 如果要修改 Customize C++ Support 部分爱沟,可在這里加入
                cppFlags "-frtti -fexceptions"
            }
        }

        ndk {
            // abiFiliter: ABI 過(guò)濾器(application binary interface,應(yīng)用二進(jìn)制接口)
            // Android 支持的 CPU 架構(gòu)
            abiFilters 'armeabi-v7a','arm64-v8a','x86','x86_64'//, 'armeabi' 不支持了
        }
    }
    buildTypes {
        ...
    }
    externalNativeBuild {
        cmake {
            path "CMakeLists.txt"
        }
    }
}

注意事項(xiàng)

  • 1.在使用JNI前摸袁,需要加載so庫(kù)
static {
    System.loadLibrary("native-lib");
}
  • 2.快速生成C++代碼:先在java中定義native方法钥顽,然后使用Alt + Enter快捷鍵自動(dòng)生成C++方法體义屏。
image
  • 3.CPP 資源文件夾下面的文件和文件夾不能重名靠汁,不然 System.loadLibrary() 時(shí)找不到,會(huì)報(bào)錯(cuò):java.lang.UnsatisfiedLinkError: Native method not found.

  • 4.在定義庫(kù)的名字時(shí)闽铐,不要加前綴 lib 和后綴 .so蝶怔,不然會(huì)報(bào)錯(cuò):java.lang.UnsatisfiedLinkError: Couldn’t load xxx : findLibrary【findLibrary returned null錯(cuò)誤.

  • 5.新建 C/C++ 源代碼文件,要添加到 CMakeLists.txt 文件中兄墅。

# 增加c++源代碼
add_library( # library的名稱(chēng).
             native-lib

             # 標(biāo)志庫(kù)共享.
             SHARED

             # C++源碼文件的相對(duì)路徑.
             src/main/cpp/native-lib.cpp )

# 將目標(biāo)庫(kù)與 NDK中的庫(kù)進(jìn)行連接
target_link_libraries( # 目標(biāo)library的名稱(chēng).
                    native-lib
                    ${log-lib} )
  • 6.引入第三方 .so文件踢星,要添加到 CMakeLists.txt 文件中。
# TODO 添加第三方庫(kù)
# TODO add_library(libavcodec-57
# TODO 原先生成的.so文件在編譯后會(huì)自動(dòng)添加上前綴lib和后綴.so隙咸,
# TODO       在定義庫(kù)的名字時(shí)沐悦,不要加前綴lib和后綴 .so,
# TODO       不然會(huì)報(bào)錯(cuò):java.lang.UnsatisfiedLinkError: Couldn't load xxx : findLibrary returned null
add_library(avcodec-57
            # TODO STATIC表示靜態(tài)的.a的庫(kù)五督,SHARED表示.so的庫(kù)
            SHARED
            IMPORTED)
set_target_properties(avcodec-57
                      PROPERTIES IMPORTED_LOCATION
                      # TODO ${CMAKE_SOURCE_DIR}:表示 CMakeLists.txt的當(dāng)前文件夾路徑
                      # TODO ${ANDROID_ABI}:編譯時(shí)會(huì)自動(dòng)根據(jù) CPU架構(gòu)去選擇相應(yīng)的庫(kù)
                      # TODO ABI文件夾上面不要再分層藏否,直接就 jniLibs/${ANDROID_ABI}/
                      # TODO ${CMAKE_SOURCE_DIR}/src/main/jniLibs/ffmpeg/${ANDROID_ABI}/libavcodec-57.so
                      ${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libavcodec-57.so)
  • 7.引入第三方 .h 文件夾,也要添加到 CMakeLists.txt 文件中
# TODO include_directories( src/main/jniLibs/${ANDROID_ABI}/include )
# TODO 路徑指向上面會(huì)編譯出錯(cuò)(無(wú)法在jniLibs中引入)充包,指向下面的路徑就沒(méi)問(wèn)題
include_directories( src/main/cpp/ffmpeg/include )
  • 8.C++ library編譯生成的so文件副签,在 build/intermediates/cmake
image

至此完成了CMake的設(shè)置,下面我們就可以愉快地進(jìn)行jni開(kāi)發(fā)了基矮!


講完了兩種進(jìn)行JNI開(kāi)發(fā)的姿勢(shì)后淆储,下面我們來(lái)簡(jiǎn)單講講JNI的基礎(chǔ)語(yǔ)法。

JNI基礎(chǔ)語(yǔ)法

基礎(chǔ)類(lèi)型

Java類(lèi)型 native類(lèi)型 描述
boolean jboolean unsigned 8 bits
byte jbyte signed 8 bits
char jchar unsigned 16 bits
short jshort signed 16 bits
int jint signed 32 bits
long jlong signed 64 bits
float jfloat 32 bits
double jdouble 64 bits
void void N/A

引用類(lèi)型

JNI為不同的java對(duì)象提供了不同的引用類(lèi)型家浇,JNI引用類(lèi)型如下:

image

在c里面本砰,所有JNI引用類(lèi)型其實(shí)都是jobject。

Native方法參數(shù)

  • JNI接口指針是native方法的第一個(gè)參數(shù)钢悲,JNI接口指針的類(lèi)型是JNIEnv点额。
  • 第二個(gè)參數(shù)取決于native method是否靜態(tài)方法,如果是非靜態(tài)方法譬巫,那么第二個(gè)參數(shù)是對(duì)對(duì)象的引用咖楣,如果是靜態(tài)方法,則第二個(gè)參數(shù)是對(duì)它的class類(lèi)的引用
  • 剩下的參數(shù)跟Java方法參數(shù)一一對(duì)應(yīng)
extern "C" /* specify the C calling convention */
jdouble Java_pkg_Cls_f__ILjava_lang_String_2 (

     JNIEnv *env,        /* interface pointer */

     jobject obj,        /* "this" pointer */

     jint i,             /* argument #1 */

     jstring s)          /* argument #2 */
{

     const char *str = env->GetStringUTFChars(s, 0);

     ...

     env->ReleaseStringUTFChars(s, str);

     return ...

}

點(diǎn)擊查看JNI接口

簽名描述

基礎(chǔ)數(shù)據(jù)類(lèi)型

Java類(lèi)型 簽名描述
boolean Z
byte B
char C
short S
int I
long J
float F
double D
void

引用數(shù)據(jù)類(lèi)型

(以L開(kāi)頭芦昔,以;結(jié)束诱贿,中間對(duì)應(yīng)的是該類(lèi)型的完整路徑)

String : Ljava/lang/String;
Object : Ljava/lang/Object;
自定義類(lèi)型 Area : Lcom/xuexiang/jnidemo/Area;

數(shù)組

(在類(lèi)型前面添加[,幾維數(shù)組就在前面添加幾個(gè)[)

int [] :[I
Long[][]  : [[J
Object[][][] : [[[Ljava/lang/Object

使用命令查看

javap -s <java類(lèi)的class文件路徑>

class文件存在于 build->intermediates->classes下。

image

JNI常見(jiàn)用法

1、jni訪問(wèn)java非靜態(tài)成員變量

  • 1.使用GetObjectClass珠十、FindClass獲取調(diào)用對(duì)象的類(lèi)

  • 2.使用GetFieldID獲取字段的ID料扰。這里需要傳入字段類(lèi)型的簽名描述。

  • 3.使用GetIntField焙蹭、GetObjectField等方法晒杈,獲取字段的值。使用SetIntField孔厉、SetObjectField等方法拯钻,設(shè)置字段的值。

注意:即使字段是private也照樣可以正常訪問(wèn)撰豺。

extern "C"
JNIEXPORT void JNICALL
Java_com_xuexiang_jnidemo_JNIApi_testCallNoStaticField(JNIEnv *env, jobject instance) {
    //獲取jclass
    jclass j_class = env->GetObjectClass(instance);
    //獲取jfieldID
    jfieldID j_fid = env->GetFieldID(j_class, "noStaticField", "I");
    //獲取java成員變量int值
    jint j_int = env->GetIntField(instance, j_fid);
    LOGI("noStaticField==%d", j_int);//noStaticField==0

    //Set<Type>Field    修改noStaticKeyValue的值改為666
    env->SetIntField(instance, j_fid, 666);
}

2粪般、jni訪問(wèn)java靜態(tài)成員變量

  • 1.使用GetObjectClassFindClass獲取調(diào)用對(duì)象的類(lèi)

  • 2.使用GetStaticFieldID獲取字段的ID污桦。這里需要傳入字段類(lèi)型的簽名描述亩歹。

  • 3.使用GetStaticIntFieldGetStaticObjectField等方法凡橱,獲取字段的值小作。使用SetStaticIntFieldSetStaticObjectField等方法稼钩,設(shè)置字段的值顾稀。

3、jni調(diào)用java非靜態(tài)成員方法

  • 1.使用GetObjectClass变抽、FindClass獲取調(diào)用對(duì)象的類(lèi)

  • 2.使用GetMethodID獲取方法的ID础拨。這里需要傳入方法的簽名描述。

  • 3.使用CallVoidMethod執(zhí)行無(wú)返回值的方法绍载,使用CallIntMethod诡宗、CallBooleanMethod等執(zhí)行有返回值的方法。

extern "C"
JNIEXPORT void JNICALL
Java_com_xuexiang_jnidemo_JNIApi_testCallParamMethod(JNIEnv *env, jobject instance) {
    //回調(diào)JNIApi中的noParamMethod
    jclass clazz = env->FindClass("com/xuexiang/jnidemo/JNIApi");
    if (clazz == NULL) {
        printf("find class Error");
        return;
    }
    jmethodID id = env->GetMethodID(clazz, "paramMethod", "(I)V");
    if (id == NULL) {
        printf("find method Error");
        return;
    }
    env->CallVoidMethod(instance, id, ++number);
}

4击儡、jni調(diào)用java靜態(tài)成員方法

  • 1.使用GetObjectClass塔沃、FindClass獲取調(diào)用對(duì)象的類(lèi)

  • 2.使用GetStaticMethodID獲取方法的ID。這里需要傳入方法的簽名描述阳谍。

  • 3.使用CallStaticVoidMethod執(zhí)行無(wú)返回值的方法蛀柴,使用CallStaticIntMethodCallStaticBooleanMethod等執(zhí)行有返回值的方法矫夯。

5鸽疾、jni調(diào)用java構(gòu)造方法

  • 1.使用FindClass獲取需要構(gòu)造的類(lèi)

  • 2.使用GetMethodID獲取構(gòu)造方法的ID。方法名為<init>, 這里需要傳入方法的簽名描述训貌。

  • 3.使用NewObject執(zhí)行創(chuàng)建對(duì)象制肮。

extern "C"
JNIEXPORT jint JNICALL
Java_com_xuexiang_jnidemo_JNIApi_testCallConstructorMethod(JNIEnv *env, jobject instance) {
    //獲取jclass
    jclass j_class = env->FindClass("com/xuexiang/jnidemo/Area");
    //找到構(gòu)造方法jmethodID   public Area(int width, int height)
    jmethodID j_constructor_methoid = env->GetMethodID(j_class, "<init>", "(II)V");
    //初始化java類(lèi)構(gòu)造方法  public Area(int width, int height)
    jobject j_Area_obj = env->NewObject(j_class, j_constructor_methoid, 2, 10);

    //找到getArea()  jmethodID
    jmethodID j_getArea_methoid = env->GetMethodID(j_class, "getArea", "()I");
    //調(diào)用java中的   public int getArea() 獲取面積
    jint j_area = env->CallIntMethod(j_Area_obj, j_getArea_methoid);
    LOGI("面積==%d", j_area);//面積==20
    return j_area;
}

6冒窍、jni引用全局變量

  • 使用NewGlobalRef創(chuàng)建全局引用,使用NewLocalRef創(chuàng)建局部引用。

  • 局部引用豺鼻,通過(guò)DeleteLocalRef手動(dòng)釋放對(duì)象;全局引用综液,通過(guò)DeleteGlobalRef手動(dòng)釋放對(duì)象。

  • 引用不主動(dòng)釋放會(huì)導(dǎo)致內(nèi)存泄漏儒飒。

7谬莹、jni異常處理

  • 使用ExceptionOccurred進(jìn)行異常的檢測(cè)。注意桩了,這里只能檢測(cè)java異常附帽。

  • 使用ExceptionClear進(jìn)行異常的清除。

  • 使用ThrowNew來(lái)上拋異常圣猎。

注意士葫,ExceptionOccurredExceptionClear一般是成對(duì)出現(xiàn)的乞而,類(lèi)似于java的try-catch送悔。

//上拋java異常
void throwException(JNIEnv *env, const char *message) {
    jclass newExcCls = env->FindClass("java/lang/Exception");
    env->ThrowNew(newExcCls, message);
}

extern "C"
JNIEXPORT void JNICALL
Java_com_xuexiang_jnidemo_JNIApi_jniTryCatchException(JNIEnv *env, jobject instance) {

    //獲取jclass
    jclass j_class = env->GetObjectClass(instance);
    //獲取jfieldID
    jfieldID j_fid = env->GetFieldID(j_class, "method", "Ljava/lang/String666;");

    //檢測(cè)是否發(fā)生Java異常
    jthrowable exception = env->ExceptionOccurred();
    if (exception != NULL) {
        LOGE("jni發(fā)生異常");
        //jni清空異常信息
        env->ExceptionClear(); //需要和ExceptionOccurred方法成對(duì)出現(xiàn)
        throwException(env, "native出錯(cuò)!");
    }
}

8爪模、日志打印

#include <android/log.h> //引用android log

//定義日志打印的方法
#define TAG "CMake-JNI" // 這個(gè)是自定義的LOG的標(biāo)識(shí)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,TAG ,__VA_ARGS__) // 定義LOGD類(lèi)型
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,TAG ,__VA_ARGS__) // 定義LOGI類(lèi)型
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,TAG ,__VA_ARGS__) // 定義LOGW類(lèi)型
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,TAG ,__VA_ARGS__) // 定義LOGE類(lèi)型
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL,TAG ,__VA_ARGS__) // 定義LOGF類(lèi)型

LOGE("jni發(fā)生異常"); //日志打印

相關(guān)連接

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末欠啤,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子屋灌,更是在濱河造成了極大的恐慌洁段,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,454評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件共郭,死亡現(xiàn)場(chǎng)離奇詭異祠丝,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)除嘹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)写半,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人尉咕,你說(shuō)我怎么就攤上這事叠蝇。” “怎么了年缎?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,921評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵悔捶,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我单芜,道長(zhǎng)蜕该,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,648評(píng)論 1 284
  • 正文 為了忘掉前任洲鸠,我火速辦了婚禮堂淡,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己淤齐,他們只是感情好股囊,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,770評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著更啄,像睡著了一般稚疹。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上祭务,一...
    開(kāi)封第一講書(shū)人閱讀 49,950評(píng)論 1 291
  • 那天内狗,我揣著相機(jī)與錄音,去河邊找鬼义锥。 笑死柳沙,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的拌倍。 我是一名探鬼主播赂鲤,決...
    沈念sama閱讀 39,090評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼柱恤!你這毒婦竟也來(lái)了数初?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,817評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤梗顺,失蹤者是張志新(化名)和其女友劉穎泡孩,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體寺谤,經(jīng)...
    沈念sama閱讀 44,275評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡仑鸥,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,592評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了变屁。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片眼俊。...
    茶點(diǎn)故事閱讀 38,724評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖敞贡,靈堂內(nèi)的尸體忽然破棺而出泵琳,到底是詐尸還是另有隱情,我是刑警寧澤誊役,帶...
    沈念sama閱讀 34,409評(píng)論 4 333
  • 正文 年R本政府宣布获列,位于F島的核電站,受9級(jí)特大地震影響蛔垢,放射性物質(zhì)發(fā)生泄漏击孩。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,052評(píng)論 3 316
  • 文/蒙蒙 一鹏漆、第九天 我趴在偏房一處隱蔽的房頂上張望巩梢。 院中可真熱鬧创泄,春花似錦、人聲如沸括蝠。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,815評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)忌警。三九已至搁拙,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間法绵,已是汗流浹背箕速。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,043評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留朋譬,地道東北人盐茎。 一個(gè)月前我還...
    沈念sama閱讀 46,503評(píng)論 2 361
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像徙赢,于是被迫代替她去往敵國(guó)和親字柠。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,627評(píng)論 2 350

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