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/xxxapkpackage/lib
如果libxxx.so还依赖其它.so文件,比如libyyy.so,则System.loadLibrary只会在/system/lib目录下查找,如果没找到,不会自动到/data/data/xxxapkpackage/lib下去找,这个时候就会报动态库没找到的错误。解决方法是在loadlibxxx.so之前,先loadlibyyy.so,具体如下:
System.loadLibrary(yyy); System.loadLibrary(xxx);
本文结合使用实例进行说明。
创建jni目录.libs目录
在工程根目录下创建jni目录,libs目录不用手动建立,如图1所示。注意,这里使用的是ndk_R7,所以不需要用jdk去生成C文件。
Java编写接口文件(Device.java)
Device.java的代码实现如下:
publicclassDevice{ static{ System.loadLibrary(device); } publicnativeStringdeviceTestString(Stringtest); }
方法名必须使用native关键字声明,并且必须使用system.loadLibrary(SO文件名)承载C类库。
编写C文件(devices.c)
这里编写的C代码属于LinuxC范畴,实现代码如下:
#includestring.h #includejni.h char*jstringTostrM(JNIEnv*env,jstringjstr) { char*pStr=NULL; jclass jstrObj encode =(*env)-FindClass(env,java/lang/String); =(*env)-NewStringUTF(env,utf-8); jstring jmethodID methodId = (*env)-GetMethodID(env, jstrObj, getBytes, (Ljava/lang/String;)[B); jbyteArraybyteArray=(jbyteArray)(*env)-CallObjectMethod(env,jstr,methodId, encode); jsize strLen =(*env)-GetArrayLength(env,byteArray); jbyte *jBuf=(*env)-GetByteArrayElements(env,byteArray, JNI_FALSE); if(jBuf0) { pStr=(char*)malloc(strLen+1); if(!pStr) { returnNULL; } memcpy(pStr,jBuf,strLen); pStr[strLen]=0; } (*env)-ReleaseByteArrayElements(env,byteArray,jBuf,0); returnpStr; } jstringJava_com_jack_Device_deviceTestString(JNIEnv*env,jclassclazz,jstringpath){ //system(echodevices.sotest/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,jstringjstr);
编写Android.mk和编译android.mk
Android.mk的代码如下:
LOCAL_PATH:=$(callmy-dir) include$(CLEAR_VARS) LOCAL_MODULE:=device LOCAL_SRC_FILES:=device.c include$(BUILD_SHARED_LIBRARY)
如果要编译成可执行文件,还需包含代码include$(BUILD_EXECUTABLE),之后按照图2所示编译即可。
编写Java代码进行C函数调用
Devicedevice=newDevice(); Stringtest=device.deviceTestString(你好~!!!); Toast toast = Toast.makeText(Jack_ndk_jstringActivity.this,test, Toast.LENGTH_LONG); toast.setGravity(Gravity.TOP,0,150); toast.show(); TextViewtext=(TextView)findViewById(R.id.text1); text.setText(test); 注意,最后在AndroidManifest.xml文件中要加入文件控制权限,代码如下: !--文件权限-- uses-permission android:name=android.permission.MOUNT_UNMOUNT_FILESYSTEMS/ uses-permissionandroid:name=android.permission.WRITE_EXTERNAL_STORAGE/
(完)