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的內容。