一基于開發(fā)Android的C++必備知識
自我感覺Android開發(fā)JNI僅僅是為了調用c庫完成一些操作,所以僅僅需要了解基礎的c/c++就足夠了,首先jni的本地函數的必備參數就是兩個結構體指針互妓,所以最先學習結構體指針
#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;}
這是一個典型的結構體定義和調用的寫法离熏,和Java的區(qū)別是不需要new對象爪模,結構體成員變量的訪問除了可以借助符號".",還可以用"->"訪問哗咆,這就需要結構體的指針,后面我們會知道本地函數會將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,就可以操作地址,因為java和c的對象不通用郭脂,所以年碘,他們互相調用的原理虛擬機就是通過指針進行地址操作,下面就分析jni的調用過程,當我們調用system.loadlibrary()時展鸡,vm就會調用JNI_OnLoad()函數屿衅,這個函數的源碼地址是/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;
}
此函數回傳JNI_VERSION_1_4值給VM,于是VM知道了其所使用的JNI版本了莹弊。此外涤久, JNI_OnLoad()函數也做了一些初期的動作,例如指令:
if (register_android_media_MediaPlayer(env) < 0) {
LOGE("ERROR: MediaPlayer native registration failed
\n");
goto bail;
}
就將此*.so的<函數名稱表>登記到VM里忍弛,以便能加快后續(xù)調用本地函數之效率响迂,Java類別透過VM而調用到本地函數。一般是仰賴VM去尋找*.so里的本地函數细疚。如果需要連續(xù)調用很多次蔗彤,每次都需要尋找一遍,會多花許多時間。此時幕与,將此*.so的<函數名稱表>登記到VM里挑势。
接下來.so中我們實現我們的本地函數:
JNIEXPORT jstring JNICALL Java_test_yuan_codelearntest_Utils_JniUtils_test(JNIEnv *env,jobject obj)
{
returnenv -> NewStringUTF("Hello World!");
}
jobject obj表示我們的本地函數所在的類的對象,JNIEnv *env這個參數可以理解為虛擬機指針啦鸣,因為虛擬機是鏈接Java和c的橋梁潮饱,我們可以利用這個指針操作java的性能,比如我們可以拿到對象的class:jclass clazz = (*env)->GetObjectClass(env, obj);這樣就可以做一些反射的操作诫给,比如拿到函數id:? m_mid = (*env)->GetMethodID(env, clazz, "setV", "(I)V");從C調用java函數:(*env)->CallVoidMethod(env, thiz, m_mid, sum);等等操作