Android设计与实现(二)

来源:互联网 发布:做网站需要会编程吗 编辑:程序博客网 时间:2024/06/08 07:00

虽然写的我也不怎么懂,先留着,慢慢磨

第二章 框架基础JNI

1.1 JNI 在Android系统中所处的位置

主要是处于上两层:

  1. 应用层:采用ndk开发
  2. 应用架构层:自定义的jni编程模型

1.2 JNI 架构层实例分析

以日志系统为例

  1. frameworks/base/core/jni/android_util_Log.cpp(JNI层实现代码)
  2. frameworks/base/core/java/adnroid/util/Log.java(Java层代码)
  3. libnaivehelper/include/nativehelper/jni.h(JNI规范的头文件)
  4. libnaivehelper/include/nativehelper/JNIHelp.h
  5. libnativehelper/JNIHelp.cpp
  6. 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结构图

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实现方法

数据结构类型

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分别代表Stringint

(IILjava/lang/String;Ljava/lang/String;)I,其中括号后面的I,是返回类型int,括号里面IILjava/lang/String;Ljava/lang/String,分别代表 intintStringString

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

引用设置

一般设置

  1. 在方法外面加入全局变量,方法内赋值
  2. 在方法里面定义静态变量,并且赋值

例如

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提供的三种引用

  1. 局部引用
    作用:增加引用计数,范围:本线程,一次native调用,方法返回后,被虚拟机回收
  2. 全局引用
    作用:增加引用计数,范围:多线程,多个native调用,必须显示释放,不释放不回收
  3. 弱全局引用
    作用:不能引用计数,范围:多线程,多个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 异常处理

检查方法:

  1. 检查上次Jni函数调用的返回值是否为null
  2. 通过调用jni函数ExceptionOccurred() 来判断是否发生异常

处理方法

  1. Native 方法立即返回,这样异常就会调用该Native方法的Jave代码中抛出,需要Java捕获异常
  2. 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;}
阅读全文
1 0
原创粉丝点击