您的位置:首页 > 移动开发 > Android开发

Android HAL开发之Java应用程序直接调用JNI库

2013-04-22 17:28 399 查看
这篇先介绍最简单的一种实现方式 - Java应用程序直接调用JNI库。

由于JNI技术的存在,在Android中,java程序能够很好的调用C/C++库。我们这里设计一个简单的HAL,一共只有三层: HAL stub <-> JNI 库 <-> JAVA应用程序。

我们现看看HAL stub的代码:
int led_device_close(struct hw_device_t* device)
{
struct led_control_device_t* ctx = (struct led_control_device_t*)device;
if (ctx) {
free(ctx);
}
return 0;
}

int led_on(struct led_control_device_t *dev, int32_t led)
{
LOGI("LED Stub: set %d on.", led);
return 0;
}

int led_off(struct led_control_device_t *dev, int32_t led)
{
LOGI("LED Stub: set %d off.", led);
return 0;
}

static int led_device_open(const struct hw_module_t* module, const char* name,
struct hw_device_t** device)
{
struct led_control_device_t *dev;

dev = (struct led_control_device_t *)malloc(sizeof(*dev));
memset(dev, 0, sizeof(*dev));

dev->common.tag =  HARDWARE_DEVICE_TAG;
dev->common.version = 0;
dev->common.module = module;
dev->common.close = led_device_close;

dev->set_on = led_on;
dev->set_off = led_off;

*device = &dev->common;

success:
return 0;
}

static struct hw_module_methods_t led_module_methods = {
open: led_device_open
};

const struct led_module_t HAL_MODULE_INFO_SYM = {
common: {
tag: HARDWARE_MODULE_TAG,
version_major: 1,
version_minor: 0,
id: LED_HARDWARE_MODULE_ID,
name: "Sample LED Stub",
author: "The Mokoid Open Source Project",
methods: &led_module_methods,
}
/* supporting APIs go here */
};


我在前面关于HAL技术的文章中已经介绍了如何写HAL stub,需要注意的只有hw_module_t和hw_device_t这两个数据结构,这里就不复述了。

下面看看JNI层代码:
struct led_control_device_t *sLedDevice = NULL;

static jboolean mokoid_setOn(JNIEnv* env, jobject thiz, jint led)
{
LOGI("LedService JNI: mokoid_setOn() is invoked.");

if (sLedDevice == NULL) {
LOGI("LedService JNI: sLedDevice was not fetched correctly.");
return -1;
} else {
return sLedDevice->set_on(sLedDevice, led);
}
}

static jboolean mokoid_setOff(JNIEnv* env, jobject thiz, jint led)
{
LOGI("LedService JNI: mokoid_setOff() is invoked.");

if (sLedDevice == NULL) {
LOGI("LedService JNI: sLedDevice was not fetched correctly.");
return -1;
} else {
return sLedDevice->set_off(sLedDevice, led);
}
}

/** helper APIs */
static inline int led_control_open(const struct hw_module_t* module,
struct led_control_device_t** device) {
return module->methods->open(module,
LED_HARDWARE_MODULE_ID, (struct hw_device_t**)device);
}

static jboolean mokoid_init(JNIEnv *env, jclass clazz)
{
led_module_t* module;

if (hw_get_module(LED_HARDWARE_MODULE_ID, (const hw_module_t**)&module) == 0) {
LOGI("LedService JNI: LED Stub found.");
if (led_control_open(&module->common, &sLedDevice) == 0) {
LOGI("LedService JNI: Got Stub operations.");
return 0;
}
}

LOGE("LedService JNI: Get Stub operations failed.");
return -1;
}

static const JNINativeMethod gMethods[] = {
{ "_init",      "()Z",  (void *)mokoid_init },
{ "_set_on",        "(I)Z", (void *)mokoid_setOn },
{ "_set_off",       "(I)Z", (void *)mokoid_setOff },
};

int register_mokoid_server_LedService(JNIEnv* env) {
static const char* const kClassName =
"com/mokoid/LedClient/LedClient";
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;
}

extern "C" 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_mokoid_server_LedService(env);

return JNI_VERSION_1_4;
}


上面的Jni代码首先通过hw_get_module得到HAL stub,open以后就可以直接使用HAL stub中定义的接口。这里还需要注意JNI_OnLoad这个函数,当Jni库被App load的时候,该函数将会自动被调用,所以我们在这里实现了注册Led Service的操作,也就是说把C/C++的接口映射到Java中去,这样在Java APP中就可以使用该接口了。在register_mokoid_server_LedService中,我们需要注意kclassname指定了需要调用该Jni库的Java
APP类 - com.mokoid.LedClient.LedClient,也就是说该Jni库只能提供给该Java程序使用。

最后是应用程序代码:
public class LedClient extends Activity {

static {
System.load("/system/lib/libmokoid_runtime.so");
}

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

// Call an API on the library.
_init();
_set_on(1);
_set_off(2);

TextView tv = new TextView(this);
tv.setText("LED 1 is on. LED 2 is off.");
setContentView(tv);
}
private static native boolean _init();
private static native boolean _set_on(int led);
private static native boolean _set_off(int led);
}


上面使用System.load来装载Jni库,当然我们也可以使用System.loadLibrary来装载,他们的唯一区别就是前者需要指定完整的路径,后者会在系统路径上(比如/system/lib/) 查找库。装载完该Jni库后,就可以使用映射后的接口了(_init, _set_on, _set_off)。

上面这种HAL的实现方式比较简单,但是也存在一个很大的问题,就是Jni库只能提供给某一个特定的Java使用,如何克服这个问题?我们可以在APP和Jni之间加一层Java service,该Jni提供给Java service使用,而所有的APP利用该service来使用Jni提供的接口。这样的话,在应用程序层,就不需要关心Jni是如何实现的了。下一篇我们会介绍这种方法。

本文出自 “Mobile
and Linux Deve..” 博客,请务必保留此出处/article/4442093.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐