声明
前阶段在项目中使用了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原理分析(二)- 数据类型转换和方法签名 的全部内容,希望文章能够帮你解决所遇到的问题。
如果觉得生活随笔 网站内容还不错,欢迎将生活随笔 推荐给好友。