Android硬件访问服务-JNI
来源:互联网 发布:泰国移动4g网络制式 编辑:程序博客网 时间:2024/05/17 09:21
JNI是Java Native Interface的缩写,它提供了若干的API实现了Java和其他语言的通信(主要是C&C++).
Android驱动开发中访问硬件有以下两种方式:
使用Java直接通过JNI访问C库,C库里面实现硬件上的Open,Read/Write,ioctl等linux驱动函数,但是这种方法有缺点,比如说一个LCD设备驱动有很多应用程序(电话、QQ、微信等)都需要访问,难道每个都要来open、read、write(/dev/fb)吗?显示是不行的,直接操作JNI访问硬件驱动只适合功能简单,代码量较小的访问,因此常用的是下面的第二种方式
使用”硬件访问服务”,并不是直接访问硬件,而是通过一个硬件访问服务来间接访问,应用程序将请求发送给硬件访问服务,由硬件访问服务通过JNI来访问我们的驱动程序,至此可以说明android=linux+封装(JNI),所以android的重点在于这个服务,对于不同的驱动需要构建不同的硬件访问服务,这种方式后面再详细描述。
下面用一个Android Studio的APP工程来简单的说明一下第一种方式的过程,APP功能非常简单,就是一个LED控制程序,直接通过JNI访问即可。
- 首先在Android Studio上建立一个Activity(可视化界面)工程,layout也非常简单,直接使用android studio提供的控件即可。每放置一个控件就会自动产生一段对应的控件源码,我们只需要修改这段源码即可,这段源码在工程的res目录下面的layout->activity_main.xml文件里面,代码如下:
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.lzf.app_0001_leddemo.MainActivity" android:orientation="vertical" android:weightSum="1">/*****************将上面的layout改为LinearLayout(并改为在垂直方向摆放)*****************/ /* 文字属性设置: * --宽度:取决于内容 * --高度:取决于内容 * --内容 * --显示:水平居中 */ <TextView android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="This is the first project!" android:layout_gravity="center_horizontal"/> /* 按键属性设置: * --ID :实现方法可以通过此ID来找到按键 * --宽度:填充整个窗口 * --高度:取决于内容 * --内容 * --双击"Button"按"Shift+F1"可查看Button操作手册 */ <Button android:id="@+id/BUTTON" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="ALL ON" /> /* 复选框设置: * --ID :实现方法可以通过此ID来找到checkbox * --宽度:填充整个窗口 * --高度:取决于内容 * --内容 * --定义一个onClick方法,选中时调用此方法,2个复选框对应同一个方法 */ <CheckBox android:id="@+id/LED1" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="LED1" android:onClick="onCheckClicked"/> <CheckBox android:id="@+id/LED2" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="LED2" android:onClick="onCheckClicked"/></LinearLayout>
代码里面涉及到的控件都是一些类对象,例如文字描述使用“TextView”,选中后使用快捷键“Ctrl+H”可以看到类的继承关系,在这里它是 “View”的子类或者继承于“View”,属于View的直接子类,像Button、ChenckBox这些是属于View的间接子类。关于控件的属性设置在代码里面都非常的显现,所以配置起来非常方便。
在Android里面访问C库:JNI
led_Ctrl(int which, int status);
led_Open();
led_Close();
以上三个定义在一个HardControl.java并声明为native方法,java代码如下:
/* 声明此文件在这个目录下面 */package com.lzf.hardlibrary;public class HardControl{ /* 定义LED控制的三个native方法 */ /* 加了static可以省略类名直接在主方法调用,不加则必须先实例化后用实例调用 */ public static native int ledCtrl(int which, int status); public static native int ledOpen(); public static native void ledClose(); /* 静态块:加了static只会执行一次,不加static则每调用一次都执行 */ /* 可以使用Crtl+Alt+T生成捕获异常的代码 */ static { try { System.loadLibrary("hardcontrol");/* 加载库,库名为hardcontrol */ } catch (Exception e) { e.printStackTrace(); } }}
接下来写一个Hardcontrol.c文件来实现上面的native方法(即JNI文件),代码如下:
#include <stdio.h>#include <jni.h> /* /usr/lib/jvm/jdk1.6.0_43/include/ */#include <stdlib.h>#include <android/log.h> /* liblog */#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <sys/ioctl.h>//添加上面的头文件后可以使用下面这个函数来打印//__android_log_print(ANDROID_LOG_DEBUG, "JNIDemo", "native add ...");static jint fd;jint ledOpen(JNIEnv *env, jobject cls){ fd = open("/dev/leds_nano", O_RDWR);//可读可写的方式打开 __android_log_print(ANDROID_LOG_DEBUG, "LEDDemo", "native ledOpen : %d", fd); if(fd >= 0) return 0; else return -1;}void ledClose(JNIEnv *env, jobject cls){ __android_log_print(ANDROID_LOG_DEBUG, "LEDDemo", "native ledClose..."); close(fd);}jint ledCtrl(JNIEnv *env, jobject cls, jint which, jint status){ __android_log_print(ANDROID_LOG_DEBUG, "LEDDemo", "fd_sta : %d", fd); int ret = ioctl(fd, status, which);//根据传入的参数控制驱动 __android_log_print(ANDROID_LOG_DEBUG, "LEDDemo", "native ledCtrl : %d, %d, %d", which, status, ret); return ret;}/* 定义JNI字段描述符 */static const JNINativeMethod methods[] = {//定义一个映射数组 {"ledOpen", "()I", (void *)ledOpen},//对应c语言的ledOpen,这个函数没有参数,返回值是int类型 {"ledClose", "()V", (void *)ledClose},//对应c语言的ledClose,这个函数没有参数,返回值是void类型 {"ledCtrl", "(II)I", (void *)ledCtrl},//对应c语言的ledCtrl,这个函数有两个int参数,返回值是int类型};/* System.loadLibrary */JNIEXPORT jint JNICALLJNI_OnLoad(JavaVM *jvm, void *reserved){ JNIEnv *env; jclass cls; /* 获得一个运行环境,JNI版本号为1.4 */ if ((*jvm)->GetEnv(jvm, (void **)&env, JNI_VERSION_1_4)) { return JNI_ERR; /* JNI version not supported */ } cls = (*env)->FindClass(env, "com/lzf/hardlibrary/HardControl");//查找路径下对应的类 if (cls == NULL) { return JNI_ERR; } /* 注册native方法 */ if((*env)->RegisterNatives(env, cls, methods, sizeof(methods)/sizeof(methods[0])) < 0)//把methods数组注册到这个环境里面的这个cls类里 { return JNI_ERR; } return JNI_VERSION_1_4;//返回版本号}
接下来是将这个JNI文件放到linux系统下使用交叉编译工具来编译,我这里使用的编译选项如下,需要根据自己出现的报错添加了几个文件路径:
arm-linux-gcc -fPIC -shared hardcontrol.c -o libhardcontrol.so
-I /usr/lib/jvm/java-1.7.0-openjdk-amd64/include/
-nostdlib /work/nanoPi_T3/android/prebuilts/ndk/9/platforms/android-21/arch-arm/usr/lib/libc.so
-I /work/nanoPi_T3/android/prebuilts/ndk/9/platforms/android-21/arch-arm/usr/include
/work/nanoPi_T3/android/prebuilts/ndk/9/platforms/android-21/arch-arm/usr/lib/liblog.so解析如下:
/*
* 第一个-I是指定jni.h目录
* -nostdlib:表明不使用标准的libc库(因为libc6.so找不到),改为后面那个路径下的libc库
* 第二个-I是指定log.h目录
* 指定使用到liblog.so的路径
*/
编译之后,把编译出来的JNI文件(hardcontrol.c)编译到APP里面去,在工程的app/libs下建立armeabi子目录,放入so文件(编译生成so库文件),放入后修改下工程目录下的build.gradle(Module:app)加上以下代码表示我们的so文件是放在libs这个目录下面的
sourceSets{ main{ jniLibs.srcDirs = ['libs'] } }
最后在控件的监听函数里面实现LED灯的控制即可,代码如下:
package com.lzf.app_0001_leddemo;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;import android.widget.CheckBox;import android.widget.Toast;import com.lzf.hardlibrary.*;/* 导入native的方法 */public class MainActivity extends AppCompatActivity { /* 用于表示LED状态 */ private boolean ledon = false; /* 记得import android.widget.Button */ /* 定义button对象 */ private Button button = null; /* 定义2个checkbox对象,对应2个led */ private CheckBox checkBoxLed1 = null; private CheckBox checkBoxLed2 = null; /* 实现一个按键监听器的类,继承于View.OnClickListener */ class MyButtonListener implements View.OnClickListener{ /* 在类里面按快捷键"Ctrl+I"会自动补全这个onClick方法 */ @Override public void onClick(View v) { HardControl hardControl = new HardControl();/* 由于静态块的关系一旦new就会调用到动态链接库 */ ledon = !ledon; /* 按下变换一次 */ if(ledon) { button.setText("ALL OFF"); checkBoxLed1.setChecked(true); checkBoxLed2.setChecked(true); /* 调用JNI实现的HardControl类来点亮LED */ HardControl.ledCtrl(0, 1); HardControl.ledCtrl(1, 1); } else { button.setText("ALL ON"); checkBoxLed1.setChecked(false); checkBoxLed2.setChecked(false); /* 熄灭 */ HardControl.ledCtrl(0, 0); HardControl.ledCtrl(1, 0); } } } /* *********** 实现onClick方法,参考CheckBox的操作手册 *********** */ public void onCheckClicked(View view){ boolean checked = ((CheckBox) view).isChecked(); /* 根据选中的ID来判断选中的是哪一个复选框 */ switch (view.getId()){ case R.id.LED1: if(checked) { /* 使用Toast提醒文字 */ Toast.makeText(getApplicationContext(),"LED1 on", Toast.LENGTH_SHORT).show(); HardControl.ledCtrl(0, 1); } else{ Toast.makeText(getApplicationContext(),"LED1 off", Toast.LENGTH_SHORT).show(); HardControl.ledCtrl(0, 0); } break; case R.id.LED2: if(checked) { Toast.makeText(getApplicationContext(),"LED2 on", Toast.LENGTH_SHORT).show(); HardControl.ledCtrl(1, 1); } else{ Toast.makeText(getApplicationContext(),"LED2 off", Toast.LENGTH_SHORT).show(); HardControl.ledCtrl(1, 0); } break; } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); /* * 打开led设备 * 快捷键:Crtl+B 调到定义处 */ HardControl.ledOpen(); /* * 找到按键并返回一个类 * 快捷键:Ctrl+Shift+空格 可以自动补齐强制转换的类型"(Button)" * Alt+F7 可以找到BUTTON的引用地方,定义了一个BUTTON变量 * Alt+Enter 可以找到相应的包import进来 */ button = (Button)findViewById(R.id.BUTTON); /* 设置按键监听器,当按键按下时MyButtonListener类里面的方法会被调用 */ button.setOnClickListener(new MyButtonListener()); /* 根据ID找到控件获得实例化对象,跟上面的button类似 */ checkBoxLed1 = (CheckBox)findViewById(R.id.LED1); checkBoxLed2 = (CheckBox)findViewById(R.id.LED2); }}
APP界面如下:
整个过程如下图所示:
- Android硬件访问服务-JNI
- Android硬件访问服务学习之(一)Android通过JNI访问硬件
- Android系统之APP访问硬件--------JNI与硬件访问服务0001
- Android 硬件访问服务
- Android硬件访问服务
- Android硬件抽象层编写JNI方法提供Java访问硬件服务接口
- 实现硬件访问服务的JNI方法
- 开发Android硬件访问服务
- 开发Android硬件访问服务
- Android 编写硬件访问服务
- Android添加硬件访问服务
- Android硬件访问服务框架
- Android硬件访问服务-Service
- Android硬件访问服务-HAL
- Android系统中硬件访问服务框架(JNI HAL)及实例
- Android下Java通过JNI访问硬件
- 在Ubuntu为Android硬件抽象层(HAL)模块编写JNI方法提供Java访问硬件服务接口
- 为Android硬件抽象层(HAL)模块编写JNI方法提供Java访问硬件服务接口
- HTML5的基础讲解2.2
- 分布式和集群的区别
- oracle exists和not exists的使用
- windows下 apache+php+mysql环境搭建
- myelipse的team没有提交等选项:
- Android硬件访问服务-JNI
- Mysql常用SQL语句集锦
- 20170723日常总结
- 自底向上分析——LR方法
- Codevs1074:食物链——题解
- 求解最长递增子序列的长度
- zoj3886(线段树,区间取模)
- 编码转换
- 频繁项挖掘-Apriori算法