Android设计与实现(二)
来源:互联网 发布:做网站需要会编程吗 编辑:程序博客网 时间:2024/06/08 07:00
虽然写的我也不怎么懂,先留着,慢慢磨
第二章 框架基础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;#elsetypedef 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实现方法
数据结构类型
JNI继承关系
JNI方法签名规则
举个栗子
在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异常处理表
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;}
- Android设计与实现(二)
- Android Binder设计与实现 – 设计篇(二)
- Android Binder设计与实现 – 设计篇(二)
- android Binder设计与实现二
- 【android开发】手写签名系统的设计与实现之实现解析pdf文件(二)
- 【android开发】手写签名系统的设计与实现之实现解析pdf文件(二)
- redis设计与实现(二)链表
- android实验二 图片查看器的设计与实现
- 【Android UI设计与开发】5.底部菜单栏(二)使用Fragment实现底部菜单栏
- 【Android UI设计与开发】10:滑动菜单栏(二)SlidingMenu 动画效果的实现
- 【Android UI设计与开发】2.引导界面(二)使用ViewPager实现欢迎引导页面
- 有限状态机(FSM)的设计与实现(二)
- 有限状态机(FSM)的设计与实现(二)
- 有限状态机(FSM)的设计与实现(二)
- 基于linux的iSCSI设计与实现(二)
- 一种日志结构文件系统的设计与实现(二)
- zigbee 无线网络通讯设计与实现(二)
- C++:多线程类库的设计与实现(二)
- 多线程
- 设计模式之三——工厂方法模式(Factory Method)&抽象工厂模式(AbstractFacotry)
- AFNetworking报错"_UTTypeCopyPreferredTagWithClass", referenced from: _AFContentTypeForPathExtens
- abap中获取COOIS的ALV内容
- 神经网络具体实现步骤.
- Android设计与实现(二)
- RadioButton 点击改变字体颜色
- 关于const的疑问
- 国际跳棋
- js 计算两个时间的时间差
- PHP应用时区问题
- HDU 5573B Binary Tree(思维)
- 购物车 天猫
- 洛谷 P1848