1. 程式人生 > >在 Android 系統中直接呼叫 SO檔案(包含System.loadLibrary載入so的路徑解釋)

在 Android 系統中直接呼叫 SO檔案(包含System.loadLibrary載入so的路徑解釋)

Android虛擬機器不能直接呼叫底層裝置,如果要對底層裝置進行呼叫,就需要用到so使
用 C語言或C++編寫完成,利用NDK進行編譯,直接執行在Linux核心中,按jni呼叫 so時,
基本型別可以直接互動。 
 在 apk裡打包進.so檔案的方法有兩種,一 是在 Android.mk檔案裡增加
“LOCAL_JNI_SHARED_LIBRARIES := libxxx”,這樣在編譯的時候,NDK 自動會把這個libxxx打
包進apk,放在xxx/lib/目錄下;二是在應用的目錄下手工建libs/armeabi目錄,然後把libxxx.so
拷貝到這個目錄下,NDK也會自動把這個libxxx.so打包進apk,位置還是在xxx/lib/目錄下。 

 在程式碼裡,使用System.loadLibrary("xxx");就可以載入這個動態庫了。這裡要注意,引數
只寫xxx就可以了,不需要寫libxxx,也不需要寫libxxx.so。 
 還有一點要說明,System.loadLibrary這個函式會在如下路徑搜尋libxxx.so檔案: 
 /system/lib 
 /data/data/xxx apk package/lib 
 如果libxxx.so還依賴其它.so檔案,比如libyyy.so,則System.loadLibrary只會在/system/lib
目錄下查詢,如果沒找到,不會自動到/data/data/xxx apk package/lib 下去找,這個時候就會

報動態庫沒找到的錯誤。解決方法是在load libxxx.so之前,先load libyyy.so,具體如下: 
 System.loadLibrary("yyy"); 
 System.loadLibrary("xxx"); 
 本文結合使用例項進行說明。 

建立 jni目錄.libs目錄 
 在工程根目錄下建立 jni 目錄,libs 目錄不用手動建立,如圖 1 所示。注意,這裡使用
的是 ndk_R7,所以不需要用jdk去生成C 檔案。 
\

圖1 
 Java編寫介面檔案(Device.java) 
 Device.java的程式碼實現如下:

public class Device { 
 static { 
 System.loadLibrary("device"); 

 } 
 public native String deviceTestString(String test); 


 方法名必須使用native關鍵字宣告,並且必須使用system.loadLibrary("SO檔名")承載
C 類庫。 


編寫C檔案(devices.c) 
 這裡編寫的C程式碼屬於Linux C 範疇,實現程式碼如下: 

#include <string.h> 
#include <jni.h> 
char* jstringTostrM(JNIEnv* env, jstring jstr) 

 char* pStr = NULL; 
 jclass jstrObj = (*env)->FindClass(env, "java/lang/String"); 
 jstring encode = (*env)->NewStringUTF(env, "utf-8"); 
 jmethodID methodId = (*env)->GetMethodID(env, jstrObj, "getBytes", 
"(Ljava/lang/String;)[B"); 
 jbyteArray byteArray = (jbyteArray)(*env)->CallObjectMethod(env, jstr, methodId, 
encode); 
 jsize strLen = (*env)->GetArrayLength(env, byteArray); 
 jbyte *jBuf = (*env)->GetByteArrayElements(env, byteArray, JNI_FALSE); 
 if (jBuf > 0) 
 { 
 pStr = (char*)malloc(strLen + 1); 
 if (!pStr) 
 { 
 return NULL; 
 } 
 memcpy(pStr, jBuf, strLen); 
 pStr[strLen] = 0; 
 } 
 (*env)->ReleaseByteArrayElements(env, byteArray, jBuf, 0); 
 return pStr; 

jstring Java_com_jack_Device_deviceTestString(JNIEnv* env,jclass clazz,jstring path){  //system("echo devices.so test > /sdcard/log/log.txt"); 
 char * test = jstringTostrM(env,path); 
 return (*env)->NewStringUTF(env, test); 


 注意C 的函式命名規則,Java的jni標準必須有,com_jack_Device是Device.java檔案的
全 名 , 再下來才是 C 函式名 , jstringTostrM 函式必須寫在
Java_com_jack_Device_deviceTestString函式前,如果不是,必須要在C 檔案頭進行宣告,聲
明程式碼為: 
 char* jstringTostrM(JNIEnv* env, jstring jstr); 


編寫 Android.mk和編譯android.mk 
 Android.mk的程式碼如下: 

LOCAL_PATH := $(call my-dir) 
include $(CLEAR_VARS) 
LOCAL_MODULE := device 
LOCAL_SRC_FILES := device.c 
include $(BUILD_SHARED_LIBRARY) 

 如果要編譯成可執行檔案,還需包含程式碼include $(BUILD_EXECUTABLE),之後按照圖2
所示編譯即可。 

圖 2 

編寫 Java程式碼進行C函式呼叫 

 Device device = new Device(); 
 String test = device.deviceTestString("你好~!!!"); 
 Toast toast = Toast.makeText(Jack_ndk_jstringActivity.this, test, Toast.LENGTH_LONG); 
 toast.setGravity(Gravity.TOP,0,150); 
 toast.show(); 
 TextView text = (TextView) findViewById(R.id.text1); 
 text.setText(test); 

 注意,最後在AndroidManifest.xml檔案中要加入檔案控制權限,程式碼如下: 

<!-- 檔案許可權 --> 
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/> 
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>