Android中關於JNI 的學習(零)簡單的例子,簡單地入門

Android中JNI的作用,就是讓Java能夠去調用由C/C++實現的代碼,爲了實現這個功能,需要用到Anrdoid提供的NDK工具包,在這裏不講如何配置了,好麻煩,配置了好久。。。

本質上,Java去調用C/C++的代碼其實就是去調用C/C++提供的方法,所以,第一步,我們要創建一個類,並且定義一個Native方法,如下:

JniTest類:

  1. public class JniTest {  
  2.   
  3.     public native String getTestString();  
  4. }  

可以看到,在這個方法的前面,用到了native關鍵字。

接着,我們要在命令行中編譯這個java文件,得到一個class文件,如下:


然後我們可以利用javah命令文件,生成一個C的頭文件,其實javah這一步不是必需的,因爲創建這個頭文件,只是爲了方便我們複製這個Jni中對應的方法名稱,因爲這些名稱實在太複雜了。 

在這裏有一點要注意,javah命令要在包的根目錄下調用,對應的類文件,必須是完整的類名,如上圖所示,會先回到src目錄,再調用javah命令。

這樣我們就會在src文件夾下在產生一個頭文件,如下圖所示:


我們可以看到其名稱是com_lms_jni_JniTest.h,其實就是包名+類名,我們可以看看裏面的內容:

  1. /* DO NOT EDIT THIS FILE - it is machine generated */  
  2. #include <jni.h>  
  3. /* Header for class com_lms_jni_JniTest */  
  4.   
  5. #ifndef _Included_com_lms_jni_JniTest  
  6. #define _Included_com_lms_jni_JniTest  
  7. #ifdef __cplusplus  
  8. extern "C" {  
  9. #endif  
  10. /* 
  11.  * Class:     com_lms_jni_JniTest 
  12.  * Method:    getTestString 
  13.  * Signature: ()Ljava/lang/String; 
  14.  */  
  15. JNIEXPORT jstring JNICALL Java_com_lms_jni_JniTest_getTestString  
  16.   (JNIEnv *, jobject);  
  17.   
  18. #ifdef __cplusplus  
  19. }  
  20. #endif  
  21. #endif  

我們可以看到,在這裏面有一個方法,名稱是Java_com_lms_jni_JniTest_getTestString,夠複雜吧,其實如果我們知道這個名稱規則,並且知道如何去實現這樣一個方法的話,我們是完全可以不生成這個頭文件的,我們可以直接寫出對應的C文件。

接下來,在jni文件中創建一個對應的C文件,名稱是值得並無所謂,但爲了統一,我們就把它叫JniTest.c吧,如下:


在這裏,我們也把com_lms_jni_JniTest.h也放到這裏了,這個其實是沒關係的,只是爲了內容的協調和統一而已,一般情況下,我們會把所以由C/C++實現的文件都放在項目目錄下一個叫 jni 的文件夾下面。

下面是在JniTest.c中實現native方法,getTestString,如下:

  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <jni.h>  
  4.   
  5. JNIEXPORT jstring JNICALL Java_com_lms_jni_JniTest_getTestString  
  6.   (JNIEnv *e, jobject obj){  
  7.     return (**e).NewStringUTF(e,"Hello from JniTest Function");  
  8. }  

在這個c文件中,我們看到,並沒有引用頭文件com_lms_jni_JniTest.h,而只是引用了一般的C/C++庫文件,比如stido.h和stdlib.h文件等,在這裏注意到一點,我們還會引用jni.h文件,jni.h文件是JNI編程中很重要的一個頭文件,關於Java中的數據類型跟jni中的數據類型的對應全部是在這個文件中定義的,後續會來看一下這個jni.h文件。

在上面JniTest.c文件中實現了方法之後,關於C/C++這邊的實現其實也就實現了,那麼接下來就是要將這個C文件編譯成so文件由Android來調用。

爲什麼是so文件呢,這是因爲Android本質上就是一個linux系統,所以其調用的JNI庫文件,都是so形式。

Android提供的NDK庫提供了ndk-build的命令來實現這個編譯過程,但在此之前,我們要先創建一個Android.mk文件,這是一個簡單的小小的Make文件,其內容如下:

  1. LOCAL_PATH := $(call my-dir)  
  2.   
  3. include $(CLEAR_VARS)  
  4.   
  5. LOCAL_MODULE := com_lms_jni_HwDemo  
  6. LOCAL_SRC_FILES := \  
  7. HwDemo.c \  
  8. JniTest.c \  
  9.   
  10. include $(BUILD_SHARED_LIBRARY)  

在這裏,我們會定義幾個變量:

LOCAL_PATH:其值是call my-dir,而my-dir是個宏函數,會返回Android.mk所在的路徑,在這裏,就是jni文件夾。

include $(CLEAR_VARS),這個命令會清除掉所有LOCAL開頭的變量,比如LOCAL_MODULE之類的,但有一個例外,就是其上面的LOCAL_PATH 。

LOCAL_MODULE:要生成的so包名,也是Android中Java代碼加載時的名稱。

LOCAL_SRC_FILES:要進行編譯的源文件,如在這裏,有HwDemo.c和JniTest.c等。

include $(BUILD_SHARED_LIBRARY):表明生成一個動態鏈接庫。

定義後這樣一個Android.mk文件之後,在命令行中調用ndk-build命令,如下:


命令實行之後,我們可以在項目目錄下看到libs中多了一個so庫,如下:


到這裏,關於Jni實現的就結束了,接下來就是如何在Android中使用這個本地方法了。

我們創建了一個Activity,在它裏面只放置一個TextView控件,它的佈局如下:

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:layout_width="match_parent"  
  4.     android:layout_height="match_parent"  
  5.     android:orientation="vertical" >  
  6.   
  7.     <TextView  
  8.         android:id="@+id/tvJni"  
  9.         android:layout_width="wrap_content"  
  10.         android:layout_height="wrap_content"  
  11.         android:text="test" />  
  12.   
  13. </LinearLayout>  

然後在Activity中,我們要加載這個so庫,如下:
  1. public class HwDemo extends Activity {  
  2.   
  3.     static {  
  4.         System.loadLibrary("com_lms_jni_HwDemo");//加載so庫  
  5.     }  
  6.       
  7.     public native String printHello();    
  8.   
  9.     @Override  
  10.     public void onCreate(Bundle savedInstanceState) {  
  11.         super.onCreate(savedInstanceState);       
  12.         setContentView(R.layout.main);  
  13.         TextView tv = (TextView)findViewById(R.id.tvJni);  
  14.         JniTest jniTest = new JniTest();//調用JniTest文件的方法  
  15.         tv.setText(jniTest.getTestString());  
  16.     }  
  17.   
  18. }  

1)利用static靜態代碼塊,加載so庫文件,可以看到在這裏,這個名稱就是Anrdoid.mk中定義的LOCAL_MODULE值。

2)創建JniTest對象,調用其getTestString()方法,最終顯示結果如下:


到這裏,通過一個簡單的例子,我們明白瞭如何在Android中利用JNI來調用C/C++的方法了。

最後,我們總結一下這幾個步驟:

1)創建Java類文件,並定義Native方法,如JniTest類。

2)利用javac生成class文件,然後回到src目錄,利用javah生成C/C++頭文件,在這裏要注意,javah命令要在包的根目錄下調用,對應的類文件,必須是完整的類名,如下:

在Src目錄:javah com.lms.jni.JniTest,在上面的截圖,也可以看到javac之後,是回到src目錄,再調用javah。

3)編寫對應的C文件,如JniTest.c,在裏面實現C/C++的方法,記得要放在jni文件夾下面。

4)編寫Android.mk文件,利用ndk-build命令生成so文件。

5)在Android中利用static靜態代碼塊,調用system.loadLibrary方法來加載so庫文件。

6)在Java邏輯中調用之前定義的JniTest類的方法。

結束。源代碼下載

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章