What is JNI
JNI是Java Native Interface的縮寫,主要是提供了一系列API揣钦,讓你能在其它語言中寫Java。
What JNI can bring us
JNI最大的好處就是,額些阅,Java你懂的狐粱,跑在JVM里面舀寓,雖然有著一處編譯,到處運行的優(yōu)勢(肌蜻,方便盎ツ埂),但是它的效率蒋搜。篡撵。。至少相對于c和C艸來說豆挽,比較低下酸休,而且,正是由于這個能一處編譯祷杈,到處運行的原因斑司,Java極容易被反編譯。Java中一般用的加密方式就是混淆了,然而其實并沒有太大的作用宿刮。你還是開源吧互站。。僵缺。因為不開源也會被反編譯的胡桃。。磕潮。
PS:并沒有貶低Java的意思翠胰,個人還是挺喜歡用Java的
然后,相反的自脯,JNI由于是用C或者C艸寫之景,效率很高,可以用來處理一些底層的東西膏潮,比如解碼或者TCP/IP有關(guān)的锻狗。編譯過后跟C(艸)編譯的結(jié)果是一樣的,在Android里面是.so文件焕参。然后轻纪,因為是C(艸),所以需要針對不同的平臺叠纷,不同的處理器進行編譯刻帚。所以,使用JNI涩嚣,你需要在編譯的時候生成許多個平臺的版本崇众,否則,Java跨平臺這個優(yōu)點相當(dāng)于直接被廢了缓艳。還有就是JNI的調(diào)試會非常蛋疼。
How to use JNI
Hello World
我用的Android Studio看峻,有各種一鍵生成(x)阶淘,要看手擼的話,網(wǎng)上應(yīng)該能搜到互妓,本文主要是介紹那些遇到的坑溪窒。
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
,這個我也不是特別理解灼狰,自我修養(yǎng)里面說是聲明為C語言宛瞄,然而刪掉過后就炸了 -
jstring
,返回值類型 -
Java_com_helloworld_jnidemo_MainActivity_stringFromJNI
交胚,Java_包名類名方法名份汗,這是函數(shù)聲明的規(guī)范 -
JNIEnv *env, jobject /* this */
盈电,JNIEnv里面有巨量的函數(shù),后面就知道了杯活,jobject就是this -
std::string hello = "Hello from C++";
匆帚,C艸 -
env->NewStringUTF(hello.c_str())
,這兒就出現(xiàn)了env的其中一個函數(shù)旁钧,這個函數(shù)會經(jīng)常在后面用到吸重,char*轉(zhuǎn)String,沒錯歪今,他們不一樣嚎幸!
然后我自己寫了一個HelloWordl和求和的函數(shù):
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語句塊中的內(nèi)容只會被執(zhí)行一次鞭铆。native-lib
為庫的名稱,聲明方法時使用native
關(guān)鍵字焦影。
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
可以隨便改,對應(yīng)System.loadLibrary("native-lib");
里面的斯辰。但是有個玄學(xué)問題舶担,不能改成test。彬呻。衣陶。被坑了。闸氮。剪况。
src/main/cpp/native-lib.cpp
里面的文件名可以隨便改,只要與你寫的文件對應(yīng)蒲跨。
好的译断,JNI入門了的樣子。
Learn More
寫出來了Hello World或悲,該繼續(xù)深入研究了孙咪。在繼續(xù)之前,我們還應(yīng)該了解一下jstring
巡语,jint
這些是啥翎蹈,這兒有個表,展示了JNI和Java里面的屬性的關(guān)系:
- 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,你應(yīng)該實現(xiàn)Java的基本功能:
- new對象
- call方法
- 獲取屬性
學(xué)會了以上三個操作,就可以用JNI代替Java中70%以上的操作了逞力。讓我們一個一個來看曙寡。
new對象 & Call方法
沒錯,new對象就是通過調(diào)用構(gòu)造方法實現(xiàn)的寇荧。
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
的參數(shù)為所在包名 - 找到對應(yīng)構(gòu)造方法
- 調(diào)用
newObject
揩抡,傳入class和構(gòu)造方法id户侥。
再看看一般的方法調(diào)用:
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。
調(diào)用方法用CallxxxMethod
峦嗤,xxx為返回值類型蕊唐。CallxxxMethod的第一個參數(shù)是jobject,不是jclass烁设,這個與NewObject
不同替梨。前面有jxxxArray,然而并沒有CallxxxArrayMethod
哎装黑,該怎么辦呢副瀑?一切都是對象,用CallObjectMethod
再強轉(zhuǎn)就可以了恋谭。
比如:
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;
總結(jié)一下:
- 每個基本類型都有對應(yīng)的簽名,基本法
- 數(shù)組用[
- 構(gòu)造方法規(guī)定為<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
的屬性和方法與普通的有一些區(qū)別材义,例如CallStaticObjectMethod
的第一個參數(shù)是jclass
均抽。這些在熟悉了上面的操作過后都沒有太大的問題了。
分享一點經(jīng)驗
- 一切都是object
- Java里的String和C(艸)里的是不一樣的其掂,要記得
NewStringUTF
油挥,被坑過 L/java/lang/String;
-
java/util/List
和java/util/ArrayList
是不一樣的。清寇。喘漏。要看清方法的參數(shù)护蝶。勋篓。儒旬。