Android底层开发之JNI编程

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 <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>

#define LOG_TAG "led_jni_log"
#include <utils/Log.h>

#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)

猜你喜欢

转载自blog.csdn.net/weixin_37787043/article/details/79357160