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,jmethodID
struct _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 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; 分号不能少。
参考
System.loadLibrary()
导入so库函数。比如你的库是libhelljni.so 那么导入是这样的System.loadLibrary(“helljni”)
该函数调用一般是在静态代码块中
static{
System.loadLibrary("helljni");
}
JNIEnv
JNIEnv JNI接口指针 用于调用方法等
JNI_OnLoad
在加载so之后需要做的工作,主动注入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/MediaScanner
package 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.cpp
int 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编写教程都是这种方法,大致流程是这样的
/* 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);
自己加上变量名
生产C 共享库
ndk-build 生产so文件,Android studio 需要做ndk配置。
build.gradle
android{
defaultConfig{
ndk{
moduleName "XXX"
ldLibs "log"
abiFilters "armeabi", "armeabi-v7a", "x86"
}
}
}
在main/下建立 jni和jniLibs
gradle.properties加入
android.useDeprecatedNdk=true
这两种方式建议用主动注册方式(或者动态注册),编写起来函数名简便,且不需要VM去查找函数省时间。
参考