天天看點

在 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"/>