Android(安卓)NDK 开发
Android NDK 开发
JNI 基础
- Java 与 C/C++ 基本类型对应
Java | jni | 占用大小 |
---|---|---|
byte | jbyte | 1 |
short | jshort | 2 |
int | jint | 4 |
float | jfloat | 4 |
long | jlong | 8 |
double | jdouble | 8 |
char | jchar | 2 |
boolean | jboolean | 1 |
void | void |
定义在
JDK_HOME/include/jni.h
JDK_HOME/include/platform/jni_md.h
- 引用类型对应
Java引用类型 | Java jni |
---|---|
Class | jclass |
Object | jobject |
String | jstring |
Array | jarray,jbooleanArray,jbyteArray,jcharArray…. |
throwable | jthrowable |
域和方法IDs
jfiledID,jmethodIDstruct _jfieldID; /* opaque structure */ typedef struct _jfieldID *jfieldID; /* field IDs */ struct _jmethodID; /* opaque structure */ typedef struct _jmethodID *jmethodID; /* method IDs */
jvalue Type
typedef union jvalue { jboolean z; jbyte b; jchar c; jshort s; jint i; jlong j; jfloat f; jdouble d; jobject l; } jvalue;
- 类型签名(Type Signatures)
Type Signature | Java Type |
---|---|
Z | boolean |
B | byte |
C | char |
S | short |
I | int |
J | long |
F | float |
D | Double |
L fully-qualified-class; | full-qualified-class |
[ type | type[] |
(arg-types)ret-type | method type |
对于Java方法:
long f (int n,String s,int[] arr);
有如下签名:
(ILjava/lang/String;[I)J
需要注意的是引用类型String,必须像这样Ljava/lang/String; 分号不能少。
参考
JNI 几个重要函数及变量
System.loadLibrary()
导入so库函数。比如你的库是libhelljni.so 那么导入是这样的System.loadLibrary(“helljni”)
该函数调用一般是在静态代码块中static{ System.loadLibrary("helljni");}
JNIEnv
JNIEnv JNI接口指针 用于调用方法等JNI_OnLoad
在加载so之后需要做的工作,主动注入native函数需要实现
JNI 主动注册和被动注册 native 函数
调用 RegisterNatives 主动注册JNI函数
Android 系统模块一般都用这个方法。具体可查看源码
frameworks\base\core\jni
frameworks\base\services\jni
fremeworks\base\media\jni我们来拿源码Media部分代码看看这个这过程这么做的。
首先,需要在Java代码中声明native函数
framework/base/media/java/android/media/MediaScannerpackage android.media;.......public class MediaScanner{ static { System.loadLibrary("media_jni"); native_init(); } ......... private static native final void native_init(); private native final void native_setup(); private native final void native_finalize();}
C++实现/framework/base/media/jni/android_media_MediaScanner.cpp
// This function gets a field ID, which in turn causes class initialization.// It is called from a static block in MediaScanner, which won't run until the// first time an instance of this class is used.static void android_media_MediaScanner_native_init(JNIEnv *env){ ALOGV("native_init"); jclass clazz = env->FindClass(kClassMediaScanner); if (clazz == NULL) { return; } fields.context = env->GetFieldID(clazz, "mNativeContext", "I"); if (fields.context == NULL) { return; }}.......static JNINativeMethod gMethods[] = {......{ "native_init", "()V", (void *)android_media_MediaScanner_native_init},{ "native_setup", "()V", (void *)android_media_MediaScanner_native_setup},{ "native_finalize", "()V", (void *)android_media_MediaScanner_native_finalize},};........// This function only registers the native methods, and is called from// JNI_OnLoad in android_media_MediaPlayer.cppint register_android_media_MediaScanner(JNIEnv *env){ return AndroidRuntime::registerNativeMethods(env, kClassMediaScanner, gMethods, NELEM(gMethods));}
真正导入so然后调用JNI_OnLoad 然后注册registerNativeMethods否则找不到native函数
我们看看framework/base/media/jni/android_media_MediaPlayer.cpp......extern int register_android_media_MediaScanner(JNIEnv *env);......jint JNI_OnLoad(JavaVM* vm, void* reserved){JNIEnv* env = NULL;jint result = -1;if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) { ALOGE("ERROR: GetEnv failed\n"); goto bail;}assert(env != NULL);........if (register_android_media_MediaScanner(env) < 0) { ALOGE("ERROR: MediaScanner native registration failed\n"); goto bail;}......./* success -- return valid version number */result = JNI_VERSION_1_4;bail: return result;}
被动注册Native函数
这个很多jni编写教程都是这种方法,大致流程是这样的
- 编写Java native 代码,javac 生产class(Android Studio 可以自动构建)
- 使用javah命令,生产包含JNI本地函数原型的头文件
一般生产的函数原型头文件是这样的
/* DO NOT EDIT THIS FILE - it is machine generated */ #include
/* Header for class com_XXX_XXXX_utils_NativeSpeedTest */ #ifndef _Included_com_XXX_XXXX_utils_NativeSpeedTest #define _Included_com_XXX_XXXX_utils_NativeSpeedTest #ifdef __cplusplus extern "C" { #endif /* * Class: com_XXX_XXXX_utils_NativeSpeedTest * Method: startDownloadWireless * Signature: ([Ljava/lang/String;II)I */ JNIEXPORT jint JNICALL Java_com_XXX_XXXX_utils_NativeSpeedTest_startDownloadWireless (JNIEnv *env, jclass cclass, jobjectArray jurls, jint jconns , jint jduration); /* * Class: com_XXX_XXXX_utils_NativeSpeedTest * Method: signatureVerify * Signature: (Ljava/lang/String;)I */ JNIEXPORT jint JNICALL Java_com_XXX_XXXX_utils_NativeSpeedTest_signatureVerify (JNIEnv *env, jclass cclass, jstring jstr); /* * Class: com_XXX_XXXX_utils_NativeSpeedTest * Method: init * Signature: (Ljava/lang/String;)I */ JNIEXPORT jint JNICALL Java_com_XXX_XXXX_utils_NativeSpeedTest_init (JNIEnv *env, jclass cclass, jstring jstr); /* * Class: com_XXX_XXXX_utils_NativeSpeedTest * Method: uninit * Signature: ()V */ JNIEXPORT void JNICALL Java_com_XXX_XXXX_utils_NativeSpeedTest_uninit (JNIEnv *env, jclass cclass); /* * Class: com_XXX_XXXX_utils_NativeSpeedTest * Method: startDownload * Signature: ([Ljava/lang/String;II)I */ JNIEXPORT jint JNICALL Java_com_XXX_XXXX_utils_NativeSpeedTest_startDownload (JNIEnv *env, jclass cclass, jobjectArray jurls, jint jconns, jint jduration); /* * Class: com_XXX_XXXX_utils_NativeSpeedTest * Method: statUpload * Signature: ([Ljava/lang/String;II)I */ JNIEXPORT jint JNICALL Java_com_XXX_XXXX_utils_NativeSpeedTest_statUpload (JNIEnv *env, jclass cclass, jobjectArray jurls, jint jconns, jint jduration); /* * Class: com_XXX_XXXX_utils_NativeSpeedTest * Method: callBack * Signature: (Ljava/lang/String;Ljava/lang/String;)I */ JNIEXPORT jint JNICALL Java_com_XXX_XXXX_utils_NativeSpeedTest_callBack (JNIEnv *env, jclass thiz, jstring jclassname, jstring jmethodname); #ifdef __cplusplus } #endif#endif 自动生产的话是没有定义变量像这样
/*
* Class: com_XXX_XXXX_utils_NativeSpeedTest
* Method: callBack
* Signature: (Ljava/lang/String;Ljava/lang/String;)I
*/
JNIEXPORT jint JNICALL Java_com_XXX_XXXX_utils_NativeSpeedTest_callBack
(JNIEnv , jclass, jstring, jstring);
自己加上变量名- 实现JNI本地函数
然后新建.cpp文件实现上面函数 生产C 共享库
ndk-build 生产so文件,Android studio 需要做ndk配置。
build.gradleandroid{ defaultConfig{ ndk{ moduleName "XXX" ldLibs "log" abiFilters "armeabi", "armeabi-v7a", "x86" } }}
在main/下建立 jni和jniLibs
gradle.properties加入
android.useDeprecatedNdk=true
- 通过JNI,调用JNI本地函数
这两种方式建议用主动注册方式(或者动态注册),编写起来函数名简便,且不需要VM去查找函数省时间。
参考
更多相关文章
- 自定义全屏Dialog
- android ndk调用Log.getStackTraceString(new Throwable())输出
- Android(安卓)调用系统浏览器 出现activitynotfoundexception 解
- 几个activity跳转特效的实现
- Android(安卓)SDL移植版学习笔记
- FregServer进程,返回BR_REPLY
- Android(安卓)如何调用MediaPlayer播放视频[状态机]【图】
- android framework添加自定义服务,实现java层api调用
- Android实现自己的回调函数