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.