1、java APP
定义了三个native方法:getVal(),setVal(int val),init_jni().
package com.android.zhang.test;
import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
public class JNIZhangFangTestActivity extends Activity {
private static final String Tag = "zhangfang_JNI_TEST";
private native int getVal();
private native void setVal(int val);
private native void init_jni();
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
init_jni();
}
static {
System.loadLibrary("zf");
}
public boolean onCreateOptionsMenu(Menu menu)
{
menu.add(0, 0, 0, "getVal");
menu.add(1, 1, 1, "setVal");
menu.add(2, 2, 2, "clear");
return true;
}
public boolean onOptionsItemSelected(MenuItem item)
{
int item_id=item.getItemId();
switch(item_id){
case 0:{
getVal();
break;
}
case 1:{
setVal(20);
break;
}
case 2:{
setVal(0);
break;
}
}
return true;
}
}
2、JNI层
com_android_zhang_test_JNIZhangFangTestActivity.h
关于如何实现JNI,请参照android JNI实现步骤
该文件实现了三个native方法,注意native方法的命名,与上面APP的函数相对应。
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_android_zhang_test_JNIZhangFangTestActivity */
#ifndef _Included_com_android_zhang_test_JNIZhangFangTestActivity
#define _Included_com_android_zhang_test_JNIZhangFangTestActivity
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_android_zhang_test_JNIZhangFangTestActivity
* Method: getVal
* Signature: ()I
*/
JNIEXPORT jint JNICALL Java_com_android_zhang_test_JNIZhangFangTestActivity_getVal
(JNIEnv *, jobject);
/*
* Class: com_android_zhang_test_JNIZhangFangTestActivity
* Method: setVal
* Signature: (I)V
*/
JNIEXPORT void JNICALL Java_com_android_zhang_test_JNIZhangFangTestActivity_setVal
(JNIEnv *, jobject, jint);
/*
* Class: com_android_zhang_test_JNIZhangFangTestActivity
* Method: init_jni
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_android_zhang_test_JNIZhangFangTestActivity_init_1jni
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
com_android_zhang_test_JNIZhangFangTestActivity.c
实现native方法。在hello_init中去找到抽象层定义的hello_module_t模块,并打开该设备,进行一系列初始化。hello_setVal(int val) 和 hello_getVal() 都是调用抽象层中定义的方法。
#include "com_android_zhang_test_JNIZhangFangTestActivity.h"
#define LOG_TAG "Zhangfang_JNI"
#include <utils/Log.h>
#include <JNIHelp.h>
//#include <android_runtime/AndroidRuntime.h>
//#include <utils/misc.h>
#include <hardware/hardware.h>
#include <stdio.h>
//#include "../../../hardware/libhardware/include/hardware/zf.h"
#include <hardware/zf.h>
//namespace android
//{
/*硬件抽象层定义的硬件访问结构体,可通过该结构体与硬件进行交互*/
struct hello_device_t* hello_device = NULL;
int hw_get_module(const char *id,const struct hw_module_t **module);
static int hello_getVal();
static void hello_setVal(int value);
static int hello_init();
JNIEXPORT jint JNICALL Java_com_android_zhang_test_JNIZhangFangTestActivity_getVal
(JNIEnv *env, jobject obj)
{
return hello_getVal();
}
JNIEXPORT void JNICALL Java_com_android_zhang_test_JNIZhangFangTestActivity_setVal
(JNIEnv *env, jobject obj, jint val)
{
hello_setVal(val);
}
JNIEXPORT void JNICALL Java_com_android_zhang_test_JNIZhangFangTestActivity_init_1jni
(JNIEnv *env, jobject obj)
{
if(hello_init() ==0){
LOGI("ZhangFang JNI: hello_init successfully!");
}else{
LOGI("ZhangFang JNI: hello_init failed!");
}
}
jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
void *venv;
LOGI("JNI_OnLoad!");
if ((*vm)->GetEnv(vm, (void**)&venv, JNI_VERSION_1_4) != JNI_OK) {
LOGE("ERROR: GetEnv failed");
return -1;
}
return JNI_VERSION_1_4;
}
static void hello_setVal(int value)
{
int val = value;
LOGI("Zhangfang JNI: set value %d to device.",val);
if(!hello_device){
LOGI("Zhangfang JNI: device is not open.");
return ;
}
hello_device->set_val(hello_device,val);
}
/*获取硬件寄存器的值*/
static int hello_getVal()
{
int val = 0;
if(!hello_device){
LOGI("Zhangfang JNI: device is not open.");
return val;
}
hello_device->get_val(hello_device,&val);
LOGI("Zhangfang JNI: get value %d from device.",val);
return val;
}
static inline int hello_device_open(const hw_module_t* module,struct hello_device_t** dev)
{
return module->methods->open(module,HELLO_HARDWARE_MODULE_ID,(struct hw_device_t**)dev);
}
/*通过硬件模块ID来加载指定硬件抽象层模块,并打开硬件*/
static int hello_init()
{
struct hello_module_t *module;
LOGI("Zhangfang JNI: initializing.......");
if(hw_get_module(HELLO_HARDWARE_MODULE_ID,(const struct hw_module_t**)&module)==0)
{
LOGI("Zhangfang JNI: hello Stub found.");
if(hello_device_open(&(module->common),&hello_device) == 0)
{
LOGI("Zhangfang: hello device is open.");
return 0;
}
LOGE("Zhangfang: failed to open hello device.");
return -1;
}
LOGE("Zhangfang JNI: failed to found hello Stub.");
return -1;
}
Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES := com_android_zhang_test_JNIZhangFangTestActivity.c
LOCAL_C_INCLUDES := $(JNI_H_INCLUDE)
LOCAL_MODULE := libzf
LOCAL_SHARED_LIBRARIES := libutils libhardware
LOCAL_PRELINK_MODULE := false
LOCAL_LDLIBS := -llog
include $(BUILD_SHARED_LIBRARY)
3、HAL层
vi /hardware/libhardware/include/hello.c#ifndef ANDROID_HELLO_INTERFACE_H
#define ANDROID_HELLO_INTERFACE_H
#include <hardware/hardware.h>
__BEGIN_DECLS
#define HELLO_HARDWARE_MODULE_ID "hello"
/*硬件模块结构体*/
struct hello_module_t {
struct hw_module_t common;
};
/*硬件设备接口结构体*/
struct hello_device_t {
struct hw_device_t common;
int fd;
int (*set_val)(struct hello_device_t* dev,int val);
int (*get_val)(struct hello_device_t* dev,int *val);
};
__END_DECLS
#endif
vi /hardware/libhardware/modules/hello/hello.c
#define LOG_TAG "HelloStub" #include <hardware/hardware.h> #include <hardware/hello.h> #include <fcntl.h> #include <errno.h> #include <cutils/log.h> #include <cutils/atomic.h> #define DEVICE_NAME "/dev/hello" #define MODULE_NAME "hello" #define MODULE_AUTHOR "zhangfanghn@gmail.com" /*打开和关闭设备*/ static int hello_device_open(const struct hw_module_t* module,const char* name,struct hw_device_t** device); static int hello_device_close(struct hw_device_t* device); /*设备访问接口*/ static int hello_get_val(struct hello_device_t* dev,int* val); static int hello_set_val(struct hello_device_t* dev,int val); /*模块方法表*/ static struct hw_module_methods_t hello_module_methods = { open:hello_device_open }; struct hello_module_t HAL_MODULE_INFO_SYM = { /*模块名称必须为这个*/ common:{ tag: HARDWARE_MODULE_TAG, /*必须是这个*/ version_major: 1, version_minor: 0, id: HELLO_HARDWARE_MODULE_ID, //向系统注册了一个ID为HELLO_HARDWARE_MODULE_ID的stub name: MODULE_NAME, author: MODULE_AUTHOR, methods: &hello_module_methods, } }; static int hello_device_open(const struct hw_module_t* module,const char*name,struct hw_device_t** device) { struct hello_device_t* dev; dev = (struct hello_device_t*)malloc(sizeof(struct hello_device_t)); if(!dev){ LOGE("Hello Stub : failed to alloc space!"); return -EFAULT; } memset(dev,0,sizeof(struct hello_device_t)); dev->common.tag = HARDWARE_DEVICE_TAG;/*这个必须为这个*/ dev->common.version = 0; dev->common.module = (hw_module_t*)module; dev->common.close = hello_device_close; dev->set_val = hello_set_val; dev->get_val = hello_get_val; if((dev->fd = open(DEVICE_NAME,O_RDWR))== -1) { LOGE("Hello Stub : faild to open %s: %s",DEVICE_NAME,strerror(errno)); free(dev); return -EFAULT; } *device = &(dev->common); LOGI("Hello Stub: open %s successfully.",DEVICE_NAME); return 0; } static int hello_device_close(struct hw_device_t* dev) { struct hello_device_t* hello_device = (struct hello_device_t*)dev; if(!hello_device){ close(hello_device->fd); free(hello_device); } return 0; } static int hello_set_val(struct hello_device_t* dev,int val) { LOGI("Hello Stub: set value %d to device.",val); write(dev->fd,&val,sizeof(val)); return 0; } static int hello_get_val(struct hello_device_t* dev,int *val) { if(!val){ LOGE("Hello Stub: error val pointer."); return -EFAULT; } read(dev->fd,val,sizeof(*val)); LOGI("Hello Stub: get value %d from device.",*val); return 0; }
android.mk
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE_TAGS := optional LOCAL_PRELINK_MODULE := false LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw LOCAL_SHARED_LIBRARIES := liblog LOCAL_SRC_FILES := hello.c LOCAL_MODULE := hello.default include $(BUILD_SHARED_LIBRARY)
4、驱动层
省略了。
修改init.rc文件,可开机加载内核驱动。
on boot # basic network init ifup lo hostname localhost domainname localdomain # set RLIMIT_NICE to allow priorities from 19 to -20 setrlimit 13 40 40 # basic kernel modules insmod /develop/hello.ko # mknod /dev/zf c 248 0
修改ueventd.rc,可设置设备的访问权限。因为应用程序一般不具备访问硬件的权限,所以需要给硬件增加相应的访问权限。
/dev/hello 0666 root root
5、出现的错误
编译JNI模块时出现如下错误:
undefined reference to 'hw_get_module'
collect2: ld returned 1 exit status
错误原因:
hw_get_module找不到,为什么会找不到呢?不是包含头文件#include <hardware/hardware.h>了吗?
其实,其真实原因是使用了hw_get_module这个函数,在链接的时候需要到libhardware.so库中去找,但是在Android.mk中却没有包含libhareware.so库,所以在链接的时候报错。
解决方法:
在Android.mk中加入该库即可:
LOCAL_SHARED_LIBRARIES := libutils libhardware
6、执行结果
I/ActivityManager( 1233): Start proc com.android.zhang.test for activity com.android.zhang.test/.JNIZhangFangTestActivity: pid=1701 uid=10031 gids={} D/dalvikvm( 1701): Debugger has detached; object registry had 1 entries I/Zhangfang_JNI( 1701): JNI_OnLoad! I/Zhangfang_JNI( 1701): Zhangfang JNI: initializing....... I/Zhangfang_JNI( 1701): Zhangfang JNI: hello Stub found. I/ZhangFangStub( 1701): ZhangFang Stub: open /dev/zf successfully. I/Zhangfang_JNI( 1701): Zhangfang: hello device is open. I/Zhangfang_JNI( 1701): ZhangFang JNI: hello_init successfully! I/ActivityManager( 1233): Displayed com.android.zhang.test/.JNIZhangFangTestActivity: +151ms D/dalvikvm( 1319): GC_EXPLICIT freed 63K, 48% free 3027K/5767K, external 3312K/4113K, paused 25ms W/KeyCharacterMap( 1701): No keyboard for id 0 W/KeyCharacterMap( 1701): Using default keymap: /system/usr/keychars/qwerty.kcm.bin I/Zhangfang_JNI( 1701): Zhangfang JNI: set value 0 to device. I/ZhangFangStub( 1701): ZhangFang Stub: set value 0 to device. W/InputManagerService( 1233): Window already focused, ignoring focus gain of: com.android.internal.view.IInputMethodClient$Stub$Proxy@407848c0 I/Zhangfang_JNI( 1701): Zhangfang JNI: set value 20 to device. I/ZhangFangStub( 1701): ZhangFang Stub: set value 20 to device. W/InputManagerService( 1233): Window already focused, ignoring focus gain of: com.android.internal.view.IInputMethodClient$Stub$Proxy@40852fa0 I/ZhangFangStub( 1701): ZhangFang Stub: get value 20 from device. I/Zhangfang_JNI( 1701): Zhangfang JNI: get value 20 from device.