Android驅動之JNI編寫


要想用java去調用C函數接口那么就需要使用JNI(Java Native Interface,Java 本地調用)去訪問本地的動態鏈接庫。

 

關於NDK的安裝,現在有linux環境下的版本,也有windows環境下的版本,這個可自行百度,這里不多說

 

在eclipse中配置NDK:打開我們的eclipse->window->preference->android->ndk設置ndk路徑->ok。

 

1、使用cygwin編譯生成.so文件:右鍵單擊項目->Android Tools->Add Native Support...->.so文件命名例如test->ok。

    完成之后eclipse會生成一個jni文件夾,里面會生成一個test.cpp的文件。

    但是我一般不用這個文件,直接delete,新建三個文件(.h .c .mk)。

    .h和.c文件是用來編寫kernel訪問接口,可以直接調用驅動程序里的函數,這里需要注意命名規則:Java_包名_類名_接口名,此類表示調用.h中的方法的類

    .mk文件是用來編譯生成.so文件的,這里需要注意LOCAL_MODULE := HelloJni,表示生成HelloJni.so庫,

      在java中調用該庫的入口是System.loadLibrary("HelloJni");

2、使用腳本編譯的配置方法

在NDKr7開始,google的windos版NDK提供了一個ndk-build.cmd的腳本,這樣就可以直接利用這個腳本編譯,而不需要cygwin了。

1、選擇你的android工程,右擊->Properties->Builders->new,新添加一個編譯器,點擊之后出現添加界面,選擇Program,點擊ok。
2、出現了添加界面,我們先給編譯器設置名稱,如XXX_builder。設置Location為<NDK安裝目錄>\ndk-build.cmd
  設置Working Directory為${workspace_loc:/項目名稱}
3、切換到Refersh選項卡,給Refersh resources upon completion打上勾,選擇“the entire resource”選項
4、切換到Build Options選項卡,勾選上最后三項。再點擊Specify Resource按鈕,選擇你的android工程
5、在編譯工具列表,我們最好將我們新建的編譯器置頂。選中點擊Up按鈕置頂ok.

 

樣例如下:

1、com_pngcui_HelloJni.h     //命名規則:Java_包名_類名_接口名

 1 /* DO NOT EDIT THIS FILE - it is machine generated */
 2 #include <jni.h>
 3 /* Header for class com_neojet_scanner_key */
 4 
 5 #ifndef _Include_com_pngcui_helloJni
 6 #define _Inlcude_com_pngcui_helloJni
 7 
 8 #ifdef _cplusplus
 9 extern "C"{
10 #endif
11     
12     /*Java_packagename_classname_methodname*/
13     /*open()*/
14     JNIEXPORT jint JNICALL Java_com_pngcui_helloJni_HelloJni_Open
15     (JNIEnv *,jobject);
16     
17     /*close()*/
18     JNIEXPORT jint JNICALL Java_com_pngcui_helloJni_HelloJni_Close
19     (JNIEnv *,jobject);
20     
21     /*ioctl()*/
22     JNIEXPORT jint JNICALL Java_com_pngcui_helloJni_HelloJni_Ioctl
23     (JNIEnv *,jobject,jint num,jint en);
24     
25 #ifdef _cplusplus
26 }
27 #endif
28 #endif

2、com_pngcui_HelloJni.c    //實現.h方法

 1 #include <stdio.h>
 2 #include <stdlib.h>
 3 #include <fcntl.h>
 4 #include <errno.h>
 5 #include <unistd.h>
 6 #include <sys/types.h>
 7 #include <sys/stat.h>
 8 #include <string.h>
 9 #include <stdint.h>
10 #include <termios.h>
11 #include <android/log.h>
12 #include <sys/ioctl.h>
13 
14 #include "com_pngcui_helloJni.h"
15 
16 #undef TCSAFLUSH
17 #define TCSAFLUSH TCSETSF
18 #ifndef _TERMIOS_H_
19 #define _TERMIOS_H_
20 #endif
21 
22 
23 int fd = 0;
24 
25 
26 /*open()*/
27 JNIEXPORT jint JNICALL Java_com_pngcui_helloJni_HelloJni_open
28     (JNIEnv *env , jobject obj){
29         /*只允許打開一次設備!*/
30         if(fd<=0)
31             fd = open("/dev/jni",O_RDWR|O_NDELAY|O_NOCTTY);
32         if(fd<=0)
33             __android_log_print(ANDROID_LOG_INFO,"serial","open /dev/jni Error..");
34         else
35             __android_log_print(ANDROID_LOG_INFO,"serial","open /dev/jni Sucess fd = %d",fd);
36     }
37     
38 JNIEXPORT jint JNICALL Java_com_pngcui_helloJni_HelloJni_close
39     (JNIEnv *env,jobject obj){
40         
41         if(fd > 0)
42             close(fd);
43     }
44     
45 JNIEXPORT jint JNICALL Java_com_pngcui_helloJni_HelloJni_ioctl
46     (JNIEnv *env,jobject obj,jint num , jint en){
47         
48         ioctl(fd,en,num);
49     }

3、Android.mk

1 LOCAL_PATH := $(call my-dir)
2 include $(CLEAR_VARS)
3 LOCAL_MODULE    := HelloJni
4 LOCAL_SRC_FILES := com_pngcui_helloJni.c
5 LOCAL_LDLIBS += -llog 
6 LOCAL_LDLIBS +=-lm
7 include $(BUILD_SHARED_LIBRARY)

在android.mk文件中我們可以知道最后會生成一個libHelloJni.so本地庫文件。

  LOCAL_PATH - 編譯時的目錄
    $(call 目錄,目錄….) 目錄引入操作符
    如該目錄下有個文件夾名稱 src,則可以這樣寫 $(call src),那么就會得到 src 目錄的完整路徑

  include $(CLEAR_VARS) -清除之前的一些系統變量
  LOCAL_MODULE - 編譯生成的目標對象
  LOCAL_SRC_FILES - 編譯的源文件
  LOCAL_C_INCLUDES - 需要包含的頭文件目錄
  LOCAL_SHARED_LIBRARIES - 鏈接時需要的外部庫
  LOCAL_PRELINK_MODULE - 是否需要prelink處理
  include$BUILD_SHARED_LIBRARY- 指明要編譯成動態庫

把以上三個文件放入jni文件夾中,最后編寫一個java類,也就是命名規則的那個java類名

Jni.java

1 package com.pngcui.helloJni;
2 
3 public class Jni {
4     
5     public native int       Open();
6     public native int       Close();
7     public native int       Ioctl(int num, int en);
8 }

最后在調用Jni.java的類中需要聲明本地庫的路徑

1 static {
2         System.loadLibrary("HelloJni");
3     }

附上完整的MainActivity.java代碼

  1 package com.pngcui.helloJni;
  2 
  3 import android.app.Activity;
  4 import android.os.Bundle;
  5 import android.view.View;
  6 import android.view.View.OnClickListener;
  7 import android.widget.Button;
  8 
  9 public class MainActivity extends Activity {
 10 
 11     
 12     Jni jni = new Jni();
 13     
 14     private Button y1;
 15     private Button y2;
 16     private Button n1;
 17     private Button n2;
 18     private Button start;
 19     
 20     int i;
 21     
 22     @Override
 23     protected void onCreate(Bundle savedInstanceState) {
 24         super.onCreate(savedInstanceState);
 25         setContentView(R.layout.activity_main);
 26     
 27         y1 = (Button)findViewById(R.id.y1);
 28         y2 = (Button)findViewById(R.id.y2);
 29         n1 = (Button)findViewById(R.id.n1);
 30         n2 = (Button)findViewById(R.id.n2);
 31         start = (Button)findViewById(R.id.start);
 32         
 33         jni.Open();
 34         
 35         y1.setOnClickListener(new manager());
 36         n1.setOnClickListener(new manager());
 37         y2.setOnClickListener(new manager());
 38         n2.setOnClickListener(new manager());
 39         start.setOnClickListener(new manager());
 40         
 41         
 42     }
 43     
 44     class manager implements OnClickListener{
 45 
 46         @Override
 47         public void onClick(View v) {
 48             
 49             //gpio_set_value(led_gpio[i],cmd),且二極管為低電平有效
 50             
 51             switch(v.getId()){
 52         
 53             case R.id.y1:
 54                 jni.Ioctl(0, 0);
 55                 break;
 56             case R.id.n1:
 57                 jni.Ioctl(0, 1);
 58                 break;
 59                 
 60             case R.id.y2:
 61                 jni.Ioctl(1, 0);
 62                 break;
 63             case R.id.n2:
 64                 jni.Ioctl(1, 1);
 65                 break;
 66                 
 67             case R.id.start:
 68                 try {
 69                     start();
 70                 } catch (InterruptedException e) {
 71                     // TODO Auto-generated catch block
 72                     e.printStackTrace();
 73                 }
 74                 break;
 75             
 76             }
 77             
 78         }
 79 
 80         private void start() throws InterruptedException {
 81             // TODO Auto-generated method stub
 82             
 83             i = 10;
 84             while(i>0){
 85                 i--;
 86                 jni.Ioctl(1, 0);
 87                 jni.Ioctl(0, 1);
 88                 
 89                 Thread.sleep(200);
 90                 
 91                 jni.Ioctl(1, 1);
 92                 jni.Ioctl(0,0);
 93                 
 94                 Thread.sleep(200);
 95             }
 96             jni.Ioctl(1, 0);
 97         }
 98         
 99     
100         
101     }
102 
103     static {
104         System.loadLibrary("HelloJni");
105     }
106 }
View Code

最后還有一個問題,那就是權限問題!也就是說到現在我們還沒有給我們的驅動程序生成的設備節點設置權限,也就是可讀可寫權限,如果不設置這個權限,那么我們的android應用時無法打開設備節點的!!!在調用open函數時會直接返回-1!!而在exynos4412平台上的android設置權限的文件在device/samsung/smdk4x12/conf/init.smdk4x12.rc中,而不是網上說的init.rcueventd.rc文件中,但是!!本人在里面設置了chmod 777 /dev/jni 依舊不能正常open,百思不得其解,有哪位大神知道怎么去自動設置權限,還麻煩教導我一下,不甚感激!

 

附上蠢蠢的解決辦法:在串口終端使用命令chmod 777 /dev/jni然后運行app,可暫時性解決此問題,但是開發板重啟以后就需要重修設置權限!!

 

---------------------------------2016.4.12 update------------------------------------------------------

權限問題的解決方案:為什么在腳本中寫了chmod命令還是不能open設備文件,因為沒有燒寫ramdisk.img文件啊啊啊啊!!!只燒寫了system.img,因為rc腳本會編譯到ramdisk鏡像中。

 

參考鏈接:http://blog.csdn.net/loongembedded/article/details/39778409

 

最后附上查看設備權限命令:ls -al /dev/xxx

r: 對應數值4
w: 對應數值2
x:對應數值1
-:對應數值0

eg。-rwxrwxrwx  對應0777 即所有用戶都可讀可寫可執行

 

---------------------2016.4.20補充驅動函數返回數據到java中----------------------

如果需要讀取底層硬件狀態等信息,並返回到app上,則需要使用jni里的方法去調用驅動函數的ioctl,而不能直接在java中寫data = jni.Ioctl(a,b);

1 JNIEXPORT jint JNICALL Java_com_pngcui_fm_Jni_GetData
2     (JNIEnv *env,jobject obj,jint cmd ){
3 
4         jint data = 0;
5 
6         data = ioctl(fd,cmd,0);
7 
8         return data;
9     }

使用如上方法才能正常返回!

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM