android分层学习笔记(二)

来源:互联网 发布:淘宝订单怎么申请退款 编辑:程序博客网 时间:2024/06/08 18:16

android 是的jni是java与c之间的中介。java通过jni访问c/c++的函数功能。
jni的编写,比较简单,而且有一个模板可以套用。就像写八股文一样,并且不用像写八股文那样要把内容填入很漂亮,而是完成功能即可,没有华丽的外表。

在hal目录下创建framework,并在framework下创建service/jni目录,即
cd hal
mkdir -p  framework/jni

在jni目录下创建com_ask_gpio.cpp文件。

头文件:
#include "utils/Log.h"            //用于在logcat中可以看到log信息                  

#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>

#include <jni.h>               //jni相关的函数
#include <gpio.h>              //gpio操作的函数及变量(id号),这个头文件其实就是module/gpio/目录下的gpio.h文件

实现gpio置1
static jboolean ask_gpio_set(JNIEnv* env, jobject thiz, jint gpio)
{
    LOGI("GPIOService JNI: ask_gpioSet()");
    
//    apc

    if (g_gpioDev == NULL)
    {
        LOGI("GPIOService JNI: not gpio device.");
        return -1;
    }
    else
    {
        return g_gpioDev->SetGPIO(g_gpioDev, gpio);
    }
}

实现gpio清0
static jboolean ask_gpio_clr(JNIEnv* env, jobject thiz, jint gpio)
{
    LOGI("GPIOService JNI: ask_gpioClr()");

    if (g_gpioDev == NULL)
    {
        LOGI("GPIOService JNI: not gpio device.");
        return -1;
    }
    else
    {
        return g_gpioDev->ClrGPIO(g_gpioDev, gpio);
    }
}
jni层打开gpio设备,其实就是调用了module/gpio/gpio.c中的gpio_device_open函数
static inline int ask_gpio_open(const struct hw_module_t* module, struct gpio_device_t** device)
{
    return module->methods->open(module,  GPIO_HARDWARE_MODULE_ID, (struct hw_device_t**)device);
}

给java层调用的接口函数
static const JNINativeMethod gMethods[] = {
    { "_gpio_init",          "()Z",    (void *)ask_gpio_init },
    { "_gpio_set",        "(I)Z", (void *)ask_gpio_set },
    { "_gpio_clr",       "(I)Z", (void *)ask_gpio_clr },
};
注:关于这个结构体的填写,涉及到c类型到java类型的转换,android定义了桥接变量,如()z之类的,具体的映射如下
JNINativeMethod的结构体变量成员分别为
const char* name;       //Java中函数的名字
const char* signature;  //signature,用字符串是描述了函数的参数和返回值
void* fnPtr;            //fnPtr是函数指针,指向C函数。

对于signature,内容必须与fnPtr是函数指针的格式一致,即
(1)如果函数fnPtr的指向的函数为void fun(void),那么signature的内容必须为“()”
(2)如果函数fnPtr的指向的函数为int func(int i, int j),那么signature的内容必须为“(II)I”

具体的每一个字符的对应关系如下
字符 Java类型 C类型
V:      void     <-->       void
Z:       jboolean  <-->   boolean
I:        jint     <-->     int
J:      jlong            long
D:     jdouble   <-->  double
F:      jfloat   <-->     float
B:      jbyte    <-->     byte
C:      jchar    <-->    char
S:      jshort   <-->   short
数组则以"["开始,用两个字符表示
[I:       jintArray  <-->    int[]
[F:     jfloatArray <-->   float[]
[B:     jbyteArray <-->   byte[]
[C:    jcharArray  <-->  char[]
[S:    jshortArray <-->  short[]
[D:    jdoubleArray <--> double[]
[J:     jlongArray  <-->   long[]
[Z:    jbooleanArray <--> boolean[]
以上为基本类型,如果Java函数的参数是class,则以"L"开头,以";"结尾中间用"/" 隔开的包及类名。而其对应的C函数名的参数则为jobject。 一个例外是String类,其对应的类为jstring
Ljava/lang/String; : String <--> jstring
Ljava/net/Socket; : Socket <--> jobject
如果JAVA函数位于一个嵌入类,则用$作为类名间的分隔符。
例如 "(Ljava/lang/String;Landroid/os/FileUtils$FileStatus;)Z"


注册访问方法
int register_askhals_server_GpioService(JNIEnv* env) {
    static const char* const kClassName = "com/ask/server/GpioService";  
    jclass clazz;

    /* look up the class */
    clazz = env->FindClass(kClassName);
    if (clazz == NULL)
    {
        LOGE("Can't find class %s/n", kClassName);
        return -1;
    }

    /* register all the methods */
    if (env->RegisterNatives(clazz, gMethods,
            sizeof(gMethods) / sizeof(gMethods[0])) != JNI_OK)
    {
        LOGE("Failed registering methods for %s/n", kClassName);
        return -1;
    }

    /* fill out the rest of the ID cache */
    return 0;
}
其中kClassName必须与编写java代码的service的时涉及到.xml中的name项一致,否则将找不到类名,这个将在下节说明。

加载c模块,调用register_askhals_server_GpioService函数
jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
    JNIEnv* env = NULL;
    jint result = -1;

    if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
        LOGE("GetEnv failed!");
        return result;
    }
    LOG_ASSERT(env, "Could not retrieve the env!");

    register_ctophals_server_GpioService(env);
     return JNI_VERSION_1_4;
}
java相关中System.load函数时,就是调用此函数了。

写完cpp文件
那么剩下就是Android.mk文件了。
如下:
LOCAL_PATH:= $(call my-dir)


include $(CLEAR_VARS)
ASK_MODULE_H_DIR := /home/gium/android_src/vendor/ask/modules/gpio/
LOCAL_SRC_FILES:= /
    com_ask_gpio.cpp
     
LOCAL_C_INCLUDES += /
    $(JNI_H_INCLUDE) /
    $(ASK_MODULE_H_DIR)
LOCAL_SHARED_LIBRARIES := /
    libcutils /
    libhardware /
    libhardware_legacy /
    libnativehelper /
    libsystem_server /
    libutils /
    libui
ifeq ($(TARGET_SIMULATOR),true)
ifeq ($(TARGET_OS),linux)
ifeq ($(TARGET_ARCH),x86)
LOCAL_LDLIBS += -lpthread -ldl -lrt
endif
endif
endif

ifeq ($(WITH_MALLOC_LEAK_CHECK),true)
    LOCAL_CFLAGS += -DMALLOC_LEAK_CHECK
endif

LOCAL_MODULE:= libaskgpio

LOCAL_PRELINK_MODULE := false
include $(BUILD_SHARED_LIBRARY)


注:ASK_MODULE_H_DIR这个是自己定义的,就是gpio模块所在目录,要根据自己的实际情况而改,目前我的目录为/home/gium/android_src/vendor/ctop/modules/gpio/
这样在文中的包含头文件可以写成include <gpio.h>

下节说明java调用这个jni的内容。

原创粉丝点击