聲明
前階段在項目中使用了Android的JNI技術,在此文中做些技術知識總結。 本文參考了一些書籍的若干章節,比如《Android進階解密-第9章-JNI原理》、《深入理解Android虛擬機-第4章-分析JNI》、《深入理解Android系統-第2章-分析JNI》、《Android NDK Beginner_'s Guide》等 本文使用的代碼時LineageOS的cm-14.1,對應Android 7.1.2,可以參考我的另一篇博客:cm-14.1 Android系統啟動過程分析(〇)-如何下載Nexus5的LineageOS14.1(cm-14.1)系統源碼并編譯、刷機
1 Java層和JNI層的數據類型轉換
????進入到源碼路徑: vim ~/LineageOS/frameworks/base/media/jni/android_ media_MediaRecorder.cpp,查看函數android_media_MediaRecorder_native_setup:
static void
android_media_MediaRecorder_native_setup ( JNIEnv
* env
, jobject thiz
, jobject weak_this
, jstring packageName
, jstring opPackageName
)
{ ALOGV ( "setup" ) ; ScopedUtfChars
opPackageNameStr ( env
, opPackageName
) ; sp
< MediaRecorder
> mr
= new MediaRecorder ( String16 ( opPackageNameStr
. c_str ( ) ) ) ; if ( mr
== NULL ) { jniThrowException ( env
, "java/lang/RuntimeException" , "Out of memory" ) ; return ; } if ( mr
- > initCheck ( ) != NO_ERROR
) { jniThrowException ( env
, "java/lang/RuntimeException" , "Unable to initialize media recorder" ) ; return ; } sp
< JNIMediaRecorderListener
> listener
= new JNIMediaRecorderListener ( env
, thiz
, weak_this
) ; mr
- > setListener ( listener
) ; const char16_t * rawClientName
= reinterpret_cast < const char16_t * > ( env
- > GetStringChars ( packageName
, NULL ) ) ; jsize rawClientNameLen
= env
- > GetStringLength ( packageName
) ; String16
clientName ( rawClientName
, rawClientNameLen
) ; env
- > ReleaseStringChars ( packageName
, reinterpret_cast < const jchar
* > ( rawClientName
) ) ; mr
- > setClientName ( clientName
) ; setMediaRecorder ( env
, thiz
, mr
) ;
}
????其中jobject、jstring類型參數都是JNI層的數據類型,Java層數據類型到JNI層就要轉換為JNI層數據結構。包括基本數據類型 和引用數據類型
1.1 Java層至JNI層基本數據類型的轉換
Java類型JNI類型C++類型關系描述簽名占內存大小 boolean jboolean unsigned char 或 unit8_t 布爾類型 Z 1 int jint int或long 整形 I 4 float jfloat float 單精度類型 F 4 double jdouble double 雙精度類型 D 8 long jlong long 長整形 J 8 short jshort short 短整型 S 2 char jchar unsigned short 字符 C 2 byte jbyte signed char 字節類型 B 1 void void void 空類型 V
????從轉換表中可以看出Java層基本數據類型轉換到JNI層只需將數據類型前加個“j”即可(除了void類型)。
1.2 Java層至JNI層引用數據類型的轉換
JavaNative簽名(以;結尾) 所有對象 jobject L+classname +; Class jclass Ljava/lang/Clas; String jstring Ljava/lang/String; Trowable jthrowable Ljava/lang/Throwable; Object[] jobjectArray [L+classname +; byte[] jbyteArray [B char[] jcharArray [C double[] jdoubleArray [D float[] jfloatArray [F int[] jintArray [I short[] jshortArray [S long[] jlongArray [J boolean[] jbooleanArray [Z
????以~/LineageOS/frameworks/base/media/java/android/media/ MediaRecorder.java中的native_setup方法為例:
private native final
void native_setup ( Object mediarecorder_this
, String clientName
, String opPackageName
) throws IllegalStateException
;
????對應在~/LineageOS/frameworks/base/media/jni/android_ media_MediaRecorder.cpp,查看函數android_media_MediaRecorder_native_setup:
static void
android_media_MediaRecorder_native_setup ( JNIEnv
* env
, jobject thiz
, jobject weak_this
, jstring packageName
, jstring opPackageName
)
{
. . . 省略n行
. . .
}
????可以發現:Object類型轉換為jobject類型,String類型轉換為jstring類型。
2 方法簽名
????進入到源碼路徑: vim ~/LineageOS/frameworks/base/media/jni/ android_media_MediaRecorder.cpp,在數組gMethods[]中可看到簽名信息:
static const JNINativeMethod gMethods
[ ] = { { "setCamera" , "(Landroid/hardware/Camera;)V" , ( void * ) android_media_MediaRecorder_setCamera
} , { "setVideoSource" , "(I)V" , ( void * ) android_media_MediaRecorder_setVideoSource
} , { "setAudioSource" , "(I)V" , ( void * ) android_media_MediaRecorder_setAudioSource
} , { "setOutputFormat" , "(I)V" , ( void * ) android_media_MediaRecorder_setOutputFormat
} , { "setVideoEncoder" , "(I)V" , ( void * ) android_media_MediaRecorder_setVideoEncoder
} , { "setAudioEncoder" , "(I)V" , ( void * ) android_media_MediaRecorder_setAudioEncoder
} , { "setParameter" , "(Ljava/lang/String;)V" , ( void * ) android_media_MediaRecorder_setParameter
} , { "_setOutputFile" , "(Ljava/io/FileDescriptor;JJ)V" , ( void * ) android_media_MediaRecorder_setOutputFileFD
} , { "setVideoSize" , "(II)V" , ( void * ) android_media_MediaRecorder_setVideoSize
} , { "setVideoFrameRate" , "(I)V" , ( void * ) android_media_MediaRecorder_setVideoFrameRate
} , { "setMaxDuration" , "(I)V" , ( void * ) android_media_MediaRecorder_setMaxDuration
} , { "setMaxFileSize" , "(J)V" , ( void * ) android_media_MediaRecorder_setMaxFileSize
} , { "_prepare" , "()V" , ( void * ) android_media_MediaRecorder_prepare
} , { "getSurface" , "()Landroid/view/Surface;" , ( void * ) android_media_MediaRecorder_getSurface
} , { "getMaxAmplitude" , "()I" , ( void * ) android_media_MediaRecorder_native_getMaxAmplitude
} , { "start" , "()V" , ( void * ) android_media_MediaRecorder_start
} , { "stop" , "()V" , ( void * ) android_media_MediaRecorder_stop
} , { "pause" , "()V" , ( void * ) android_media_MediaRecorder_pause
} , { "resume" , "()V" , ( void * ) android_media_MediaRecorder_resume
} , { "native_reset" , "()V" , ( void * ) android_media_MediaRecorder_native_reset
} , { "release" , "()V" , ( void * ) android_media_MediaRecorder_release
} , { "native_init" , "()V" , ( void * ) android_media_MediaRecorder_native_init
} , { "native_setup" , "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;)V" , ( void * ) android_media_MediaRecorder_native_setup
} , { "native_finalize" , "()V" , ( void * ) android_media_MediaRecorder_native_finalize
} , { "native_setInputSurface" , "(Landroid/view/Surface;)V" , ( void * ) android_media_MediaRecorder_setInputSurface
} ,
} ;
????簡單地說,存在簽名的原因就是Java語言的方法是可以重載的 ,重載的方法名字相同而參數不同,所以JNI僅通過方法名無法確定對應的是重載的哪個方法,必須要參數簽名來輔助其關聯。
避免博客拖太長,后續參見下篇Android系統的JNI原理分析(三)- JNIEnv
總結
以上是生活随笔 為你收集整理的Android系统的JNI原理分析(二)- 数据类型转换和方法签名 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。