Android通過JNI調用驅動程序(完全解析實例)

要達到的目的:android系統中,用JAVA寫界面程序,調用jni中間庫提供的接口,去操作某個驅動節點,實現read,writer ioctl等操作!這對底層驅動開發人員是很重要的一個調試通道,也是android 系統下提供一些特殊功能接口的方法!

本文前提:我們假設已經寫了一個驅動程序,它是控制LED的亮滅的,並且創建了一個節點:/dev/vib,也就是通過open這個vib節點,可以read/write/ioctl 操作驅動程序實現LED燈的亮滅控制,具體可以看我另一篇博文《android驅動例子(LED燈控制)

開發環境 1、ubuntu下的NDK編譯環境,2、Esclips開發環境

 
一、編寫JNI模塊

    當安裝好NDK編譯環境後,會在它的目錄下找到sample目錄,它裏面有一些例子,可以參考這些例子來寫我們自已的模塊。

 

1、 source文件夾下,新建“LEDSJNI”文件夾。

2、 Source/LEDSJNI/jni/目錄下,新建“vib-jni.c”

vib-jni.c文件

	#include 
#include 
#include  /*包括文件操作,如open() read() close() write()等*/
//----for output the debug log message
#include 
#define  LOG_TAG    "vib-jni"
#define  LOGI(...)  __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
#define  LOGE(...)  __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
#define  DEVICE_NAME  "/dev/vib"  //device point
#define  VIB_ON 0x11
#define  VIB_OFF 0x22
int fd;
jstring
Java_com_auly_control_vibClass_stringFromJNI( JNIEnv* env,
                                                  jobject thiz )
{
    return (*env)->NewStringUTF(env, "Hello from JNI--Peter for vib!");//打印字符串
}
 
jint   
Java_com_auly_control_vibClass_Init( JNIEnv*  env )
{
   LOGE("vibClass_Init() /n");
   fd = open(DEVICE_NAME,O_RDWR);//打開設備
   LOGE("vibClass_Init()-> fd = %d  /n",fd);
   if(fd == -1)
       {
           LOGE("open device %s error /n ",DEVICE_NAME);//打印調試信息
           return 0;
   }
   else
       {
          return 1;
       }
}
 
jint 
Java_com_auly_control_vibClass_IOCTLVIB( JNIEnv*  env, jobject  thiz, jint controlcode )
{
    int CTLCODE = controlcode;
    LOGE("IOCTLVIB() = %x --vibClass_IOCTLVIB /n",CTLCODE);
       switch(CTLCODE)
           {
        case VIB_ON:
                {
                    ioctl(fd,VIB_ON);//調用驅動程序中的ioctrl接口,把命令VIB_ON傳下去,實現硬件操作
                break;
                }
        case VIB_OFF:
                {
                    ioctl(fd,VIB_OFF);//調用驅動程序中的ioctrl接口,把命令VIB_OFF傳下去,實現硬件操作
                break;
                }        
        default:break;
       }
       return 1;
 }

3、相同目錄下的新建Android.mk如下

Android.mk文件

		LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE    := vib-jni
LOCAL_SRC_FILES := vib-jni.c
LOCAL_CFLAGS    := -Werror
LOCAL_LDLIBS    := -llog -lGLESv2 //__android_log_print 函數
include $(BUILD_SHARED_LIBRARY)

可以看到,主要是修改LOCAL_SRC_FILES指向源文件的名稱!

還有一點很重要,如果要使用調試LOG 打印,也就是__android_log_print 函數。要在LOCAL_LDLIBS中添加-llog,如上面的Android.mk所示。

4、編譯JNI模塊

		#cd /home/workspace/android-ndk-r4b/sources/LEDSJNI

進到剛纔寫的JNI目錄

 

			#ndk-build

編譯JNI,編譯成功後,會在LEDSJNI文件夾下生成libs和obj兩個文件夾,並在

LEDSJNI/libs/armeabi下得到目標文件libvib-jni.so

 

(目前LEDSJNI文件夾只有3個目錄jni,libs,obj)

 

二、JAVA程序

1、Eclipse新建工程

拷貝LEDSJNI目錄到Windows下,例如C盤下。然後在它裏面新建Eclipse 工程。

 
 

新鍵工程後,

如果出現如下錯誤:

ERROR: Unable to open class file C:/LEDSJNI/gen/com/auly/control/R.java: No such file or directory

解決方法如下:

對着該工程鼠標右鍵 》bulid path 》configure build path》java build path》order and Export

 

把裏面的android 2.1勾上,Build project,就OK了

 

然後 Run as > Android application,就會出現android的模擬器了,裏面跑個helloworld出來。

 

2、加入button和文本輸出

程序到上面爲止代碼是ADT自動生成的,似乎與我們一點關係也沒有。那我們來改一下代碼,因爲我們調用JNI接口是爲了訪問驅動程序操作硬件的,例如寫,讀,打開LED,關閉LED等等,由按鈕觸發的動作。

第一步是,增加兩個Button,,在main.xml裏描述一下:打開Res > layout> main.xml 文件

		<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    >
<TextView
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:text="@string/hello"
    />
 <Button android:id="@+id/led_on"/*這表示需要一個唯一的UID來作爲Button的ID,它的引用名是led_on。*/
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/LEDon">/*表示這個按鈕的文本是來源於另一個資源描述文件strings.xml裏的文:字資源LEDon */
        <requestFocus/>
</Button>
 <Button android:id="@+id/led_off"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/LEDoff">
        <requestFocus/>
 </Button> 
</LinearLayout>

實際代碼中,把註釋去掉,否則編譯不過的。

3、加入輸出字符串資源

工程 > values > strings.xml 文件

		修改如下

"1.0" encoding="utf-8"?>

    "hello">Led 控制程序
    "app_name">LEDAPP
"LEDon">打開LED
"LEDoff">關閉LED

上面的”打開LED”等資源,就是用在按鈕上顯示出來的字符串

經過上面的修改,現在程序界面上,已經有如下效果了

鼠標右鍵工程名>Run as > Android application 運行程序。

 

4、加入按鈕對應的動作

“打開LED”按扭:調用JNI的IOCTLVIB(VIB_ON);

“關閉LED”按鈕:調用JNI的 IOCTLVIB(VIB_OFF);

操作:

在LEDAPP > src > com.auly.control > vibrator.java文件

			package com.auly.control;
/**定義頭文件*/
import android.widget.TextView;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.util.Log;
import android.app.Activity;
import android.os.Bundle;
public class vibrator extends Activity {
    /** 定義變量 */
    public   static   final   int   VIB_ON = 0x11;   
    public   static   final   int   VIB_OFF = 0x22;
    vibClass mvibClass;/**定義類*/
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
 /**----------------初始化------------- */       
            mvibClass = new vibClass();/*聲明類*/
            mvibClass.Init(); //調用JNI庫裏的初始化函數
 /**----------------按鈕:打開LED------------- */             
     Button btn1 = (Button)findViewById(R.id.led_on);/*這裏用到的ID,就是在main.xml裏定義的 led_on*/ 
     btn1.setOnClickListener(new View.OnClickListener() 
            {
                public void onClick(View v) /**當按鈕按下*/
                {
                    mvibClass.IOCTLVIB(VIB_ON);
                }
            });
/**----------------按鈕:關閉LED------------- */ 
            Button btn2 = (Button)findViewById(R.id.led_off);/*聲明按鈕,id main.xml裏有定義*/
            btn2.setOnClickListener(new View.OnClickListener() 
            {
                public void onClick(View v) /**當按鈕按下*/
                {
                    mvibClass.IOCTLVIB(VIB_OFF);
                }
            });
    }
}

如果在保存時遇到說 save problems,無法保存,請先複製上面的代碼,然後,關閉vibrator.java,提示保存時選不保存,然後在左邊資源窗中再次雙擊打開該文件,把幾日才複製下來的內容,粘貼上去,一般就能正常保存,不知道是不是Eclipse的不穩定造成的。

5、添加類vibClass

鼠標右鍵com.auly.control > new > Class

填參數:

 

Finish後,在/src/com/auly/control/得到如下的類文件

vibClass.java

修改如下

		package com.auly.control;
/*Class for Vibrator --peter*/
public class vibClass {
    static {
        System.loadLibrary("vib-jni");/*加載JNI庫*/
    }
    /*聲明 函數*/
    public static native String stringFromJNI();/*輸出字符串
    對應於JNI裏面的
    jstring Java_com_auly_control_vibClass_stringFromJNI( JNIEnv* env,jobject thiz )
    */
    public static native int Init();/*初始化函數,對應於JNI裏面的
                                   jint Java_com_auly_control_vibClass_Init( JNIEnv*  env )
                                    */
    public static native int IOCTLVIB(int controlcode);
                                    /*IO CTRL 接口
                                     * 對應於JNI裏的
 jint Java_com_auly_control_vibClass_IOCTLVIB( JNIEnv*  env, jobject  thiz, jint controlcode )
 */
}
 
三、 編譯運行

鼠標右鍵工程名,彈出菜單,選擇 Run as > Android Application 就可以看到編譯過程,編譯完成後,會自動調用android模擬器,看到如下效果

 

安裝到開發板:

在C:/LEDSJNI目錄下,會看到bin文件夾,裏面的LEDAPP.apk就是這個程序的安裝文件,可以把它安裝的開發板上,運行本程序,看控制開發板上的LED燈的效果。

步驟:

1、開發板上跑的kenel就已經把了LED驅動編譯在裏面了,

可以參考《android驅動例子(LED燈控制)》

2、開發板android跑起來後,PC機打開串口工具例如DNW,打開與開發板連接的COM口,然後敲打回車,就會在終端裏看到“#”並有光標,表面進入了開發板的命令行終端,

輸入命令:

	#chmod 777 /dev/vib 

      這是爲了使得vib這個節點可以被我們寫的JNI操作,不然會open失敗的,因爲APK安裝的JNI模塊,權限不夠,這個節點是我們的LED驅動生成的控制節點。

也可以在android文件系統yaffs編譯時,通過init.rc 文件來實現這個操作,就是在該文件裏隨便一行,寫上面的命令行,啓動時會自動執行!這樣就不用手動的改變該節點的屬性了。

3、拷貝LEDAPP.apk到開發板上,通過安裝工具把它安裝到開發板上,如果不會安,可以GOOGLE一下,

4、運行程序,就能按程序上的近鈕來控制開發板上的LED亮滅了!

發佈了9 篇原創文章 · 獲贊 6 · 訪問量 13萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章