Android应用程序通过JNI调用驱动程序(友善Smart210)
实现目标:
写一个简单的测试smart210上LED的应用程序,应用程序通过JNI调用Android系统下的Linux内核中的LED的驱动程序,实现在应用程序上控制开发板上4个LED的目的。
开发环境:
win7 32位的系统;
开发板:友善Smart210(s5pv210);
Android版本:Android-4.0.3;
Linux内核版本:Linux-3.0.8
环境搭建以及完成JNI部分:
这里假设你的电脑上已经装好了开发应用程序的环境,其中Android sdk的下载地址为http://developer.android.com/sdk/index.html 根据自己电脑的系统来下载适合自己的sdk吧。
ndk的简介:(参考网络博客)
NDK全称:Native Development Kit。
1、NDK是一系列工具的集合。
NDK提供了一系列的工具,帮助开发者快速开发C(或C++)的动态库,并能自动将so和java应用一起打包成apk。这些工具对开发者的帮助是巨大的。
NDK集成了交叉编译器,并提供了相应的mk文件隔离CPU、平台、ABI等差异,开发人员只需要简单修改mk文件(指出“哪些文件需要编译”、“编 译特性要求”等),就可以创建出so。
NDK可以自动地将so和Java应用一起打包,极大地减轻了开发人员的打包工作。
2、NDK提供了一份稳定、功能有限的API头文件声明。
Google明确声明该API是稳定的,在后续所有版本中都稳定支持当前发布的API。从该版本的NDK中看出,这些API支持的功能非常有限,包含有:C标准库(libc)、标准数学库(libm)、压缩库(libz)、Log库(liblog)。
对于Windows环境下NDK的开发,如果使用的NDK是r7之前的版本,必须要安装Cygwin才能使用NDK,所以为Eclipse需要配置的builder,其实是执行Cygwin,然后传递ndk-build作为参数。在NDKr7开始,Google的Windows版的NDK提供了一个ndk-build.cmd的脚本,这样,就可以直接利用这个脚本编译,而不需要使用Cygwin了。只需要为Eclipse Android工程添加一个Builders,就能让Eclipse自动编译NDK。
现在我们来讲解怎么搭建ndk的环境,ndk的下载地址为:https://developer.android.com/tools/sdk/ndk/index.html 下载后解压相应的ndk,我解压在D盘的android-ndk目录下,解压后的的文件为android-ndk-r9c,如下图:
1.解压完ndk后,接下来我们来在Eclipse中怎么把ndk的部分设置进去,首先打开Eclipse,创建一个新工程,我去的工程名字为:LEDAPP,包的名字为:com.ndk.led,如下图:
至于具体怎么在Eclipse下建立Android的工程,网上很多资料,只是这里需要注意,因为后边要用到所以专门弄出来说说。
2.在新建立的工程里面建立一个jni文件夹,该文件存放ndk需要编译的文件,具体为:在所建工程的名字LEDAPP上右键->New->folder,然后写入jni的名字,完成后,如图下图:
3.建立并配置Builder
(a)右键LEDAPP->Properties->Builders,如下图:
(b)点击右边的New,如下图:
(c)然后点击"Program",并点击OK按钮,出现下图:
(d)在弹出的【EditConfiguration】对话框中,配置选项卡【Main】。
在“Name“中输入新builders的名称(我取名为ndk_Builder)。
在“Location”中输入nkd-build.cmd的路径。
(我的是D:\android-ndk\android-ndk-r9c\ndk-build.cmd,根据各自的ndk路径设置,也可以点击“Browser File System…”来选取这个路径)。在“WorkingDiretcoty”中输入${workspace_loc:/LEDAPP}(也可以点击“Browse Workspace”来选取LEDAPP目录)。具体设置完成后,如下图:
(e)【EditConfiguration】对话框中,配置选项卡【Refresh】。
勾选“Refresh resources upon completion”,
勾选“The entire workspace”,
勾选“Recuresively include sub-folders”。 具体入下图:
(f)【Edit Configuration】对话框中,配置选项卡【Build options】。
勾选“After a “Clean””,
勾选“During manual builds”,
勾选“During auto builds”,
勾选“Specify working set of relevantresources”。
点击“Specify Resources…”
勾选LEDAPP工程的“jni“目录,点击”finish“。 具体设置完如下图:
(g)点击上图中的OK按钮后,再点击刚开始出现的对话框的OK按钮,这样就设置完成了
4.在LEDAPP下新建一个JniLed.class,这就是存放Android应用程序所调用的类的地方,步骤为:右键LEDAPP->New->class,具体如下图:
在“name”的输入框中输入:JniLed,在package输入框中,选择com.ndk.led,然后点击finish,完成JniLed.class的创建。接下来双击刚创建的JniLed.class,在里面输入如下的内容:
package com.ndk.led; public class JniLed { static public native int LedInit(); static public native int LedIOCTL(int cmd, int led_num); }其中JniLed中的那两个函数,就是我们对led驱动程序经过封装后的函数,你从名字当中也可以看出来,第一个函数就是初始化led,第二个就是通过命令和选择哪一个led进行控制
5.生成com_ndk_led_JniLed.h文件,具体为通过cmd命令进入windows的命令行控制界面,然后进入到工程所在的目录下,具体为进入D盘->test-android/LEDAPP/bin/classes,然后输入:javah com.ndk.led.JniLed,再回车。这样会在classes文件下面生成:com_ndk_led_JniLed.h文件,这就是生成的c/c++文件的头文件。
6.在工程中的jni文件夹下,建立Android.mk文件,名字一定是这个,不能是其他的名字。并且输入如下的内容:
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := LEDAPP LOCAL_SRC_FILES := com_ndk_led_JniLed.c include $(BUILD_SHARED_LIBRARY)这就是Android下面的Makefile,注意第三行中的“LEDAPP”和第四行中的“com_ndk_led_JniLed.c”,一个是模块的名字,另外一个是要编译的c语言的源文件,要和上面的.h文件保存一致。
7.把在classes下面生成的com_ndk_led_JniLed.h文件拷贝到刚开始所建立的jni文件夹下,同时建立com_ndk_led_JniLed.c的文件,这个c文件就是通过调用Linux内核中的驱动函数,以一定的方式进行封装,使Android应用程序能调用驱动的关键。这个文件中的函数要看具体的驱动函数来完成,比如说,咱们现在就得看友善所提供的smart210的Linux-3.0.8内核中的led的驱动函数,具体位置为:友善的Linux-3.0.8/char/mini210_leds.c,我会把这个文件共享,方便大家查看,下面说说com_ndk_led_JniLed.c文件的内容,具体如下:
#include "com_ndk_led_JniLed.h" #include <stdlib.h> #include <stdio.h> #include <string.h> #include <jni.h> #include <fcntl.h> /*包括文件操作,如open() read() close() write()等*/ //----for output the debug log message #include <android/log.h> int fd; //文件标识符 #define DEVICE_NAME "/dev/leds" //这是linux下面的设备节点,Android应用程序就是通过这个来识别驱动程序的 #ifdef __cplusplus extern "C" { #endif JNIEXPORT jint JNICALL Java_com_ndk_led_JniLed_LedInit(JNIEnv *env, jclass arg) { fd = open(DEVICE_NAME,O_RDWR);//打开设备 ioctl(fd,1,2); if(fd == -1) return 0; else return 1; } JNIEXPORT jint JNICALL Java_com_ndk_led_JniLed_LedIOCTL(JNIEnv *env, jclass arg, jint cmd, jint led_num) { int CTLCODE = led_num; switch(CTLCODE) { case 0: if(cmd==0) ioctl(fd,0,0); //调用led驱动函数中的ioctl,这里需要注意,要按驱动里的函数保持一致 else if(cmd==1) ioctl(fd,1,0); break; case 1: if(cmd==0) ioctl(fd,0,1); else if(cmd==1) ioctl(fd,1,1); break; case 2: if(cmd==0) ioctl(fd,0,2); else if(cmd==1) ioctl(fd,1,2); break; case 3: if(cmd==0) ioctl(fd,0,3); else if(cmd==1) ioctl(fd,1,3); break; default: break; } return 1; } #ifdef __cplusplus } #endif
8.同时要更新com_ndk_led_JniLed.h文件中的内容,具体的更新代码如下:
/* DO NOT EDIT THIS FILE - it is machine generated */ #include <jni.h> /* Header for class com_ndk_led_JniLed */ #ifndef _Included_com_ndk_led_JniLed #define _Included_com_ndk_led_JniLed #ifdef __cplusplus extern "C" { #endif /* * Class: com_ndk_led_JniLed * Method: LedInit * Signature: ()I */
//这就是.c文件中实现函数的声明,注意要一致啊,负责会出现“unfortunately,xxx has stopped!”的错误 JNIEXPORT jint JNICALL Java_com_ndk_led_JniLed_LedInit (JNIEnv *, jclass); /* * Class: com_ndk_led_JniLed * Method: LedIOCTL * Signature: (II)I */ JNIEXPORT jint JNICALL Java_com_ndk_led_JniLed_LedIOCTL (JNIEnv *, jclass, jint, jint); #ifdef __cplusplus } #endif #endif
以上就是JNI的部分,下面我将进行Android应用程序的开发
Android的应用开发
这一部分我们粗糙的讲一下,就是弄几个按键,然后调用刚才生成的类,然后下载到smart210上做一个测试。
1.更新LEDAPP下面的res/layout/activity_main.xml,这是Android应用程序的界面布局文件,具体的更新内容如下:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context=".MainActivity" > <Button android:id="@+id/led1_on" android:text="@string/myButton1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="5dip" /> <Button android:id="@+id/led1_off" android:text="@string/myButton2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="115dip" /> <Button android:id="@+id/led2_on" android:text="@string/myButton3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="245dip" /> <Button android:id="@+id/led2_off" android:text="@string/myButton4" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="355dip" /> <Button android:id="@+id/led3_on" android:text="@string/myButton5" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="70dip" android:layout_marginLeft="5dip" /> <Button android:id="@+id/led3_off" android:text="@string/myButton6" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="70dip" android:layout_marginLeft="115dip" /> <Button android:id="@+id/led4_on" android:text="@string/myButton7" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="70dip" android:layout_marginLeft="245dip" /> <Button android:id="@+id/led4_off" android:text="@string/myButton8" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="70dip" android:layout_marginLeft="355dip" /> </RelativeLayout>
2.更新LEDAPP下的res/values/strings.xml的内容,这个里面的内容在前面的activity_main.xml的布局文件中需要,具体内容如下:
<?xml version="1.0" encoding="utf-8"?> <resources> <string name="app_name">LEDAPP</string> <string name="action_settings">Settings</string> <string name="hello_world">Hello world!</string> <string name="myButton1">LED1_ON</string> <string name="myButton2">LED1_OFF</string> <string name="myButton3">LED2_ON</string> <string name="myButton4">LED2_OFF</string> <string name="myButton5">LED3_ON</string> <string name="myButton6">LED3_OFF</string> <string name="myButton7">LED4_ON</string> <string name="myButton8">LED4_OFF</string> </resources>3.现在看看MainActivity.java文件,在他里面的具体调用刚生成的类,具体的代码如下:
package com.ndk.led; import android.os.Bundle; import android.app.Activity; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; public class MainActivity extends Activity { static { System.loadLibrary("LEDAPP"); }//上面的System.loadLibrary("LEDAPP")这一句一定要加上,这是调用刚才生成的库的函数,负责会出现“unfortunately,xxx has stopped!”的错误 Button Button1,Button2,Button3,Button4,Button5,Button6,Button7,Button8; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); JniLed.LedInit(); Button1 = (Button)findViewById(R.id.led1_on); Button2 = (Button)findViewById(R.id.led1_off); Button3 = (Button)findViewById(R.id.led2_on); Button4 = (Button)findViewById(R.id.led2_off); Button5 = (Button)findViewById(R.id.led3_on); Button6 = (Button)findViewById(R.id.led3_off); Button7 = (Button)findViewById(R.id.led4_on); Button8 = (Button)findViewById(R.id.led4_off); class ButtonClick implements OnClickListener { @Override public void onClick(View v) { // TODO Auto-generated method stub switch(v.getId()) { case R.id.led1_on: //Toast.makeText(MainActivity.this, "按钮1", Toast.LENGTH_LONG).show(); JniLed.LedIOCTL(1,0); break; case R.id.led1_off: JniLed.LedIOCTL(0,0); break; case R.id.led2_on: JniLed.LedIOCTL(1,1); break; case R.id.led2_off: JniLed.LedIOCTL(0,1); break; case R.id.led3_on: JniLed.LedIOCTL(1,2); break; case R.id.led3_off: JniLed.LedIOCTL(0,2); break; case R.id.led4_on: JniLed.LedIOCTL(1,3); break; case R.id.led4_off: JniLed.LedIOCTL(0,3); break; } } } Button1.setOnClickListener(new ButtonClick()); Button2.setOnClickListener(new ButtonClick()); Button3.setOnClickListener(new ButtonClick()); Button4.setOnClickListener(new ButtonClick()); Button5.setOnClickListener(new ButtonClick()); Button6.setOnClickListener(new ButtonClick()); Button7.setOnClickListener(new ButtonClick()); Button8.setOnClickListener(new ButtonClick()); } }
这里再简单第说一下引起“unfortunately,xxxhas stopped!”的错误,一般是函数的名字不一致,没有加载创建的库所引起的。好吧,就这么多吧,希望对大家有用,下面是Android下的源代码以及smart210的led驱动文件,链接如下:
Android源代码链接:http://download.csdn.net/detail/xie0812/6947967
smart210的led驱动链接:http://download.csdn.net/detail/xie0812/6947979
下面是在smart210上截取效果图片:
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。