JNI

What is JNI

JNI是Java Native Interface的縮寫,主要是提供了一系列API,讓你能在其它語言中寫Java。

What JNI can bring us

JNI最大的好處就是,額,Java你懂的,跑在JVM里面,雖然有著一處編譯,到處運行的優勢(,方便啊),但是它的效率。。。至少相對于c和C艸來說,比較低下,而且,正是由于這個能一處編譯,到處運行的原因,Java極容易被反編譯。Java中一般用的加密方式就是混淆了,然而其實并沒有太大的作用。你還是開源吧。。。因為不開源也會被反編譯的。。。
PS:并沒有貶低Java的意思,個人還是挺喜歡用Java的

然后,相反的,JNI由于是用C或者C艸寫,效率很高,可以用來處理一些底層的東西,比如解碼或者TCP/IP有關的。編譯過后跟C(艸)編譯的結果是一樣的,在Android里面是.so文件。然后,因為是C(艸),所以需要針對不同的平臺,不同的處理器進行編譯。所以,使用JNI,你需要在編譯的時候生成許多個平臺的版本,否則,Java跨平臺這個優點相當于直接被廢了。還有就是JNI的調試會非常蛋疼。

How to use JNI

Hello World

我用的Android Studio,有各種一鍵生成(x),要看手擼的話,網上應該能搜到,本文主要是介紹那些遇到的坑。

AS生成的main.cpp長這樣:

#include <jni.h>
#include <string>

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

分析一下:

  • 幾個include,其中jni.h是JNI必需的,其他的可以添加C(艸)中的,比如stdio.h什么的
  • extern C,這個我也不是特別理解,自我修養里面說是聲明為C語言,然而刪掉過后就炸了
  • jstring,返回值類型
  • Java_com_helloworld_jnidemo_MainActivity_stringFromJNI,Java_包名類名方法名,這是函數聲明的規范
  • JNIEnv *env, jobject /* this */,JNIEnv里面有巨量的函數,后面就知道了,jobject就是this
  • std::string hello = "Hello from C++";,C艸
  • env->NewStringUTF(hello.c_str()),這兒就出現了env的其中一個函數,這個函數會經常在后面用到,char*轉String,沒錯,他們不一樣!

然后我自己寫了一個HelloWordl和求和的函數:

extern "C"
jstring
Java_com_helloworld_jnidemo_MainActivity_helloworld(JNIEnv *env, jobject /* this */) {
    return env->NewStringUTF("Hello World");
}

extern "C"
jint
Java_com_helloworld_jnidemo_MainActivity_sum(JNIEnv *env, jobject /* this */, jint a, jint b) {
    return a + b;
}

Java中該這樣寫:

static {
    System.loadLibrary("native-lib");
}

public native String stringFromJNI();

public native String helloworld();

其中,System.loadLibrary("native-lib");這句是加載庫,static語句塊中的內容只會被執行一次。native-lib為庫的名稱,聲明方法時使用native關鍵字。

CMakeLists.txt:

add_library( 
            native-lib
            SHARED
            src/main/cpp/native-lib.cpp )
find_library( 
            log-lib
            log )
target_link_libraries(
                    native-lib
                    ${log-lib} )

其中,native-lib可以隨便改,對應System.loadLibrary("native-lib");里面的。但是有個玄學問題,不能改成test。。。被坑了。。。
src/main/cpp/native-lib.cpp里面的文件名可以隨便改,只要與你寫的文件對應。

好的,JNI入門了的樣子。

Learn More

寫出來了Hello World,該繼續深入研究了。在繼續之前,我們還應該了解一下jstringjint這些是啥,這兒有個表,展示了JNI和Java里面的屬性的關系:

  • jint --> int
  • jbyte --> byte
  • jshort --> short
  • jlong --> long
  • jfloat --> float
  • jdouble --> double
  • jchar --> char
  • jboolean --> boolean
  • jclass --> java.lang.Class
  • jstring --> java.lang.String
  • jarray --> Array
  • jxxxArray --> xxx[]
  • jobject --> Object
  • ...

注意最后一個,一切皆為對象。

使用JNI,你應該實現Java的基本功能:

  • new對象
  • call方法
  • 獲取屬性

學會了以上三個操作,就可以用JNI代替Java中70%以上的操作了。讓我們一個一個來看。

new對象 & Call方法

沒錯,new對象就是通過調用構造方法實現的。

extern "C"
jobject
Java_com_helloworld_asdf_MainActivity_newObject(JNIEnv *env, jobject /* this */) {
    jclass clazz = env->FindClass("java/lang/Object");
    jmethodID init = env->GetMethodID(clazz, "<init>", "()V");
    jobject result = env->NewObject(clazz, init);
    return result;
}

步驟:

  • 找到class,用/代替.,FindClass的參數為所在包名
  • 找到對應構造方法
  • 調用newObject,傳入class和構造方法id。

再看看一般的方法調用:

extern "C"
jint
Java_com_helloworld_asdf_MainActivity_stringLen(JNIEnv *env, jobject /* this */, jstring str) {
    jclass clazz = env->GetObjectClass(str);
    jmethodID lenId = env->GetMethodID(clazz, "length", "()I");
    jint result = env->CallIntMethod(str, lenId);
    return result;
}

GetObjectClass可以直接從object中拿到class。

調用方法用CallxxxMethod,xxx為返回值類型。CallxxxMethod的第一個參數是jobject,不是jclass,這個與NewObject不同。前面有jxxxArray,然而并沒有CallxxxArrayMethod哎,該怎么辦呢?一切都是對象,用CallObjectMethod再強轉就可以了。
比如:

extern "C"
jstring
Java_com_helloworld_asdf_MainActivity_toString(JNIEnv *env, jobject /* this */, jobject object) {
    jclass clazz = env->GetObjectClass(object);
    jmethodID lenId = env->GetMethodID(clazz, "toString", "()Ljava/lang/String;");
    jstring result = (jstring) env->CallObjectMethod(object, lenId);
    return result;
}

方法簽名:
簡直有毒,反人類

  • construction --> <init>
  • void --> V
  • boolean --> Z
  • byte --> B
  • char --> C
  • short --> S
  • int --> I
  • long --> J
  • float --> F
  • double --> D
  • x[] --> [x
  • x[][] --> [[x
  • java.lang.String --> L/java/lang/String;

總結一下:

  • 每個基本類型都有對應的簽名,基本法
  • 數組用[
  • 構造方法規定為<init>
  • 其它類為L類;,注意:分號不能丟,分號不能丟,分號不能丟

獲取Field

extern "C"
jint
Java_com_helloworld_asdf_MainActivity_getX(JNIEnv *env, jobject /* this */, jobject test) {
    jclass clazz = env->GetObjectClass(test);
    jfieldID lenId = env->GetFieldID(clazz, "x", "I");
    jint result = env->GetIntField(test, lenId);
    return result;
}

static

static的屬性和方法與普通的有一些區別,例如CallStaticObjectMethod的第一個參數是jclass。這些在熟悉了上面的操作過后都沒有太大的問題了。

分享一點經驗

  • 一切都是object
  • Java里的String和C(艸)里的是不一樣的,要記得NewStringUTF,被坑過
  • L/java/lang/String;
  • java/util/Listjava/util/ArrayList是不一樣的。。。要看清方法的參數。。。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,501評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,673評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,610評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,939評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,668評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,004評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,001評論 3 449
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,173評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,705評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,426評論 3 359
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,656評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,139評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,833評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,247評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,580評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,371評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,621評論 2 380

推薦閱讀更多精彩內容