記錄一次Android NDK與JNI使用基礎(chǔ)

準(zhǔn)備工作

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


    developEnvironment.png
  • 2.AndroidStudio版本


    ASVersion.png
  • 3.在SDKManager中下載NDK工具


    ndk.png

靜態(tài)注冊(cè)Native函數(shù)

  1. 創(chuàng)建Android項(xiàng)目

2.創(chuàng)建native方法的工具類(lèi)JniTest距糖,代碼如下

package com.xc.jnitest.exercise;

public class JniTest {

    public  native String get();

    public native void set(String str);
}

3.修改MainActivity以及l(fā)ayout文件
activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/textview1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        android:textSize="20sp"
        android:textStyle="bold"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>

MainActivity

public class MainActivity extends AppCompatActivity {
    TextView mText;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mText = findViewById(R.id.textview1);

        JniTest jniTest = new JniTest();

        mText.setText(jniTest.get());
    }
}

4.生成.class文件

  • 可以在Terminal中使用 “javac com/xc/jnitest/exercise/JniTest.java”命令生成.class文件
  • 在AndroidStudio中可以直接Build-> Make Project生成,如下圖
    make_project.png

使用Terminal生成的.class文件在同級(jí)目錄下岭皂,使用Build->Make project生成的在build目錄下浊猾,如下圖

使用javac生成.png

使用Build生成.png

5.在Terminalcd到相應(yīng)目錄下铅碍,執(zhí)行javah -jni命令生成.h頭文件

  • java目錄下執(zhí)行javah -jni com.xc.jnitest.exercise.JniTest命令
  • build/intermediates/javac/debug/compileDebugJavaWithJavac/classes目錄下執(zhí)行 javah -jni com.xc.jnitest.exercise.JniTest命令

生成對(duì)應(yīng)的.h頭文件如下圖

頭文件.png

.h文件內(nèi)容如下:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_xc_jnitest_exercise_JniTest */

#ifndef _Included_com_xc_jnitest_exercise_JniTest
#define _Included_com_xc_jnitest_exercise_JniTest
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_xc_jnitest_exercise_JniTest
 * Method:    get
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_xc_jnitest_exercise_JniTest_get
  (JNIEnv *, jobject);

/*
 * Class:     com_xc_jnitest_exercise_JniTest
 * Method:    set
 * Signature: (Ljava/lang/String;)V
 */
JNIEXPORT void JNICALL Java_com_xc_jnitest_exercise_JniTest_set
  (JNIEnv *, jobject, jstring);

#ifdef __cplusplus
}
#endif
#endif

JNIEnv*:表示一個(gè)紙箱JNI環(huán)境的指針,可以通過(guò)它來(lái)訪問(wèn)JNI提供的接口方法捌治;
jobject:表示Java對(duì)象中的this
JNIEXPORTJNICALL:他們是JNI中所定義的宏岗钩,可以在jni.h這個(gè)頭文件中查找到。
jstring: 是返回值類(lèi)型
Java_com_xc_jnitest_exercise 是包名
JniTest: 是類(lèi)名
get: 是方法名

  • 上邊只是讓你更理解創(chuàng)建步驟具滴,如果嫌麻煩凹嘲,可以直接使用
    javah -d ../jni com.xc.jnitest.exercise.JniTest
    創(chuàng)建.h頭文件,當(dāng)然這里就可以省略生成.class文件的步驟了构韵,其中
    -d ../jni 是指定要生成的頭文件到那個(gè)目錄下

6.在main目錄下創(chuàng)建一個(gè)jni文件夾周蹭,將剛才生成的.h文件剪切過(guò)來(lái)。在jni目錄下新建一個(gè)c++文件疲恢。命名為jni-test.cpp凶朗。(注:c文件的后綴為.c,c++文件后綴為.cpp
如下圖:

目錄.png

7.編寫(xiě)jni-test.cpp文件

#include <jni.h>
#include <stdio.h>

#include "com_xc_jnitest_exercise_JniTest.h"

JNIEXPORT jstring JNICALL Java_com_xc_jnitest_exercise_JniTest_get
  (JNIEnv *env, jobject obj ){
    return env->NewStringUTF("Hello from JNI !");
  }

/*
 * Class:     com_xc_jnitest_exercise_JniTest
 * Method:    set
 * Signature: (Ljava/lang/String;)V
 */
JNIEXPORT void JNICALL Java_com_xc_jnitest_exercise_JniTest_set
  (JNIEnv *env, jobject obj, jstring string){
    char* str = (char*) env->GetStringUTFChars(string,NULL);
    env->ReleaseStringUTFChars(string,str);
  }
  • #inclued "com_xc_jnitest_exercise_JniTest.h" 添加頭文件
  • 實(shí)現(xiàn)兩個(gè)方法方法
  1. jni目錄下添加Android.mk文件
LOCAL_PATH := $(call my-dir)  //

include $(CLEAR_VARS)

LOCAL_MODULE    := jni-test

LOCAL_SRC_FILES := jni-test.cpp

include $(BUILD_SHARED_LIBRARY)
  • LOCAL_PATH := $(call my-dir):每個(gè)Android.mk文件必須以定義開(kāi)始。它用于在開(kāi)發(fā)tree中查找源文件显拳。宏my-dir則由Build System 提供棚愤。返回包含Android.mk目錄路徑。
  • include $(CLEAR_VARS)CLEAR_VARS變量由Build System提供。并指向一個(gè)指定的GNU Makefile宛畦,由它負(fù)責(zé)清理很多LOCAL_xxx瘸洛。例如LOCAL_MODULE,LOCAL_SRC_FILES次和,LOCAL_STATIC_LIBRARIES等等反肋。但不是清理LOCAL_PATH。這個(gè)清理是必須的踏施,因?yàn)樗械木幾g控制文件由同一個(gè)GNU Make解析和執(zhí)行石蔗,其變量是全局的。所以清理后才能便面相互影響畅形。
  • LOCAL_MODULE := jni-test:LOCAL_MODULE模塊必須定義养距,以表示Android.mk中的每一個(gè)模塊。名字必須唯一且不包含空格日熬。Build System 會(huì)自動(dòng)添加適當(dāng)?shù)那熬Y和后綴棍厌。例如,demo竖席,要生成動(dòng)態(tài)庫(kù)浴骂,則生成libdemo.so蹦渣。但請(qǐng)注意:如果模塊名字被定義為libabd钠惩,則生成libabc.so秕狰。不再添加前綴帘皿。
  • LOCAL_SRC_FILES := ndkdemotest.c:這行代碼表示將要打包的C/C++源碼东跪。不必列出頭文件,build System 會(huì)自動(dòng)幫我們找出依賴(lài)文件鹰溜。缺省的C++ 源碼的擴(kuò)展名為.cpp虽填。
  • include $(BUILD_SHARED_LIBRARY)BUILD_SHARED_LIBRARY是Build System提供的一個(gè)變量,指向一個(gè)GUN Makefile Script曹动。它負(fù)責(zé)收集自從上次調(diào)用include $(CLEAR_VARS)后的所有LOCAL_xxxxinx斋日。并決定編譯什么類(lèi)型
    • BUILD_STATIC_LIBRARY:編譯為靜態(tài)庫(kù)
    • BUILD_SHARED_LIBRARY:編譯為動(dòng)態(tài)庫(kù)
    • BUILD_EXECUTABLE:編譯為Native C 可執(zhí)行程序
    • BUILD_PREBUILT:該模塊已經(jīng)預(yù)先編譯

9.配置app module的build.gradle文件

android {
    compileSdkVersion 28
    defaultConfig {
        applicationId "com.xc.jnitest"
        minSdkVersion 21
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

        ndk{
            moduleName "jni-test"
            abiFilters "armeabi-v7a", "x86"
        }
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    
    externalNativeBuild {
        ndkBuild {
            path 'src/main/jni/Android.mk'
        }
    }
    sourceSets.main {
        jni.srcDirs = []
        jniLibs.srcDirs = ['src/main/jniLibs']
    }
}

配置后就可以生成.so文件了(PS:項(xiàng)目Build之后)

  1. 加載so庫(kù)
package com.xc.jnitest.exercise;

public class JniTest {

    static{
        System.loadLibrary("jni-test");
    }

    public static native String get();

    public static native void set(String str);
}

11.運(yùn)行項(xiàng)目


running.png

動(dòng)態(tài)注冊(cè)Native函數(shù)

  • 不必忍受冗長(zhǎng)的函數(shù)名,自由命名函數(shù)名墓陈,在JNI_OnLoad方法里進(jìn)行注冊(cè)恶守。

1.修改生成后的.h頭文件名為jni-test.h,如下

修改頭文件名.png

修改.h文件方法名

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_xc_jnitest_exercise_JniTest */

#ifndef _Included_com_xc_jnitest_exercise_JniTest
#define _Included_com_xc_jnitest_exercise_JniTest
#ifdef __cplusplus
extern "C" {
#endif

jstring get (JNIEnv *, jobject);

void set (JNIEnv *, jobject, jstring);

#ifdef __cplusplus
}
#endif
#endif

2.修改.cpp文件如下

#include <jni.h>
#include <stdio.h>

#include "jni-test.h"

jstring get(JNIEnv *env, jobject obj) {
    return env->NewStringUTF("Hello from JNI !");
}


void set(JNIEnv *env, jobject obj, jstring string) {
    char *str = (char *) env->GetStringUTFChars(string, NULL);
    env->ReleaseStringUTFChars(string, str);
}

3.添加參數(shù)映射函數(shù)

//參數(shù)映射表
static JNINativeMethod getMethods[] = {
        {"get", "()Ljava/lang/String;", (void *) get},
        {"set", "(Ljava/lang/String;)", (void *) set},
};

它的返回值是JNINativeMethod類(lèi)型:
JNI允許我們提供一個(gè)函數(shù)映射表,注冊(cè)給Java虛擬機(jī)贡必,這樣JVM就可以用函數(shù)映射表來(lái)調(diào)用相應(yīng)的函數(shù)兔港。這樣就可以不必通過(guò)函數(shù)名來(lái)查找需要調(diào)用的函數(shù)了。Java與JNI通過(guò)JNINativeMethod的結(jié)構(gòu)來(lái)建立聯(lián)系仔拟,它被定義在jni.h中衫樊,其結(jié)構(gòu)內(nèi)容如下:

typedef struct { 
    const char* name; 
    const char* signature; 
    void* fnPtr; 
} JNINativeMethod; 
  • 第一個(gè)變量name,代表的是Java中的函數(shù)名
  • 第二個(gè)變量signature,代表的是Java中的參數(shù)和返回值
  • 第三個(gè)變量fnPtr科侈,代表的是的指向C函數(shù)的函數(shù)指針

第二個(gè)變量signature定義如下:
(參數(shù)1類(lèi)型標(biāo)示载佳;參數(shù)2類(lèi)型標(biāo)示;參數(shù)3類(lèi)型標(biāo)示...)返回值類(lèi)型標(biāo)示
當(dāng)參數(shù)為引用類(lèi)型的時(shí)候臀栈,參數(shù)類(lèi)型的標(biāo)示的根式為"L包名"蔫慧,其中包名的.(點(diǎn))要換成"/",比如String就是Ljava/lang/String挂脑,MenuLandroid/view/Menu,如果返回值是void,對(duì)應(yīng)的簽名就是V藕漱。
如果是基本類(lèi)類(lèi)型,其簽名如下(除了boolean和long崭闲,其他都是首字母大寫(xiě)):

類(lèi)型標(biāo)示 Java類(lèi)型
Z boolean
B byte
C char
S short
I int
J long
F float
D double

數(shù)組類(lèi)型:

類(lèi)型標(biāo)示 Java類(lèi)型
[簽名 數(shù)組
[i int[]
[Ljava/lang/Object String[]
  • 可以使用JDK的javap -s com.xc.jnitest.exercise.JniTest直接查看他的signature肋联,這里的com.xc.jnitest.exercise.JniTestclass文件路徑

4.注冊(cè)native方法

//native類(lèi)路徑
static const char *className = "com/com/xc/jnitest/exercise/JniTest";

//注冊(cè)native方法
static int registerNatives(JNIEnv *engv) {
    jclass clazz;
    clazz = engv->FindClass(className);   //找到native類(lèi)
    if (clazz == NULL) {
        return JNI_FALSE;
    }
    //int len = sizeof(methods) / sizeof(methods[0]);
    if (engv->RegisterNatives(clazz, getMethods,
                              sizeof(getMethods) / sizeof(getMethods[0])) <
        0) {
        return JNI_FALSE;
    }
    return JNI_TRUE;
}

JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
    JNIEnv *env = NULL;
    jint result = -1;
    if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {
        return result;
    }
    assert(env != NULL);
    //為了方便管理我們將不同java類(lèi)中的native方法分別注冊(cè)
    if (registerNatives(env) < 0) {  //注冊(cè)native方法
        return result;
    }
    //如果還有別的native類(lèi),可繼續(xù)在此進(jìn)行注冊(cè)
    return JNI_VERSION_1_6;
}

jni-test.cpp完整代碼如下

#include <jni.h>
#include <stdio.h>
#include <assert.h>

#include "jni-test.h"

jstring get(JNIEnv *env, jobject obj) {
    return env->NewStringUTF("Hello from JNI !");
}


void set(JNIEnv *env, jobject obj, jstring string) {
    char *str = (char *) env->GetStringUTFChars(string, NULL);
    env->ReleaseStringUTFChars(string, str);
}

//參數(shù)映射表
static JNINativeMethod getMethods[] = {
        {"get", "()Ljava/lang/String;", (void *) get},
        {"set", "(Ljava/lang/String;)V", (void *) set},
};

//native類(lèi)路徑
static const char *className = "com/xc/jnitest/exercise/JniTest";

//注冊(cè)native方法
static int registerNatives(JNIEnv *engv) {
    jclass clazz;
    clazz = engv->FindClass(className);   //找到native類(lèi)
    if (clazz == NULL) {
        return JNI_FALSE;
    }
    //int len = sizeof(methods) / sizeof(methods[0]);
    if (engv->RegisterNatives(clazz, getMethods,
                              sizeof(getMethods) / sizeof(getMethods[0])) <
        0) {
        return JNI_FALSE;
    }
    return JNI_TRUE;
}

JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved) {
    JNIEnv *env = NULL;
    jint result = -1;
    if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {
        return result;
    }
    assert(env != NULL);
    //為了方便管理我們將不同java類(lèi)中的native方法分別注冊(cè)
    if (registerNatives(env) < 0) {  //注冊(cè)native方法
        return result;
    }
    //如果還有別的native類(lèi)刁俭,可繼續(xù)在此進(jìn)行注冊(cè)

    return JNI_VERSION_1_6;
}

5.齊活橄仍,運(yùn)行!

native代碼反調(diào)用Java層代碼

1.獲取class對(duì)象:

  • jclass FindClass(const char* clsName)
    通過(guò)類(lèi)的名稱(chēng)(類(lèi)的全名牍戚,這時(shí)候包名不是用'"."點(diǎn)號(hào)而是用"/"來(lái)區(qū)分的)來(lái)獲取jclass侮繁。比如:
jclass jcl_string=env->FindClass("java/lang/String");
  • jclass GetObjectClass(jobject obj)
    通過(guò)對(duì)象實(shí)例來(lái)獲取jclass,相當(dāng)于Java中的getClass()函數(shù)
  • jclass getSuperClass(jclass obj)
    通過(guò)jclass可以獲取其父類(lèi)的jclass對(duì)象

2.獲取屬性方法
在Native本地代碼中訪問(wèn)Java層的代碼如孝,一個(gè)常用的常見(jiàn)的場(chǎng)景就是獲取Java類(lèi)的屬性和方法宪哩。所以為了在C/C++獲取Java層的屬性和方法,JNI在jni.h頭文件中定義了jfieldID和jmethodID這兩種類(lèi)型來(lái)分別代表Java端的屬性和方法第晰。在訪問(wèn)或者設(shè)置Java某個(gè)屬性的時(shí)候锁孟,首先就要現(xiàn)在本地代碼中取得代表該Java類(lèi)的屬性的jfieldID,然后才能在本地代碼中進(jìn)行Java屬性的操作茁瘦,同樣品抽,在需要調(diào)用Java類(lèi)的某個(gè)方法時(shí),也是需要取得代表該方法的jmethodID才能進(jìn)行Java方法操作甜熔。

  • GetFieldID/GetMethodID
    獲取某個(gè)屬性/某個(gè)方法
  • GetStaticFieldID/GetStaticMethodID
    獲取某個(gè)靜態(tài)屬性/靜態(tài)方法
    方法實(shí)現(xiàn)如下:
jfieldID GetFieldID(JNIEnv *env, jclass clazz, const char *name, const char *sig);
jmethodID GetMethodID(JNIEnv *env, jclass clazz, const char *name, const char *sig);
jfieldID GetStaticFieldID(JNIEnv *env, jclass clazz, const char *name, const char *sig);
jmethodID GetStaticMethodID(JNIEnv *env, jclass clazz,const char *name, const char *sig);

3.構(gòu)造對(duì)象
常用的JNI中創(chuàng)建對(duì)象的方法如下:

jobject NewObject(jclass clazz, jmethodID methodID, ...)

比如有我們知道Java類(lèi)中可能有多個(gè)構(gòu)造函數(shù)圆恤,當(dāng)我們要指定調(diào)用某個(gè)構(gòu)造函數(shù)的時(shí)候,會(huì)調(diào)用下面這個(gè)方法

jmethodID mid = (*env)->GetMethodID(env, cls, "<init>", "()V");
obj = (*env)->NewObject(env, cls, mid);

即把指定的構(gòu)造函數(shù)傳入進(jìn)去即可腔稀。
現(xiàn)在我們來(lái)看下他上面的兩個(gè)主要參數(shù)

  • clazz:是需要?jiǎng)?chuàng)建的Java對(duì)象的Class對(duì)象
  • methodID:是傳遞一個(gè)方法ID

簡(jiǎn)化代碼如下:

jobject NewObjectA(JNIEnv *env, jclass clazz, 
jmethodID methodID, jvalue *args);

這里多了一個(gè)參數(shù)盆昙,即jvalue *args,這里是args代表的是對(duì)應(yīng)構(gòu)造函數(shù)的所有參數(shù)的焊虏,我們可以應(yīng)將傳遞給構(gòu)造函數(shù)的所有參數(shù)放在jvalues類(lèi)型的數(shù)組args中弱左,該數(shù)組緊跟著放在methodID參數(shù)的后面。NewObject()收到數(shù)組中的這些參數(shù)后炕淮,將把它們傳給編程任索要調(diào)用的Java方法拆火。

如果參數(shù)不是數(shù)組怎么處理:

jobject NewObjectV(JNIEnv *env, jclass clazz, 
jmethodID methodID, va_list args);

這個(gè)方法和上面不同在于,這里將構(gòu)造函數(shù)的所有參數(shù)放到在va_list類(lèi)型的參數(shù)args中,該參數(shù)緊跟著放在methodID參數(shù)的后面们镜。

總結(jié)

1.靜態(tài)注冊(cè)native函數(shù)

  • 第1步:在Java中先聲明native方法
  • 第2步:編譯Java源文件javac得到.class文件
  • 第3步:通過(guò)javah -jni命令導(dǎo)出JNI的.h頭文件币叹,添加.c.cpp文件,實(shí)現(xiàn)函數(shù)方法
  • 第4步:使用Java需要交互的本地代碼模狭,實(shí)現(xiàn)在Java中聲明的Native方法
  • 第5步:添加Android.mk文件颈抚,修改build.gradle文件,將本地代碼編譯成動(dòng)態(tài)庫(kù)
  • 第6步:通過(guò)Java命令執(zhí)行Java程序嚼鹉,最終實(shí)現(xiàn)Java調(diào)用本地代碼贩汉。

2.動(dòng)態(tài)注冊(cè)native函數(shù), 在靜態(tài)注冊(cè)的基礎(chǔ)上:

  • 修改簡(jiǎn)化.h文件的方法名與方法
  • 修改簡(jiǎn)化.cpp文件的方法名
  • 添加映射函數(shù)與注冊(cè)函數(shù)

項(xiàng)目git地址:https://github.com/x-fp/JniTest

參考文章:http://www.reibang.com/p/87ce6f565d37
這篇文章寫(xiě)得非常詳細(xì)锚赤,感謝作者的幫助

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末匹舞,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子线脚,更是在濱河造成了極大的恐慌赐稽,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,997評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件浑侥,死亡現(xiàn)場(chǎng)離奇詭異姊舵,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)寓落,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,603評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)括丁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人伶选,你說(shuō)我怎么就攤上這事躏将。” “怎么了考蕾?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,359評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)会宪。 經(jīng)常有香客問(wèn)我肖卧,道長(zhǎng),這世上最難降的妖魔是什么掸鹅? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,309評(píng)論 1 292
  • 正文 為了忘掉前任塞帐,我火速辦了婚禮,結(jié)果婚禮上巍沙,老公的妹妹穿的比我還像新娘葵姥。我一直安慰自己,他們只是感情好句携,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,346評(píng)論 6 390
  • 文/花漫 我一把揭開(kāi)白布榔幸。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪削咆。 梳的紋絲不亂的頭發(fā)上牍疏,一...
    開(kāi)封第一講書(shū)人閱讀 51,258評(píng)論 1 300
  • 那天,我揣著相機(jī)與錄音拨齐,去河邊找鬼鳞陨。 笑死,一個(gè)胖子當(dāng)著我的面吹牛瞻惋,可吹牛的內(nèi)容都是我干的厦滤。 我是一名探鬼主播,決...
    沈念sama閱讀 40,122評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼歼狼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼掏导!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起蹂匹,我...
    開(kāi)封第一講書(shū)人閱讀 38,970評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤碘菜,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后限寞,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體忍啸,經(jīng)...
    沈念sama閱讀 45,403評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,596評(píng)論 3 334
  • 正文 我和宋清朗相戀三年履植,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了计雌。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,769評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡玫霎,死狀恐怖凿滤,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情庶近,我是刑警寧澤翁脆,帶...
    沈念sama閱讀 35,464評(píng)論 5 344
  • 正文 年R本政府宣布,位于F島的核電站鼻种,受9級(jí)特大地震影響反番,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜叉钥,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,075評(píng)論 3 327
  • 文/蒙蒙 一罢缸、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧投队,春花似錦枫疆、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,705評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)寝贡。三九已至,卻和暖如春钞螟,著一層夾襖步出監(jiān)牢的瞬間兔甘,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,848評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工鳞滨, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留洞焙,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,831評(píng)論 2 370
  • 正文 我出身青樓拯啦,卻偏偏與公主長(zhǎng)得像澡匪,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子褒链,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,678評(píng)論 2 354

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