Android中JNI编程—-android应用是如何与驱动进行交互
a, java代码调用jni的接口
b, jni代码的实现
jni: java native interface,java本地接口
或者是: java调用c/c++代码的接口
图1 JNI的实现原理
——————————————————————————————————————
app中: 与jni交互的接口;
1, 加载动态库
static{//优先执行
System.loadLibrary(“led_jni”); // /system/lib/libled_jni.so
}
2, 声明本地方法,名字和参数都是自定义
public native int openDev();
public native int devOn();
public native int devOff();
public native int closeDev();
3, 调用本地方法
LedNative myled;
myled = new LedNative();
myled.openDev();
接下来在eclipse中写相关代码:(功能:实现点灯)
创建new project
在UI中添加2个button
包名:com.example.ledcontrol
类:/LedControl/src/com/example/ledcontrol/LedActivity.java
package com.example.ledcontrol;
//这个程序不能直接在安卓模拟器运行
import com.example.lowlevel.LedNative;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.util.Log;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;
//外加设置监听事件
public class LedActivity extends Activity implements OnClickListener{
final String TAG = "LedActivity";//写log
Button btn_led_on;
Button btn_led_off;
LedNative myled;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_led);
initUI();
// 3.调用本地方法
myled = new LedNative();
myled.openDev(); //这里有4个方法,这是其中之一
}
//实现
private void initUI() {
// TODO Auto-generated method stub
btn_led_on = (Button) findViewById(R.id.btn_led_on);//初始化
btn_led_on.setOnClickListener(this);//设置监听自己
btn_led_off = (Button) findViewById(R.id.btn_led_off);
btn_led_off.setOnClickListener(this);
}
@Override
//处理UI
public void onClick(View v) {
// TODO Auto-generated method stub
switch (v.getId()) {
case R.id.btn_led_on:
Toast.makeText(LedActivity.this, "亮灯啦-----", 1000).show();//显示效果
Log.d(TAG, "好亮啊-----");
myled.devOn();
break;
case R.id.btn_led_off:
Toast.makeText(LedActivity.this, "灭灯啦-----", 1000).show();
Log.d(TAG, "天黑请闭眼");
myled.devOff();
break;
default:
break;
}
}
@Override
protected void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
myled.closeDev();
}
}
包名:com.example.lowlevel
类:/LedControl/src/com/example/lowlevel/LedNative.java
package com.example.lowlevel;
//这个程序不能直接在安卓模拟器运行
public class LedNative {
//1.加载动态库
static{ //优先执行
System.loadLibrary("led_jni");// /system/lib/libled_jni.so
}
//2, 声明本地方法,名字和参数都是自定义
public native int openDev();//在底层中先打开
public native int devOn();//控制过程
public native int devOff();
public native int closeDev();//最后关闭
}
——————————————————————————————————————————————————
在Ubuntu中去编写jni: 用sourceinsight
准备步骤:先把si_android404-android.tgz拷贝到目录下,并解压tar -xvf si_android404-android.tgz
接下来编写led_jni.cpp
编写方法:参考模板—development/samples/SimpleJNI/jni
实现JNI_onLoad()
jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
//获取到环境变量对象---提供各种操作方法---注册方法
JNIEnv *env = NULL;
jint vm->GetEnv(void * * env,jint version)
//构建映射表,注册给dvm
typedef struct {
const char* name; //java的方法名
const char* signature; //方法的描述
void* fnPtr;//c/c++的函数名
} JNINativeMethod;
// 参数1-- java方法所在的包路径信息--通过env->FindClass得到
//参数2---映射表
//参数3--映射表中的项目个数
//返回值--错误为负数
jint env->RegisterNatives(jclass clazz,const JNINativeMethod * methods,jint nMethods)
}
代码led_jni.cpp
#include
#include
#include
#include
#include
#include
#define LOG_TAG "led_jni_log"
#include
#include "jni.h"
static jint fd;
jint open_led(JNIEnv *env, jobject thiz)//记住参数
{
LOGD("-----------%s--------------\n", __FUNCTION__);
fd = open("/dev/led1", O_RDWR);
if(fd < 0)
{
LOGE("open error : %s\n", strerror(errno));
return -1;
}
return 0;
}
jint led_on(JNIEnv *env, jobject thiz)
{
LOGD("-----------%s--------------\n", __FUNCTION__);
jint on = 1;
jint ret;
ret = write(fd, &on, 4);//第3个参数表示4字节
if(ret < 0)
{
LOGE("write on error : %s\n", strerror(errno));
return -1;
}
return 0;
}
jint led_off(JNIEnv *env, jobject thiz)
{
LOGD("-----------%s--------------\n", __FUNCTION__);
jint on = 0;
jint ret;
ret = write(fd, &on, 4);
if(ret < 0)
{
LOGE("write off error : %s\n", strerror(errno));
return -1;
}
return 0;
}
jint close_led(JNIEnv *env, jobject thiz)
{
LOGD("-----------%s--------------\n", __FUNCTION__);
close(fd);
return 0;
}
//映射表数组
const JNINativeMethod led_jni_methods[] = {
{"openDev", "()I", (void*)open_led},
{"devOn", "()I", (void*)led_on},
{"devOff", "()I", (void*)led_off},
{"closeDev", "()I", (void*)close_led},
};
//实现JNI_onLoad()
//JNI_OnLoad返回值--正确为JNI_VERSION_1_4,错误为负数
jint JNI_OnLoad(JavaVM * vm,void * reserved)
{
LOGD("-----------%s--------------\n", __FUNCTION__);
JNIEnv *env = NULL;
jint ret;
//1.获取到环境变量对象---提供各种操作方法---注册方法
// 参数1--被初始化的env
//参数2--jni的版本
//返回值--正确为0,错误为负数
ret = vm->GetEnv((void * * )&env, JNI_VERSION_1_4);
if(ret != JNI_OK)
{
LOGE("vm->GetEnv error\n");
return -1;
}
//2.构建映射表,注册给dvm
jclass clz = env->FindClass("com/example/lowlevel/LedNative");
if(clz == NULL)
{
LOGE("env->FindClass error\n");
return -1;
}
// 参数1-- java方法所在的包路径信息--通过env->FindClass得到
//参数2---映射表
//参数3--映射表中的项目个数
//返回值--错误为负数
ret = env->RegisterNatives(clz,
led_jni_methods,
sizeof(led_jni_methods)/sizeof(led_jni_methods[0]));
if(ret < 0)
{
LOGE("env->RegisterNatives error\n");
return -1;
}
return JNI_VERSION_1_4;
}
代码Android.mk
1 LOCAL_PATH:= $(call my-dir)
2 include $(CLEAR_VARS)
3
4 LOCAL_MODULE_TAGS := optional
5
6 #编译后的目标文件,一定要和System.loadLibrary()名字保持一致
7 LOCAL_MODULE := libled_jni
8
9 #指定的原文件
10 LOCAL_SRC_FILES := led_jni.cpp
11
12 #因为使用了LOGD
13 LOCAL_SHARED_LIBRARIES := libutils
14
15 #因为使用了jni.h, LOCAL_C_INCLUDES用于jni.h的所在路径
16 LOCAL_C_INCLUDES += $(JNI_H_INCLUDE)
17
18 #把目标文件编译成动态库
19 include $(BUILD_SHARED_LIBRARY)
6 #编译后的目标文件,一定要和System.loadLibrary()名字保持一致
如图:
还要注意:JNI_H_INCLUDE
接下来执行程序
每次打开一个终端,先执行以下命令:
接着编译:
可以看到目标文件libled_jni.so安装到/system/lib…里面(这是安卓默认的路径,apk一般编译到system/app里面)
接着编写驱动程序 ko
需要用Makefile编译
在src_210目录下创建mydrv/led_drv/Makefile
ifeq ($(KERNELRELEASE),)
KERNELDIR = /home/george/src_210/linux-3.0.8-FS210
PWD =$(shell pwd)
modules:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules
#arm-none-linux-gnueabi-gcc hello_test.c -o hello_test
#cp hello_test hello.ko /opt/filesystem/s5pv210
modules_install:
$(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install
clean:
rm -rf *_test *.so *.o *.ko .tmp_versions *.mod.c *.order *.symvers
else
obj-m :=s5pv210_led.o
endif
最后是运行:
apk—>安装到开发板
1,自动安装—/system/lib
2,手动安装—/mnt/sdcard/(需要重启)
vim init.fs210.rc
7 mkdir /mnt/sdcard 0777 system system
8 mkdir /mnt/ext_sd 0777 system system
9 mkdir /mnt/usb 0777 system system
jni.so ---> /system/lib
cp -raf out/target/product/fs210/system/lib/libled_jni.so /opt/rootfs_dir/system/lib
drv.ko --> /system/lib/modules/
cp -raf s5pv210_led.ko /opt/rootfs_dir/system/lib/modules/
接着把制作好的apk放到/opt/rootfs_dir/mnt/sdcard中
在开发板中:
logcat -c
logcat
I/ActivityManager( 2213): START {act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 pkg=com.hq.ledcontrol cmp=com.hq.ledcontrol/.LedActivity} from pid 2884
D/dalvikvm( 3010): Not late-enabling CheckJNI (already on)
E/AudioHardware( 2139): AudioStreamOutALSA::write END WITH ERROR !!!!!!!!!(0x4667d0, 4096)
D/dalvikvm( 2213): GC_CONCURRENT freed 424K, 8% free 9263K/9991K, paused 14ms+45ms
I/ActivityManager( 2213): Start proc com.hq.ledcontrol for activity com.hq.ledcontrol/.LedActivity: pid=3010 uid=10053 gids={}
D/OpenGLRenderer( 2884): Flushing caches (mode 0)
D/led_jni_log( 3010): ———–JNI_OnLoad————–
D/led_jni_log( 3010): ———–open_led————–
E/led_jni_log( 3010): open error : No such file or directory // 驱动没有安装
解决:
/ # insmod /system/lib/modules/s5pv210_led.ko
再次启动:
/ # logcat
——— beginning of /dev/log/system
I/ActivityManager( 2213): START {act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=com.hq.ledcontrol/.LedActivity} from pid 2487
——— beginning of /dev/log/main
D/dalvikvm( 2213): GC_FOR_ALLOC freed 317K, 10% free 9181K/10119K, paused 31ms
D/led_jni_log( 3010): ———–open_led————–
E/led_jni_log( 3010): open error : Permission denied //权限有问题
I/ActivityManager( 2213): Displayed com.hq.ledcontrol/.LedActivity: +240ms
W/IInputConnectionWrapper( 3010): showStatusIcon on inactive InputConnection
D/OpenGLRenderer( 2487): Flushing caches (mode 1)
D/OpenGLRenderer( 2487): Flushing caches (mode 0)
W/InputManagerService( 2213): Starting input on non-focused client com.android.internal.view.IInputMethodClientProxy@4139d230 (uid=10028 pid=2487)
D/LedActivity( 3010): 亮瞎了狗眼
D/led_jni_log( 3010): ———–led_on————–
E/led_jni_log( 3010): write on error : Bad file number
解决:
/ # chmod 777 /dev/led1
再次重启应用程序:
总结,每次打开应用程序都要装载驱动,修改权限,不方便,这时,我们可以
自动装载ko和修改权限: init.rc(/opt/rootfs_dir/)
283 insmod /system/lib/modules/s5pv210_led.ko
284 chmod 777 /dev/led1
285
286
287 on boot
288 # basic network init
程序链接地址:LED_CODE.zip(链接:https://pan.baidu.com/s/1dAGY2y 密码:os15)