一基于開發(fā)Android的C++必備知識
自我感覺Android開發(fā)JNI僅僅是為了調(diào)用c庫完成一些操作,所以僅僅需要了解基礎(chǔ)的c/c++就足夠了,首先jni的本地函數(shù)的必備參數(shù)就是兩個結(jié)構(gòu)體指針,所以最先學(xué)習(xí)結(jié)構(gòu)體指針
#includestruct smile
{
char sna;
char size;
float price;
};
int main(void)
{
struct smile x;
x.sna = 'M';
x.size = 'B';
x.price = 20.5;
printf( "%c, %c, %.1f", x.sna, x.size, x.price );
return 0;}
這是一個典型的結(jié)構(gòu)體定義和調(diào)用的寫法,和Java的區(qū)別是不需要new對象,結(jié)構(gòu)體成員變量的訪問除了可以借助符號".",還可以用"->"訪問,這就需要結(jié)構(gòu)體的指針,后面我們會知道本地函數(shù)會將Java對象的地址傳遞給c,所以jni中都是指針訪問的形式
#include#includestruct smile {
char sna;
float price;
};
int main(void)
{
struct smile x;
struct smile *px;
px = &x;
px->sna = 'R';
px->price = 26.8;
printf( "Sna=[%c], Price=%.1f", x.sna, x.price );
}
上面就是指針訪問的形式,將定義的struct對象指針賦值給*px,就可以操作地址,因?yàn)閖ava和c的對象不通用,所以,他們互相調(diào)用的原理虛擬機(jī)就是通過指針進(jìn)行地址操作,下面就分析jni的調(diào)用過程,當(dāng)我們調(diào)用system.loadlibrary()時,vm就會調(diào)用JNI_OnLoad()函數(shù),這個函數(shù)的源碼地址是/system/lib/libmedia_jni
// #define LOG_NDEBUG 0
#define LOG_TAG "MediaPlayer-JNI"
// ………
jint JNI_OnLoad(JavaVM* vm, void* reserved) {
JNIEnv* env = NULL;
jint result = -1;
if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
LOGE("ERROR: GetEnv failed\n"); goto bail;
}
assert(env != NULL);
if (register_android_media_MediaPlayer(env) < 0) {
LOGE("ERROR: MediaPlayer native registration failed\n");
goto bail;
}
/* success -- return valid version number */
result = JNI_VERSION_1_4;
bail: return result;
}
此函數(shù)回傳JNI_VERSION_1_4值給VM,于是VM知道了其所使用的JNI版本了。此外, JNI_OnLoad()函數(shù)也做了一些初期的動作,例如指令:
if (register_android_media_MediaPlayer(env) < 0) {
LOGE("ERROR: MediaPlayer native registration failed
\n");
goto bail;
}
就將此*.so的<函數(shù)名稱表>登記到VM里,以便能加快后續(xù)調(diào)用本地函數(shù)之效率,Java類別透過VM而調(diào)用到本地函數(shù)。一般是仰賴VM去尋找*.so里的本地函數(shù)。如果需要連續(xù)調(diào)用很多次,每次都需要尋找一遍,會多花許多時間。此時,將此*.so的<函數(shù)名稱表>登記到VM里。
接下來.so中我們實(shí)現(xiàn)我們的本地函數(shù):
JNIEXPORT jstring JNICALL Java_test_yuan_codelearntest_Utils_JniUtils_test(JNIEnv *env,jobject obj)
{
returnenv -> NewStringUTF("Hello World!");
}
jobject obj表示我們的本地函數(shù)所在的類的對象,JNIEnv *env這個參數(shù)可以理解為虛擬機(jī)指針,因?yàn)樘摂M機(jī)是鏈接Java和c的橋梁,我們可以利用這個指針操作java的性能,比如我們可以拿到對象的class:jclass clazz = (*env)->GetObjectClass(env, obj);這樣就可以做一些反射的操作,比如拿到函數(shù)id:? m_mid = (*env)->GetMethodID(env, clazz, "setV", "(I)V");從C調(diào)用java函數(shù):(*env)->CallVoidMethod(env, thiz, m_mid, sum);等等操作