虽然写的我也不怎么懂,先留着,慢慢磨
第二章 框架基础JNI
1.1 JNI 在Android系统中所处的位置
主要是处于上两层:
- 应用层:采用ndk开发
- 应用架构层:自定义的jni编程模型
1.2 JNI 架构层实例分析
以日志系统为例
- frameworks/base/core/jni/android_util_Log.cpp(JNI层实现代码)
- frameworks/base/core/java/adnroid/util/Log.java(Java层代码)
- libnaivehelper/include/nativehelper/jni.h(JNI规范的头文件)
- libnaivehelper/include/nativehelper/JNIHelp.h
- libnativehelper/JNIHelp.cpp
- frameworks/base/core/jni/AndroidRuntime.cpp
2.1 Log.java 分析
/**
* @param tag The tag to check.
* @param level The level to check.
* @return Whether or not that this is allowed to be logged.
* @throws IllegalArgumentException is thrown if the tag.length() > 23.
*/
public static native boolean isLoggable(String tag, int level);
/** @hide */ public static final int LOG_ID_MAIN = 0;
/** @hide */ public static final int LOG_ID_RADIO = 1;
/** @hide */ public static final int LOG_ID_EVENTS = 2;
/** @hide */ public static final int LOG_ID_SYSTEM = 3;
/** @hide */
public static native int println_native(int bufID,int priority,
String tag, String msg);
在java层有俩个native方法,isLoggable和println_native,这两个方法在jni层实现,这里可以直接调用。
2.2 Log的JNI层分析
isLoggable实现
static jboolean android_util_Log_isLoggable(JNIEnv* env, jobject clazz, jstring tag, jint level)
{
if (tag == NULL) {
return false;
}
const char* chars = env->GetStringUTFChars(tag, NULL);
if (!chars) {
return false;
}
jboolean result = false;
if ((strlen(chars)+sizeof(LOG_NAMESPACE)) > PROPERTY_KEY_MAX) {
char buf2[200];
snprintf(buf2, sizeof(buf2), "Log tag \"%s\" exceeds limit of %d characters\n",
chars, PROPERTY_KEY_MAX - sizeof(LOG_NAMESPACE));
jniThrowException(env, "java/lang/IllegalArgumentException", buf2);
} else {
result = isLoggable(chars, level);
}
env->ReleaseStringUTFChars(tag, chars);
return result;
}
println_native实现 位置在android_util_Log.cpp
/*
* In class android.util.Log:
* public static native int println_native(int buffer, int priority, String tag, String msg)
*/
static jint android_util_Log_println_native(JNIEnv* env, jobject clazz,
jint bufID, jint priority, jstring tagObj, jstring msgObj)
{
const char* tag = NULL;
const char* msg = NULL;
if (msgObj == NULL) {
jniThrowNullPointerException(env, "println needs a message");
return -1;
}
if (bufID < 0 || bufID >= LOG_ID_MAX) {
jniThrowNullPointerException(env, "bad bufID");
return -1;
}
if (tagObj != NULL)
tag = env->GetStringUTFChars(tagObj, NULL);
msg = env->GetStringUTFChars(msgObj, NULL);
int res = __android_log_buf_write(bufID, (android_LogPriority)priority, tag, msg);
if (tag != NULL)
env->ReleaseStringUTFChars(tagObj, tag);
env->ReleaseStringUTFChars(msgObj, msg);
return res;
}
2.3 log的jni方法的注册 位置在android_util_Log.cpp
/*
* JNI registration.
*/
static JNINativeMethod gMethods[] = {
/* name, signature, funcPtr */
{ "isLoggable", "(Ljava/lang/String;I)Z", (void*) android_util_Log_isLoggable },
{ "println_native", "(IILjava/lang/String;Ljava/lang/String;)I", (void*) android_util_Log_println_native },
};
JNINativeMethod 是一个结构体 在jni.h中有定义
typedef struct {
const char* name;
const char* signature;
void* fnPtr;
} JNINativeMethod;
其中android_util_Log.cpp的register_android_util_Log方法
int register_android_util_Log(JNIEnv* env)
{
jclass clazz = env->FindClass("android/util/Log");
if (clazz == NULL) {
ALOGE("Can't find android/util/Log");
return -1;
}
levels.verbose = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "VERBOSE", "I"));
levels.debug = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "DEBUG", "I"));
levels.info = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "INFO", "I"));
levels.warn = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "WARN", "I"));
levels.error = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "ERROR", "I"));
levels.assert = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "ASSERT", "I"));
return AndroidRuntime::registerNativeMethods(env, "android/util/Log", gMethods, NELEM(gMethods));
}
其中 AndroidRuntime::registerNativeMethods 在 AndroidRuntime.cpp中实现
/*
* Register native methods using JNI.
*/
/*static*/ int AndroidRuntime::registerNativeMethods(JNIEnv* env,
const char* className, const JNINativeMethod* gMethods, int numMethods)
{
return jniRegisterNativeMethods(env, className, gMethods, numMethods);
}
其中jniRegisterNativeMethods在JNIHelp.h 中定义
/*
* Register one or more native methods with a particular class.
* "className" looks like "java/lang/String". Aborts on failure.
* TODO: fix all callers and change the return type to void.
*/
int jniRegisterNativeMethods(C_JNIEnv* env, const char* className, const JNINativeMethod* gMethods, int numMethods);
在JNIHelp.cpp中实现
extern "C" int jniRegisterNativeMethods(C_JNIEnv* env, const char* className,
const JNINativeMethod* gMethods, int numMethods)
{
JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
ALOGV("Registering %s natives", className);
scoped_local_ref<jclass> c(env, findClass(env, className));
if (c.get() == NULL) {
ALOGE("Native registration unable to find class '%s', aborting", className);
abort();
}
if ((*env)->RegisterNatives(e, c.get(), gMethods, numMethods) < 0) {
ALOGE("RegisterNatives failed for '%s', aborting", className);
abort();
}
return 0;
}
Android Log结构图
2.4 JNIEnv
体系结构
JNIEnv 定义
struct _JNIEnv;
struct _JavaVM;
typedef const struct JNINativeInterface* C_JNIEnv;
#if defined(__cplusplus)
typedef _JNIEnv JNIEnv;
typedef _JavaVM JavaVM;
#else
typedef const struct JNINativeInterface* JNIEnv;
typedef const struct JNIInvokeInterface* JavaVM;
#endif
从定义中看出是用了__cplusplus
,作为区分,兼容 C 和 C++两种形式
C++ 中:JNIEnv 就是 struct_JNIEnv
C 中:JNIEnv 就是 const struct JNINativeInterface*
JNIEnv 和 JNINativeInterface 的区别
JNIEnv定义
struct _JNIEnv {
/* do not rename this; it does not seem to be entirely opaque */
const struct JNINativeInterface* functions;
#if defined(__cplusplus)
jint GetVersion()
{ return functions->GetVersion(this); }
jclass DefineClass(const char *name, jobject loader, const jbyte* buf,
jsize bufLen)
{ return functions->DefineClass(this, name, loader, buf, bufLen); }
jclass FindClass(const char* name)
{ return functions->FindClass(this, name); }
jmethodID FromReflectedMethod(jobject method)
{ return functions->FromReflectedMethod(this, method); }
jfieldID FromReflectedField(jobject field)
{ return functions->FromReflectedField(this, field); }
........................
可以看出在const struct JNINativeInterface 是间接调用的 const struct JNINativeInterface* 的方法
JNINativeInterface定义
struct JNINativeInterface {
......................
jclass (*DefineClass)(JNIEnv*, const char*, jobject, const jbyte*,
jsize);
jclass (*FindClass)(JNIEnv*, const char*);
jmethodID (*FromReflectedMethod)(JNIEnv*, jobject);
jfieldID (*FromReflectedField)(JNIEnv*, jobject);
/* spec doesn't show jboolean parameter */
jobject (*ToReflectedMethod)(JNIEnv*, jclass, jmethodID, jboolean);
jclass (*GetSuperclass)(JNIEnv*, jclass);
jboolean (*IsAssignableFrom)(JNIEnv*, jclass, jclass);
/* spec doesn't show jboolean parameter */
jobject (*ToReflectedField)(JNIEnv*, jclass, jfieldID, jboolean);
jint (*Throw)(JNIEnv*, jthrowable);
.....................
可以看出在JNINativeInterface
里面定义许多方法,这里才涉及到了JNI函数的调用,具体实现在虚拟机中。
2.5 在Java中调用JNI实现方法
数据结构类型
Java类型 | JNI类型 | 字长 |
---|---|---|
boolean | jboolean | 8位 |
byte | jbyte | 8位 |
char | jchar | 16位 |
short | jshort | 16位 |
int | jint | 32位 |
long | jlong | 64位 |
float | jfloat | 32位 |
double | jdouble | 64位 |
void | void |
JNI继承关系
JNI方法签名规则
JAVA类型 | 类型签名 |
---|---|
boolean | Z |
byte | B |
char | c |
short | S |
int | I |
long | J |
float | F |
double | D |
类 | L全限定类名 |
数组 | [元素类型签名 |
举个栗子
在android_util_Log.cpp
中,注册方法 getMethods中。
/*
* JNI registration.
*/
static JNINativeMethod gMethods[] = {
/* name, signature, funcPtr */
{ "isLoggable", "(Ljava/lang/String;I)Z", (void*) android_util_Log_isLoggable },
{ "println_native", "(IILjava/lang/String;Ljava/lang/String;)I", (void*) android_util_Log_println_native },
};
返回结果就是JNINativeMethod
,其中结构为
typedef struct {
const char* name;
const char* signature;
void* fnPtr;
} JNINativeMethod;
其中第二个参数就是方法签名信息:(Ljava/lang/String;I)Z
,括号后面的Z
就是返回类型boolean
,括号里面:Ljava/lang/String;
、I
分别代表String
、int
;
(IILjava/lang/String;Ljava/lang/String;)I
,其中括号后面的I
,是返回类型int
,括号里面I
、I
、Ljava/lang/String;
、Ljava/lang/String
,分别代表 int
、int
、String
、String
;
2.4 JNI 操作Java对象
获取对象
C++中
jclass findClass(const char* name);
jclass GetObjectClass(jobject obj);
C中
jclass (FindClass)(JNIEnv, const char*);
jclass (GetObjectClass)(JNIEnv ,jobject);
获取成员变量和方法函数
访问对象的域 | 调用实例方法 | 访问静态域 | 调用静态方法 |
---|---|---|---|
GetFieldID | GetMethodID | GetStaticFieldID | GetStaticMethodID |
Get<Type>Field | Call<Type>MethodID | GetStatic<Type>Field | CallStaic<Type>Method |
Set<Type>Field | CallNouvirtual<Type>Method | SetStatic<Type>Field |
引用设置
一般设置
- 在方法外面加入全局变量,方法内赋值
- 在方法里面定义静态变量,并且赋值
例如
static jboolean android_util_Log_isLoggable(JNIEnv* env, jobject clazz, jstring tag, jint level)
{
................
//clazz_ref1 = clazz;
//static jobject clazz_ref2 = NULL;
//clazz_ref2 = clazz;
if ((strlen(chars)+sizeof(LOG_NAMESPACE)) > PROPERTY_KEY_MAX) {
char buf2[200];
............
jniThrowException(env, "java/lang/IllegalArgumentException", buf2);
} else {
result = isLoggable(chars, level);
}
................
return result;
}
这样做会造成虚拟机无法跟踪该对象的引用计数,相当于没有增加引用计数,如果jobject 已经被虚拟机回收,则clazz_ref1 和 clazz_ref2 将引用一个野指针。
JNI提供的三种引用
- 局部引用
作用:增加引用计数,范围:本线程,一次native调用,方法返回后,被虚拟机回收 - 全局引用
作用:增加引用计数,范围:多线程,多个native调用,必须显示释放,不释放不回收 - 弱全局引用
作用:不能引用计数,范围:多线程,多个native调用,不必须显示释放,不阻止回收
注意:用弱全局引用,注意判断指针的对象被回收,用IsSameObject函数。
C 中:jboolean (IsSameObject)(JNIEnv, jobject,jobject)
C++中:jboolean IsSameObject(jobject ref1,jobject ref2)
正确引用设置如下
static jboolean android_util_Log_isLoggable(JNIEnv* env, jobject clazz, jstring tag, jint level)
{
................
g_clazz_ref = env->NewGlobalRef(clazz);
if ((strlen(chars)+sizeof(LOG_NAMESPACE)) > PROPERTY_KEY_MAX) {
char buf2[200];
............
jniThrowException(env, "java/lang/IllegalArgumentException", buf2);
} else {
result = isLoggable(chars, level);
}
................
env->DeleteGlalRef(g_clazz_ref);
return result;
}
2.6 JNI 异常处理
检查方法:
- 检查上次Jni函数调用的返回值是否为null
- 通过调用jni函数ExceptionOccurred() 来判断是否发生异常
处理方法
- Native 方法立即返回,这样异常就会调用该Native方法的Jave代码中抛出,需要Java捕获异常
- Native 方法可以调用Exceptionclear()来清除异常,然后执行直接的异常处理代码
JNI异常处理表
JNI异常处理函数 | 功能描述 |
---|---|
Throw | 抛出现有异常 |
ThrowNew | 抛出新的异常 |
ExceptionOccurred | 判断是否发生异常,并获得异常的引用 |
ExceptionCheck | 判断是否发生异常 |
ExceptionDescribe | 异常堆栈信息 |
ExceptionClear | 清除一个未处理的异常 |
FatalError | 严重错误,退出 |
Log例子
static jboolean android_util_Log_isLoggable(JNIEnv* env, jobject clazz, jstring tag, jint level)
{
if (tag == NULL) {
return false;
}
//局部引用
const char* chars = env->GetStringUTFChars(tag, NULL);
if (!chars) {
return false;
}
jboolean result = false;
if ((strlen(chars)+sizeof(LOG_NAMESPACE)) > PROPERTY_KEY_MAX) {
char buf2[200];
snprintf(buf2, sizeof(buf2), "Log tag \"%s\" exceeds limit of %d characters\n",
chars, PROPERTY_KEY_MAX - sizeof(LOG_NAMESPACE));
//异常处理
jniThrowException(env, "java/lang/IllegalArgumentException", buf2);
} else {
result = isLoggable(chars, level);
}
//显示释放
env->ReleaseStringUTFChars(tag, chars);
return result;
}
其中jniThrowException
方法在JNIHelp.h中,在JNIHelp.cpp中
定义:
int jniThrowException(C_JNIEnv* env, const char* className, const char* msg);
实现
extern "C" int jniThrowException(C_JNIEnv* env, const char* className, const char* msg) {
JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
if ((*env)->ExceptionCheck(e)) {
/* TODO: consider creating the new exception with this as "cause" */
scoped_local_ref<jthrowable> exception(env, (*env)->ExceptionOccurred(e));
(*env)->ExceptionClear(e);
if (exception.get() != NULL) {
char* text = getExceptionSummary(env, exception.get());
ALOGW("Discarding pending exception (%s) to throw %s", text, className);
free(text);
}
}
scoped_local_ref<jclass> exceptionClass(env, findClass(env, className));
if (exceptionClass.get() == NULL) {
ALOGE("Unable to find exception class %s", className);
/* ClassNotFoundException now pending */
return -1;
}
if ((*env)->ThrowNew(e, exceptionClass.get(), msg) != JNI_OK) {
ALOGE("Failed throwing '%s' '%s'", className, msg);
/* an exception, most likely OOM, will now be pending */
return -1;
}
return 0;
}