JNI與底層調(diào)用1

JNI開發(fā)系列閱讀

1. JNI 簡介

1.1 什么是JNI

Java Native Interface(JNI),它允許Java 代碼和其他語言寫的代碼進(jìn)行交互茉兰。JNI 一開始是為了本地已編譯語言旦委,尤其是C 和C++而設(shè)計的,但是它并不妨礙你使用其他語言,只要調(diào)用約定受支持就可以了箍土。

1.2 為什么用JNI

  • JNI 擴(kuò)展了Java 虛擬機(jī)的能力,因為Java 不能直接和硬件交互, 不能開發(fā)驅(qū)動
  • Java 代碼效率一般要低于C 代碼,而Native code 效率高,因此在數(shù)學(xué)運算,實時渲染的游戲上以及音視頻處理上都需要用Java 調(diào)用C 語言
  • 復(fù)用C/C++代碼推捐,C 語言經(jīng)過幾十年的發(fā)展卖陵,已經(jīng)形成了強(qiáng)大的類庫(比如文件壓縮遭顶,人臉識別opencv,7zip泪蔫,ffmpeg 等)棒旗,這些類庫我們沒必要用java 語言重新實現(xiàn)一遍,通過JNI 直接調(diào)用這些類庫即可
  • 特殊的業(yè)務(wù)場景撩荣,比如電視铣揉、車載系統(tǒng)、微波爐等跟硬件直接相關(guān)的開發(fā)

2. NDK 簡介

2.1 NDK 產(chǎn)生的背景

Android 平臺從誕生起餐曹,就已經(jīng)支持C逛拱、C++開發(fā)。眾所周知台猴,Android 的SDK 基于Java 實現(xiàn)橘券,這意味著基于Android SDK 進(jìn)行開發(fā)的第三方應(yīng)用都必須使用Java 語言。但這并不等同于“第三方應(yīng)用只能使用Java”卿吐。在Android SDK 首次發(fā)布時,Google 就宣稱其虛擬機(jī)Dalvik 支持JNI 編程方式锋华,也就是第三方應(yīng)用完全可以通過JNI 調(diào)用自己的C 動態(tài)庫嗡官,即在Android 平臺上,“Java+C”的編程方式是一直都可以實現(xiàn)的毯焕。

不過衍腥,Google 也表示磺樱,使用原生SDK 編程相比Dalvik 虛擬機(jī)也有一些劣勢,Android SDK 文檔里婆咸,找不到任何JNI 方面的幫助竹捉。即使第三方應(yīng)用開發(fā)者使用JNI 完成了自己的C 動態(tài)鏈接庫(so)開發(fā),但是so 如何和應(yīng)用程序一起打包成apk 并發(fā)布尚骄?這里面也存在技術(shù)障礙块差。比如程序更加復(fù)雜,兼容性難以保障倔丈,無法訪問Framework API憨闰,Debug 難度更大等。開發(fā)者需要自行斟酌使用需五。

于是NDK 就應(yīng)運而生了鹉动。NDK 全稱是Native Development Kit。

NDK 的發(fā)布宏邮,使“Java+C”的開發(fā)方式終于轉(zhuǎn)正泽示,成為官方支持的開發(fā)方式。NDK 將是Android 平臺支持C 開發(fā)的開端蜜氨。

2.2 為什么使用NDK

  • 代碼的保護(hù)械筛。由于apk 的java 層代碼很容易被反編譯,而C/C++庫反編譯難度較大
  • 可以方便地使用現(xiàn)存的開源庫记劝。大部分現(xiàn)存的開源庫都是用C/C++代碼編寫的
  • 提高程序的執(zhí)行效率变姨。將要求高性能的應(yīng)用邏輯使用C 開發(fā),從而提高應(yīng)用程序的執(zhí)行效率
  • 便于移植厌丑。用C/C++寫得庫可以方便在其他的嵌入式平臺上再次使用

2.3 NDK 簡介

2.3.1 NDK 是一系列工具的集合

NDK 提供了一系列的工具定欧,幫助開發(fā)者快速開發(fā)C(或C++)的動態(tài)庫,并能自動將so 和java 應(yīng)用一起打包成apk怒竿。NDK 集成了交叉編譯器砍鸠,并提供了相應(yīng)的mk 文件隔離CPU、平臺耕驰、ABI 等差異爷辱,開發(fā)人員只需要簡單修改mk 文件(指出“哪些文件需要編譯”、“編譯特性要求”等)朦肘,就可以創(chuàng)建出so饭弓。

2.3.2 NDK 提供了一份穩(wěn)定、功能有限的API 頭文件聲明

Google 明確聲明該API 是穩(wěn)定的媒抠,在后續(xù)所有版本中都穩(wěn)定支持當(dāng)前發(fā)布的API弟断。從該版本的NDK中看出,這些API 支持的功能非常有限趴生,包含有:C 標(biāo)準(zhǔn)庫(libc)阀趴、標(biāo)準(zhǔn)數(shù)學(xué)庫(libm)昏翰、壓縮庫(libz)、Log 庫(liblog)刘急。

3. NDK 的安裝

3.1 NDK 的下載

NDK 的官方下載地址

3.2 將NDK 解壓到一個不包含空格和中文的目錄下

本人將NDK 解壓在D:\software\ndkr9\android-ndk-r9b 中棚菊。

3.3 NDK 目錄結(jié)構(gòu)說明

自定義好組合控件之后,之前的activity_setting.xml 中的代碼就可以進(jìn)行簡化叔汁,具體如下所示:

jni
  • build:該目錄存放的使用NDK 的mk 腳本统求,mk 腳本指定了編譯參數(shù)
  • docs:該目錄存放的是NDK 的使用幫助文檔
  • platforms:這里面存放的是與各個Android 版本相關(guān)的平臺(x86,arm攻柠,mips)相關(guān)C 語言庫和頭文件
  • prebuilt:預(yù)編譯工作目錄
  • samples:存放的是演示程序
  • sources:存放的是NDK 工具鏈的C 語言源碼
  • tests:測試相關(guān)的文件
  • toolchains:工具鏈球订,存放了三種架構(gòu)的靜態(tài)庫等文件
  • ndk-build.cmd:Window 平臺使用NDK 的命令
  • ndk-build:Linux 平臺使用NDK 的命令

4. JNI 入門

下面通過一個簡單的JNI 案例來演示如何使用JNI 編程。

1)創(chuàng)建一個新的Android 工程《JNI 入門》瑰钮,工程的最終目錄結(jié)構(gòu)如下圖所示冒滩。

jni

2)在MainActivity.java 類中定義一個native 方法

//定義一個native 方法,意思是該方法的具體實現(xiàn)交給C 語言實現(xiàn)
public native String helloC();

3)在工程跟目錄下創(chuàng)建一個文件夾jni浪谴,該目錄名稱是約定(約定優(yōu)于配置)好的开睡,不能是其他名字。
4)在jni 目錄下創(chuàng)建hello.c 源文件苟耻,文件名可以按照見名知意的規(guī)則來創(chuàng)建篇恒。hello.c 代碼清單如下。

#include<stdio.h>//引入頭文件
 //引入jni.h jni.h 文件里面定義了jni 的規(guī)范凶杖,jni.h 在ndk 的目錄中找到胁艰,然后放到當(dāng)前工程中的jni目錄下即可
#include<jni.h>
//定義在MainActivity.java 類中的helloC 對應(yīng)的C 語言函數(shù)
jstring Java_com_itheima_jnihello_MainActivity_helloC(JNIEnv* env, jobject obj) {
char* str = "hello from C";
//調(diào)用jni.h 中定義的創(chuàng)建字符串函數(shù)
jstring string = (*(*env)).NewStringUTF(env, str);
return string;

Tips:上面的代碼雖然簡單但是關(guān)于jni.h 頭文件和方法名必須單獨說明。

  • jni.h 頭文件位于NDK 安裝目錄下/platforms/android-*/(某平臺)/usr/include 目錄中智蝠,如下圖
jni

上面的某平臺指CPU 的三種架構(gòu)如下圖腾么。我們選擇任意一架構(gòu)皆可,但是對于手機(jī)來說CPU 用arm架構(gòu)的最多杈湾,x86 次之解虱,mips 架構(gòu)最少。

jni
  • JNI 中C 源文件方法名的命名規(guī)則

這里的命名規(guī)則指用于跟java 文件中native 方法對應(yīng)的C 語言方法漆撞,而C 語言中的其他方法命名只要符合C 語言規(guī)則就行殴泰。

jstring Java_com_itheima_jnihello_MainActivity_helloC(JNIEnv* env, jobject obj)

jstring 是方法返回值類型,我們可以把jstring 看成是java 中String 跟C 語言中char*類型的一個中間轉(zhuǎn)換類型浮驳,java 跟C 語言的數(shù)據(jù)類型是不一樣的悍汛,他們之間要想互相調(diào)用就必須通過一種中介來實現(xiàn),這個中介就是在jni.h 頭文件中定義的至会。關(guān)于更多的轉(zhuǎn)換類型员凝,在本文檔的第2 章會有更詳細(xì)的說明。

方法名第一個字母必須是Java奋献,首單詞大寫健霹,然后下劃線,然后是將該方法所在的包瓶蚂、類糖埋、方法用“”連接起來,比如com.itheima.jnihello.MainActivity 類中的helloC 方法,轉(zhuǎn)變成C 語言中的方法名為Java_com_itheima_jnihello_MainActivity_helloC窃这。

方法的形參有兩個是必須的也就是不管java 中的方法是否有形參瞳别,但是C 語言中對應(yīng)的方法必須有JNIEnv* env,和jobject obj,如果java 方法中還用其他形參杭攻,那么在C 語言中嚴(yán)格按照順序排在jobject obj參數(shù)的后面即可祟敛。

上面的env 代表指向JVM 的指針,obj 是調(diào)用該方法的java 對象兆解。

5)使用NDK 工具將hello.c 編譯成hello.so 文件
為了方便直接在控制臺中使用NDK 工具的ndk-build.cmd 命令馆铁,我們首先將ndk-build.cmd 所在的目錄設(shè)置成系統(tǒng)環(huán)境變量。環(huán)境變量配置好以后锅睛,在命令行中輸入ndk-build.cmd 會有如下提示:

jni

將當(dāng)前目錄切換到hello.c 所在的工程目錄埠巨,這時候如果直接輸入ndk-build.cmd 那么會出現(xiàn)如下異常:

jni

出現(xiàn)這種錯誤時因為我們并沒有告訴ndk 我們要將那個C 語言源代碼編譯成目標(biāo)文件。為了告訴ndk要將那個C 源文件編譯成目標(biāo)文件现拒,我們需要在工程中的jni 目錄中添加Android.mk 配置文件辣垒。

6)在當(dāng)前工程的jni 目錄下添加Android.mk 配置文件,該配置文件可以從ndk 安裝目錄的實例代碼中拷貝印蔬,然后修改勋桶。

Android.mk 文件清單如下,我們只需要修改LOCAL_MODULE 和LOCAL_SRC_FILES 兩個參數(shù)即可侥猬。LOCAL_MODULE 參數(shù)是指定編譯后的目標(biāo)文件的名稱例驹,其實編譯好的目標(biāo)文件名為libhello.so,LOCAL_SRC_FILES 指定了要編譯的源文件陵究。

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := hello
LOCAL_SRC_FILES := hello.c
include $(BUILD_SHARED_LIBRARY)

7)在cmd 中眠饮,將當(dāng)前目錄切換到hello.c 所在目錄,然后重新執(zhí)行ndk-build.cmd 命令铜邮,這次成功編譯仪召,cmd 顯示效果如下圖所示。

jni

查看項目目錄結(jié)構(gòu)松蒜,發(fā)現(xiàn)在libs 目錄中多了兩個文件夾armeabi 和x86扔茅,這兩個文件夾下分別包含了一個libhello.so 動態(tài)鏈接庫。這也代表著當(dāng)前工程中的動態(tài)庫支持arm 架構(gòu)和x86 架構(gòu)的cpu秸苗。

jni

Tips:可能你的并沒有同時生成這兩個文件召娜,是因為我的工程中引入了Application.mk 文件,因此你需要引入該文件惊楼。

Application.mk 文件清單:

# Build both ARMv5TE and ARMv7-A machine code.
APP_ABI := armeabi x86

8)該清單其實只有一行內(nèi)容玖瘸,第一行是注釋秸讹。APP_ABI 參數(shù)指定要生成的目標(biāo)文件支持的平臺都有哪些,默認(rèn)是armeabi 如果想支持多個平臺只需要空一格然后寫出其他平臺名字即可雅倒。在MainActivity.java 中調(diào)用C 語言

public class MainActivity extends Activity {
    //加載libhello.so 動態(tài)庫璃诀,但是我們加載的時候必須去掉lib 和后綴
    static{
        System.loadLibrary("hello");
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
    //定義一個native 方法,意思是該方法的具體實現(xiàn)交給C 語言實現(xiàn)
    public native String helloC();
    //點擊按鈕調(diào)用C 語言方法
    public void click(View view){
        Toast.makeText(this, helloC(), 1).show();
    }
}

運行上面工程蔑匣,效果如下:

jni

Tips:如果我們編譯的arm 平臺的so 文件劣欢,但是卻部署到了x86 平臺的模擬器上,那么運行的時候會報找不到libhello.so 的異常裁良。

5. JNI 規(guī)范

5.1 JNI 數(shù)據(jù)類型和數(shù)據(jù)結(jié)構(gòu)

1)基本數(shù)據(jù)類型
JNI 基本類型和本地等效類型的對應(yīng)表格如下:

jni

2)引用類型凿将,JNI 還包含了很多對應(yīng)于不同Java 對象的引用類型,JNI 引用類型的組織層次如下圖所示:

jni

5.2 JNI 接口函數(shù)命名方式

設(shè)置向?qū)ФetUpActivity2.java 的代碼邏輯如下所示价脾,設(shè)置向?qū)Ф膱D形化界面如2-20 所示牧抵。

5.2.1 類型簽名

Java 虛擬機(jī)的類型簽名如下:

類型簽名 Java 類型
Z boolean
B byte
C char
S short
I int
J long
F float
D double
Lfully-qulitied-class; 全限定類
[type type[] 數(shù)組
(argtypes)rettype 方法類型

例如咐旧,Java 方法int feet(int n, String s,int [] arr)的類型簽名如下:

(ILJava/lang/String;[I)I

圓括號里面為參數(shù)情龄,I 表示第一個參數(shù)int 型,LJava/lang/String;表示第二個參數(shù)為全限定Java.lang.String類型肥败,[I 表示第三個參數(shù)為int 型的數(shù)組座硕,圓括號后面為返回值類型弛作,I 表示返回值為int 型

5.2.2 一般函數(shù)的JNI 接口函數(shù)命名方式

一般JNI 接口函數(shù)命名如下:Java_包名類名方法名。

例如:某工程下com/itheima 包下MainActivity 類的int getIntFromC()方法的C 語言實現(xiàn)函數(shù)命名如下:

jint Java_com_itheima_MainActivity_getIntFromC(JNIEnv* env,jobject obj)

其中华匾,包名所包含的“/”應(yīng)全部以下劃線替代映琳,其本地實現(xiàn)的參數(shù)和返回值也應(yīng)轉(zhuǎn)換為JNI 類型。

5.2.3 重載函數(shù)的JNI 接口函數(shù)命名方式

重載函數(shù)的JNI 實現(xiàn)在一般函數(shù)的JNI 實現(xiàn)之外蜘拉,還應(yīng)添加上類型簽名以作為同名函數(shù)之間的區(qū)別萨西,其接口函數(shù)命名如下:Java_包名類名方法名_參數(shù)簽名。

例如:某工程下com/itheima 包下MainActivity 類的int getIntFromC(int n, String s,int [] arr)方法的C 語言實現(xiàn)函數(shù)命名如下:

jint Java_com_itheima_MainActivity_getIntFromC_ILJava_lang_String23I
(JNIEnv* env, jobject obj, jint n, jstring s, jintarray arr)

JNI 在函數(shù)命名時采用名字?jǐn)_亂方案旭旭,以保證所有的Unicode 字符都能轉(zhuǎn)換為有效的C 函數(shù)名谎脯,所有的“/”,無論是包名中的還是全限定類名中的,均使用“_”代替持寄,用_0,?,_9 來代替轉(zhuǎn)義字符源梭,如下:

轉(zhuǎn)義字符序列 表示
_0XXXX Unicode 字符XXXX
_1 字符“_”
_2 簽名中的字符“;”
_3 簽名中的字符“[”

5.3 JNI 函數(shù)與API

設(shè)置向?qū)齋etUpActivity3.java 的代碼邏輯如下所示稍味,設(shè)置向?qū)膱D形化界面如2-21 所示废麻。
在本文檔中我們所主要需要關(guān)心的是C/C++數(shù)據(jù)類型與JNI 本地類型之間的轉(zhuǎn)化過程,這個過程某些數(shù)據(jù)的轉(zhuǎn)換需要使用JNIEnv 對象的一系列方法來完成模庐。

5.3.1 jstring 轉(zhuǎn)換為C 風(fēng)格字符串

char* test = (char)(env)->GetStringUTFChars(env,jstring,NULL);

使用完畢后烛愧,應(yīng)調(diào)用:

(*env)->ReleaseStringUTFChars(env, jstring, test);

釋放資源。

5.3.2 C 風(fēng)格字符串轉(zhuǎn)換為jstring

char charStr[50];
jstring jstr;
jstr = env -> NewStringUTF(charStr);

5.3.3 C 語言中獲取的一段char的buffer 傳遞給Java*

在jni 中new 一個byte 數(shù)組,然后使用
(*env)->SetByteArrayRegion(env, bytearray, 0, len, buffer) 操作將buffer 拷貝到數(shù)組中怜姿。這種方式主要是針對buffer 中存在“\0”的情況慎冤,如果以C 風(fēng)格字符串的方式讀入,就會損失“\0”之后的字符沧卢。

5.3.4 數(shù)組操作

JNI 函數(shù) 功能
GetArrayLength 返回數(shù)組中的元素數(shù)
NewObjectArray 創(chuàng)建一個指定長度的原始數(shù)據(jù)類型數(shù)組
GetObjectArrayElement 返回Object 數(shù)組的元素
SetObjectArrayElement 設(shè)置Object 數(shù)組的元素
GetObjectArrayRegion 將原始數(shù)據(jù)類型數(shù)組中的內(nèi)容拷貝到預(yù)先分配好的內(nèi)存緩存中
SetObjectArrayRegion 設(shè)置緩存中數(shù)組的值
ReleaseObjectArrayRegion 釋放GetObjectArrayRegion 分配的內(nèi)存

Tips:對int粪薛,char 等基本數(shù)據(jù)類型的數(shù)組操作,將相關(guān)Object 名稱替換為對應(yīng)基本數(shù)據(jù)類型名稱即為相關(guān)函數(shù)搏恤。

數(shù)組操作的方法選擇基于使用者的需求而定,如果使用者需要在內(nèi)存中拷貝數(shù)組并對其進(jìn)行操作那么一般使用GetObjectArrayRegion 和SetObjectArrayRegion 函數(shù)湃交,否則一般使用SetObjectArrayElement 和GetObjectArrayElement 函數(shù)熟空。

6. 案例-銀行登錄系統(tǒng)

需求:假設(shè)銀行的登陸模塊是用C 語言來編寫的,但是我們的Android 應(yīng)用想登陸銀行系統(tǒng)搞莺,那么就需要通過JNI 來實現(xiàn)了息罗。

創(chuàng)建一個新Android 工程《建行客戶端》,工程目錄結(jié)構(gòu)如下圖才沧。

jni

在工程中創(chuàng)建jni 文件夾迈喉,然后將jni.h、Android.mk温圆、Application.mk 從JNI 入門工程拷貝進(jìn)去挨摸。在jni 目錄下創(chuàng)建login.c 文件,在該文件中實現(xiàn)登錄業(yè)務(wù)邏輯岁歉。代碼清單如下得运。

#include<stdio.h>
//系統(tǒng)在查找投文件的時候""中的文件會去本地搜索,<>中的文件會去系統(tǒng)目錄中搜索锅移,因為jni.h 在當(dāng)前目錄中
所以用""將jni.h 引起來熔掺,可以加快搜索速度
#include"jni.h"
int login(int card,int pwd){
    //真實的業(yè)務(wù)邏輯要復(fù)雜的多,這里只簡單的返回銀行卡號和密碼號
    return card+pwd;
}
jint Java_com_itheima_ccb_MainActivity_login(JNIEnv* env,jobject obj,jint card,jint
        pwd){
    return login(card,pwd);
}

是用ndk 工具非剃,將login.c 編譯成動態(tài)庫文件置逻。編譯前修改Android.mk 文件的LOCAL_SRC_FILES := login.c

jni

編寫在MainActivity.java 類

public class MainActivity extends Activity {
    static{
        System.loadLibrary("login-jni");
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
    public native int login(int card,int pwd);
    public void login(View view){
        EditText et_card = (EditText) findViewById(R.id.et_card);
        EditText et_pwd = (EditText) findViewById(R.id.et_pwd);
        int card = Integer.valueOf(et_card.getText().toString());
        int pwd = Integer.valueOf(et_pwd.getText().toString());
        int result = login(card, pwd);
        Toast.makeText(this, ""+result, 1).show();
    }
}

布局文件比較簡單,這里就不再給出备绽。運行上面的代碼券坞,運行結(jié)果如下:

jni

7. CDT 插件的安裝

7.1 CDT 簡介

CDT 項目致力于為Eclipse 平臺提供功能完全的C/C++ 集成開發(fā)環(huán)境(Integrated DevelopmentEnvironment,IDE)疯坤。CDT 是完全用Java 實現(xiàn)的開放源碼項目(根據(jù)Common Public License 特許的)报慕,它作為Eclipse SDK 平臺的一組插件。這些插件將C/C++ 透視圖添加到Eclipse 工作臺(Workbench)中压怠,現(xiàn)在后者可以用許多視圖和向?qū)б约案呒壘庉嫼驼{(diào)試支持來支持C/C++ 開發(fā)眠冈。

7.2 CDT 的下載

CDT 插件可以通過eclipse 的在線安裝,但是受限于跨國家網(wǎng)絡(luò)訪問,一般不是很好用蜗顽。因此這里我主要給大家說的是如何離線安裝布卡。

下載CDT 離線安裝包。針對不同版本eclipse 的cdt 安裝包如下雇盖,大家可以從我的百度網(wǎng)盤上直接下載忿等。考慮到我們大部分都用的最新的ADT 因此建議選擇8.5.0 版本的CDT崔挖。

選擇eclipse 的Help->Install New Software...贸街,彈出如下對話框

jni

點擊Add 按鈕,在彈出的對話框中輸入Name狸相。在Location 欄如果輸入一個http 地址是讓eclipse自動從網(wǎng)絡(luò)上下載安裝薛匪,這里我們點擊Archive 按鈕找到我們事先下載好的離線安裝包。然后點擊OK脓鹃。

jni

將CDT 所有的插件勾選上逸尖,同時將最下面的聯(lián)網(wǎng)檢查更新去掉勾選,然后點擊Next瘸右,直到Finish娇跟。

jni

安裝好以后在File->New->Other 中會有C/C++選項,如下圖太颤。

jni

在Open Perspective 中也多了C/C++視圖可選項苞俘,如下圖。

jni

安裝好以后栋齿,我們就可以在eclipse 中開發(fā)我們的C/C++工程了苗胀。不過對我們Android 開發(fā)人員來說用到的機(jī)會不是很多。就算是開發(fā)C/C++工程瓦堵,大多數(shù)程序員也不會選擇在eclipse 平臺上進(jìn)行開發(fā)基协。Eclipse更多的是專注于Java 語言項目的開發(fā),比如JavaEE菇用、Android澜驮。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市惋鸥,隨后出現(xiàn)的幾起案子杂穷,更是在濱河造成了極大的恐慌,老刑警劉巖卦绣,帶你破解...
    沈念sama閱讀 218,036評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件耐量,死亡現(xiàn)場離奇詭異,居然都是意外死亡滤港,警方通過查閱死者的電腦和手機(jī)廊蜒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,046評論 3 395
  • 文/潘曉璐 我一進(jìn)店門趴拧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人山叮,你說我怎么就攤上這事著榴。” “怎么了屁倔?”我有些...
    開封第一講書人閱讀 164,411評論 0 354
  • 文/不壞的土叔 我叫張陵脑又,是天一觀的道長。 經(jīng)常有香客問我锐借,道長问麸,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,622評論 1 293
  • 正文 為了忘掉前任钞翔,我火速辦了婚禮口叙,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘嗅战。我一直安慰自己,他們只是感情好俺亮,可當(dāng)我...
    茶點故事閱讀 67,661評論 6 392
  • 文/花漫 我一把揭開白布驮捍。 她就那樣靜靜地躺著,像睡著了一般脚曾。 火紅的嫁衣襯著肌膚如雪东且。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,521評論 1 304
  • 那天本讥,我揣著相機(jī)與錄音珊泳,去河邊找鬼。 笑死拷沸,一個胖子當(dāng)著我的面吹牛色查,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播撞芍,決...
    沈念sama閱讀 40,288評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼秧了,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了序无?” 一聲冷哼從身側(cè)響起验毡,我...
    開封第一講書人閱讀 39,200評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎帝嗡,沒想到半個月后晶通,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,644評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡哟玷,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,837評論 3 336
  • 正文 我和宋清朗相戀三年狮辽,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,953評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡隘竭,死狀恐怖塘秦,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情动看,我是刑警寧澤尊剔,帶...
    沈念sama閱讀 35,673評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站菱皆,受9級特大地震影響须误,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜仇轻,卻給世界環(huán)境...
    茶點故事閱讀 41,281評論 3 329
  • 文/蒙蒙 一京痢、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧篷店,春花似錦祭椰、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,889評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至蹄殃,卻和暖如春携茂,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背诅岩。 一陣腳步聲響...
    開封第一講書人閱讀 33,011評論 1 269
  • 我被黑心中介騙來泰國打工讳苦, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人吩谦。 一個月前我還...
    沈念sama閱讀 48,119評論 3 370
  • 正文 我出身青樓鸳谜,卻偏偏與公主長得像,于是被迫代替她去往敵國和親式廷。 傳聞我的和親對象是個殘疾皇子卿堂,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,901評論 2 355

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