android+硬件服务,Android硬件访问服务(一)

JNI

JNI即Java Native Interface.它是Java访问C/C++的接口。我们知道Java是平台无关的语言,那为什么要创建一个和Native相关的语言呢,这不就破会了Java平台无关的特性吗?

虽然Java很强大,几乎无所不能。但在对性能要求比较高的环境中,Java就比C/C++逊色多了,因为毕竟需要虚拟机嘛。而且,有些Linux系统调用Java是不支持的,比如ioctl,只能C/C++才能调用。那么Java怎么调用Native层的C/C++代码呢?这就需要用到JNI技术。其实JNI技术的核心就是建立Java层与Native层的映射关系,记住这一点。Java调用C的过程大概分三步。

加载C库

Java层中调用System.loadLibrary("XXX")函数,一般是在静态代码块中调用这个函数。XXX是库的名字,比如库名是libtest.so.那么xxx就是test。

建立映射

有两种方法建立Java与C的映射关系,一种是所谓静态的,另外一种当然是动态的。静态映射的方法比较简单。

javac Test.java

javah Test//会生成一个头文件

然后根据头文件填充本地C函数。

动态映射稍微复杂点,Android系统中使用的就是这种方法,下面会详细介绍。

实战

下面就以一个例子来介绍Java如何通过JNI来操作LED的。实验使用的是IMX6Q平台,Android6.0。

首先看看内核驱动层,LED在硬件上很简单,就是通过拉高拉低GPIO来点亮和熄灭LED。

#define IMX_GPIO_NR(port, index)((((port)-1)*32)+((index)&31))static int led_major = 211;

struct led_gpio_desc {

int gpio;

char *name;

};

static struct led_gpio_desc led_gpio [] = {

{

.gpio = IMX_GPIO_NR(3, 21),

.name = "led4",

},

{

.gpio = IMX_GPIO_NR(3, 22),

.name = "led5",

},

{

.gpio = IMX_GPIO_NR(3, 23),

.name = "led6",

},

};

static int led_gpio_open(struct inode *inode , struct file *filp)

{

int ret;

int i;

for (i = 0; i < 3; i++) {

if (gpio_is_valid(led_gpio[i].gpio)){

ret = gpio_request(led_gpio[i].gpio, led_gpio[i].name);

if (ret) {

printk(KERN_ERR "led gpio request failed.\n");

return -1;

}

}

}

return 0;

}

static long led_gpio_ctrl (struct file *file, unsigned int cmd, unsigned long args)

{

gpio_direction_output(led_gpio[args].gpio, cmd);

return 0;

}

static const struct file_operations led_gpio_fops = {

.owner = THIS_MODULE,

.open = led_gpio_open,

.unlocked_ioctl = led_gpio_ctrl,

};

static int led_gpio_init(void)

{

int ret;

ret = register_chrdev(led_major, "led_gpio", &led_gpio_fops);

if (ret < 0) {

printk(KERN_ERR "led_gpio: can't get major %d.\n", led_major);

return ret;

}

}

static void led_gpio_exit(void)

{

unregister_chrdev(led_major, "led_gpio");

}

可以看到代码很简单,就是一个字符设备驱动程序,核心就是通过ioctrl系统调用来操作LED。

接下来看看JAVA怎么操作到LED的,先看JNI层。

#include /* /usr/lib/jvm/java-1.7.0-openjdk-amd64/include/ */#include

#include

#include

#include

#include

#include

#include /* liblog */

#define TAG "LedGPIO"// 定义info信息#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,TAG,__VA_ARGS__)// 定义debug信息#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)// 定义error信息#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,TAG,__VA_ARGS__)

#if 0

typedef struct {

char *name; /* Java里调用的函数名 */

char *signature; /* JNI字段描述符, 用来表示Java里调用的函数的参数和返回值类型 */

void *fnPtr; /* C语言实现的本地函数 */

} JNINativeMethod;

#endifstatic jint fd = 0;

jint led_open(JNIEnv *env, jobject cls)

{

fd = open("/dev/gpiodev", O_RDWR);

if (fd < 0)

{

LOGE("open /dev/gpiodev error\n");

return -1;

}

LOGD("native open\n");

return 0;

}

void led_close(JNIEnv *env, jobject cls)

{

close(fd);

LOGD("native close\n");

}

jint led_ctrl(JNIEnv *env, jobject cls, jint which, jint status)

{

LOGD("native ctrl which = %d, status = %d\n", which, status);

ioctl(fd, status, which);

return 0;

}

static const JNINativeMethod methods[] = {

{"ledOpen", "()I", (void *)led_open},

{"ledClose", "()V", (void *)led_close},

{"ledCtrl", "(II)I", (void *)led_ctrl},

};

/* System.loadLibrary */

JNIEXPORT jint JNICALL

JNI_OnLoad(JavaVM *jvm, void *reserved)

{

JNIEnv *env;

jclass cls;

if ((*jvm)->GetEnv(jvm, (void **)&env, JNI_VERSION_1_4)) {

return JNI_ERR; /* JNI version not supported */

}

cls = (*env)->FindClass(env, "com/example/jason/led_gpio/led_ctrl");

if (cls == NULL) {

return JNI_ERR;

}

/* 2. map java hello c c_hello */

if ((*env)->RegisterNatives(env, cls, methods, sizeof(methods)/sizeof(methods[0])) < 0)

return JNI_ERR;

return JNI_VERSION_1_4;

}

下面简单分析下,当Java层调用System.loadLibrary时,JNI层的JNI_OnLoad就会被调用。在JNI_OnLoad中,通过FindClass把Java层的led_ctrl类绑定起来,不然怎么知道JNI中提供的方法是给哪个类使用的呢?

JNI代码的编译可把我折腾了一番。直接贴一个编译命令

arm-linux-gnueabihf-gcc -fPIC -shared led_ctrl.c -o libled_ctrl.so -I/usr/lib/jvm/java-1.7.0-openjdk-amd64/include/ -nostdlib /work/android-6.0.1-2.1.0/prebuilts/ndk/9/platforms/android-21/arch-arm/usr/lib/libc.so -I/work/android-6.0.1-2.1.0/prebuilts/ndk/9/platforms/android-21/arch-arm/usr/include/ /work/android-6.0.1-2.1.0/prebuilts/ndk/9/platforms/android-21/arch-arm/usr/lib/liblog.so -mfloat-abi=softfp

如果不加-nostdlib /work/android-6.0.1-2.1.0/prebuilts/ndk/9/platforms/android-21/arch-arm/usr/lib/libc.so APK会报java.lang.UnsatisfiedLinkError: dlopen failed: library “libc.so.6” not found

liblog.so主要是为了在JNI中添加打印调试信息。

-mfloat-abi=softfp 由于我使用的编译工具链配置成立了hard-float ABI。而C库貌似使用的是soft-float ABI.

所以这里加上这个参数,以避免出现XXX uses VFP register arguments, XXX.so does not have的错误

然后就是建立映射关系了,在methods数组中就将Java层和Native建立起联系了。那Java层的代码又是怎么调用的呢?首先声明led_ctrl类,由于里面的本地方法都是static的,所以不需要实例话对象,就可以直接调用ledCtrl来操作LED了,是不是很简单?

public class led_ctrl {

public static native int ledOpen();

public static native void ledClose();

public static native int ledCtrl(int which, int status);

static {

System.loadLibrary("led_ctrl");

}

}

在测试之前还有几步,由于我们的驱动程序不会自动创建设备节点,需要手动创建,而且还需要访问权限

root@sabresd_6dq:/ mknod /dev/gpiodev c 211 0

root@sabresd_6dq:/ chmod 666 /dev/gpiodev

下面是测试结果。

72c54089adf540c7b2c1ea1438cda4b7.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值