天天看点

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