在 Android 系統中直接呼叫 SO檔案(包含System.loadLibrary載入so的路徑解釋)
阿新 • • 發佈:2019-02-15
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"/>
用 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"/>