1. 程式人生 > >Android 呼叫.so檔案 jni

Android 呼叫.so檔案 jni

android呼叫so

android虛擬機器不能直接呼叫底層裝置,我們如果要對底層裝置進行呼叫就需要用到so.

so使用C語言或C++編寫完成,使用ndk進行編譯,直接執行在linux核心中.

按jni呼叫so時基本型別可以直接互動,jstring使用時有點麻煩,所以我做一個jstring和char*進行轉換的例子.

第一步:

工程根目錄下建立jni目錄.libs目錄不用手動建立.


注意:這裡使用ndk_R7所以不需要用jdk去生成C檔案頭.

第二部:

java編寫介面檔案(Device.java)

  1. publicclass Device {  
  2.      static
     {  
  3.             System.loadLibrary("device");  
  4.         }  
  5.      publicnative String deviceTestString(String test);  
  6. }  
方法名必須使用native關鍵字宣告,並且必須使用system.loadLibrary("SO檔名")承載c類庫;

第三部:

編寫C檔案(devices.c)

這裡編寫的c程式碼屬於linux C範疇.

  1. #include <string.h>
  2. #include <jni.h>
  3. char* jstringTostrM(JNIEnv* env, jstring jstr)  
  4. {         
  5.     char* pStr = NULL;  
  6.     jclass     jstrObj   = (*env)->FindClass(env, "java/lang/String");  
  7.     jstring    encode    = (*env)->NewStringUTF(env, "utf-8");  
  8.     jmethodID  methodId  = (*env)->GetMethodID(env, jstrObj, "getBytes""(Ljava/lang/String;)[B");  
  9.     jbyteArray byteArray = (jbyteArray)(*env)->CallObjectMethod(env, jstr, methodId, encode);  
  10.     jsize      strLen    = (*env)->GetArrayLength(env, byteArray);  
  11.     jbyte      *jBuf     = (*env)->GetByteArrayElements(env, byteArray, JNI_FALSE);  
  12.     if (jBuf > 0)  
  13.     {  
  14.         pStr = (char*)malloc(strLen + 1);  
  15.         if (!pStr)  
  16.         {  
  17.             return NULL;  
  18.         }  
  19.         memcpy(pStr, jBuf, strLen);  
  20.         pStr[strLen] = 0;  
  21.     }  
  22.     (*env)->ReleaseByteArrayElements(env, byteArray, jBuf, 0);  
  23.     return pStr;  
  24. }  
  25. jstring Java_com_jack_Device_deviceTestString(JNIEnv* env,jclass clazz,jstring path){  
  26.     //system("echo devices.so test > /sdcard/log/log.txt");
  27.     char * test = jstringTostrM(env,path);  
  28.     return (*env)->NewStringUTF(env, test);  
  29. }  

注意:C的函式命名規則:Java是jni標準必須有,com_jack_Device是Device.java檔案的全名,再下來才是C函式名

jstringTostrM函式必須寫在Java_com_jack_Device_deviceTestString函式前,如果不是必須要C檔案開頭進行宣告.

宣告程式碼:

  1. char* jstringTostrM(JNIEnv* env, jstring jstr);  
第四步:編寫Android.mk和編譯

android.mk檔案如下:

  1. LOCAL_PATH := $(call my-dir)  
  2. include $(CLEAR_VARS)  
  3. LOCAL_MODULE    := device  
  4. LOCAL_SRC_FILES := device.c  
  5. include $(BUILD_SHARED_LIBRARY)  
如果要編譯成可執行檔案
  1. include $(BUILD_EXECUTABLE)  

編譯:


第五步:編寫java程式碼進行C函式呼叫:

  1. Device device = new Device();  
  2.        String test = device.deviceTestString("你好~!!!");  
  3.        Toast toast = Toast.makeText(Jack_ndk_jstringActivity.this, test, Toast.LENGTH_LONG);  
  4.        toast.setGravity(Gravity.TOP,0,150);  
  5.        toast.show();  
  6.        TextView text = (TextView) findViewById(R.id.text1);  
  7.        text.setText(test);  
注意:最好在AndroidManifest.xml檔案中加入檔案控制控制權限.
  1. <!-- 檔案許可權 -->
  2. <uses-permissionandroid:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
  3. <uses-permissionandroid:name="android.permission.WRITE_EXTERNAL_STORAGE"/>