Android设计与实现(二)

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

第二章 框架基础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;
#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类型字长
booleanjboolean8位
bytejbyte8位
charjchar16位
shortjshort16位
intjint32位
longjlong64位
floatjfloat32位
doublejdouble64位
voidvoid

JNI继承关系

这里写图片描述

JNI方法签名规则

JAVA类型类型签名
booleanZ
byteB
charc
shortS
intI
longJ
floatF
doubleD
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);

获取成员变量和方法函数

访问对象的域调用实例方法访问静态域调用静态方法
GetFieldIDGetMethodIDGetStaticFieldIDGetStaticMethodID
Get<Type>FieldCall<Type>MethodIDGetStatic<Type>FieldCallStaic<Type>Method
Set<Type>FieldCallNouvirtual<Type>MethodSetStatic<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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
基于Android的App设计实现是指在Android操作系统上开发并实现一个应用程序的过程。在进行设计实现之前,首先要确定App的功能、目标用户和需求等。接下来,可以按照以下步骤进行设计实现: 1. 界面设计:根据App的功能和用户需求,设计用户界面。可以利用可视化界面设计工具进行操作界面的设计,如绘制界面布局、选择合适的图标和颜色等。 2. 功能开发:根据需求,开发App的功能模块。这包括实现用户交互逻辑、数据传输、数据处理等功能。可以使用Java编程语言和Android开发工具包(SDK)进行开发。 3. 数据库设计:根据功能需求,设计数据的存储方式和数据库结构。Android提供了SQLite数据库来存储应用程序的数据。开发人员可以使用SQL语句来创建和管理数据库,以及存取数据。 4. 测试与调试:进行各个功能模块的测试,并进行调试。可以使用模拟器或真实的Android设备对App进行测试,检查和修复可能出现的错误。 5. 发布与更新:当App开发完成并经过测试后,可以将其发布到Google Play等应用商店,并跟踪用户反馈和评价,以便进行后续的更新和改进。 在设计实现过程中,需要注重以下几点: 1. 用户体验:根据用户需求和习惯,设计简洁、直观的界面,提供良好的用户体验。 2. 性能优化:保证App的运行速度和响应时间,在开发过程中优化代码,减少资源占用。 3. 兼容性考虑:考虑不同Android版本和不同屏幕尺寸的设备兼容性,确保App的功能在各种设备上正常运行。 4. 安全性:对于涉及用户隐私和敏感数据的功能,要采取必要的安全措施,如加密数据传输和存储。 通过以上步骤,可以设计实现一个功能完善、界面友好的Android App,并为用户提供良好的使用体验。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值