一直以来在android上只是用JNI和和ndk来调用底层,写jni的确是件很痛苦的事情,各种数据的转换很头疼。
下面介绍我自己在android上使用jna的情况。
jna的知识我就不在这里重复说明了,从https://github.com/twall/jna/archive/master.zip下载jna3.5.0,里面dist\javadoc目录下的index.html文件介绍了jna的使用方法。
通常在windows,linux,mac上直接就可以将dist目录下的jna.jar文件放到eclipse里面直接调用了,但是在android下不行,android里面调用的需要重新编译
libjnidispatch.so文件和jna源文件。
1:在eclipse里面建一个java工程名字叫jna,将下载的master.zip里面的src里面的源文件全部copy到jna的src目录下:结构如下图所示:
然后将该工程里面的class文件打包成一个jar文件,名字叫jna.jar。
只所以要重新编译jna,是因为master.zip自带的jna.jar文件里面带so文件,而android上jar文件里面是不能带so文件的,否则无法生成apk文件。
下面进入第二步编译一个能在android上运行的libjnidispatch.so文件。
1:找一个linux操作系统我这里使用的是ubuntu12.04。
建立一个叫jna的目录,将master.zip里面的native文件夹copy到linux上这个新建的jna目录下,我是用工具winscp工具传倒ubuntu上去的。文件结构如系统所示:
注:里面的MakefileCopyOf是我自己备份的Makefile文件。
先打开Makefile文件。
首先大家看到了一段话
OS=$(shell uname | sed -e 's/CYGWIN.*/win32/g' \
-e 's/MINGW32.*/win32/g' \
-e 's/SunOS.*/solaris/g' \
-e 's/NetBSD/netbsd/g' \
-e 's/FreeBSD/freebsd/g' \
-e 's/OpenBSD/openbsd/g' \
-e 's/Darwin.*/darwin/g' \
-e 's/AIX.*/aix/g' \
-e 's/Linux.*/linux/g')
这段代码的功能是获取操作系统的名称是linux还是别的其他系统。但是在android上我们将这段代码替换成OS=android
接着大家会看到JNA_JNI_VERSION=3.5.0 # auto-generated by ant这样的代码。
这个时候需要将他改成JNA_JNI_VERSION=3.5.0
注意3.5.0后面不能带空格啊,我第一次编译的时候就是因为#号前面的空格没有去掉害的我查了半天的错误才找出来。
接着是需要配置交叉编译器和ndk了。
大概在Makefile110行左右
# Android build (cross-compile) requires the android SDK+NDK.
# Ensure the following tools are in your path and adjust NDK_PLATFORM as needed
在这2句话的下面修改CC,CPP,LD,RANLIB,STRIP和NDK_PLATFORM这几个变量的值改成交叉编译器和ndk所在的路径就行了。
下面是我的机器上的配置
大家只要改成自己对应的路径就行了。
配置完Makefile后,接着生成com_sun_jna_Native.h文件
由于下载的master.zip里面native目录下缺少这个文件,所以导致编译失败所以需要自己生成。
用javah目录就可以生成。
javah -classpath "前面eclipse里面生成的jna的class文件所在的目录" -d "生成的头文件存放的路径" com.sun.jna.Native
这样就会在 "生成的头文件存放的路径" 里面生成com_sun_jna_Native.h文件了。将这个头文件传到服务器上。
接着执行在linux的jna目录下执行make all命令
执行完后在jna/build/native下会生成几个so文件,如下图所示
将libjnidispatch.so文件copy出来。
到这里编译完成。
下面介绍怎么在android里面调用JNA了
首先建立一个android工程,在工程里面新建一个叫lib的文件夹,将前面生成的jna.jar文件copy到lib目录里面。
然后右键点击工程-->build path--->configure build path在弹出框里面的libraries选项里面add jars将lib目录里面的jna.jar加进去。
然后在工程里面建立一个叫libs文件夹,然后在libs里面建立一个叫armeabi的文件夹,将libjnidispatch.so文件copy到armeabi文件夹里面。
接着建立一个Activity在java文件里面加上以下代码
static{
try{
System.setProperty("jna.library.path", "/system/lib");
System.loadLibrary("jnidispatch");
}catch(Exception e){
e.printStackTrace();
}
}
其中 System.setProperty("jna.library.path", "/system/lib");表示从/system/lib目录下加载要调用的so文件,如果要调用的so文件在多个路径下,则多个路径用冒号分隔
如:System.setProperty("jna.library.path", "/system/lib:/usr/lib");表示jna会从/system/lib目录和/usr/lib目录下查找so文件。
如果so文件是用户自己编译的,则只需要将so文件放在libs/armeabi目录下就行了,jna会自动查找这个目录下的so文件,不在这个
目录下的so文件需要将路径设置到jna.library.path属性里面,通过System.setProperty("jna.library.path", "/system/lib:/usr/lib:so文件所在的路径");设置即可。
System.loadLibrary("jnidispatch");这条语句是加载jna运行所必需的so文件。
下面是我通过jna调用libc.so里面的malloc函数的代码:
其中libc.so在我的androi手机的/system/lib目录下,所以我设置的so路径为 System.setProperty("jna.library.path", "/system/lib");
首先建立一个叫StandardC的接口,代码如下
import com.sun.jna.Library;
import com.sun.jna.Native;
import com.sun.jna.Pointer;
public interface StandardC extends Library {
StandardC INSTANCE = (StandardC)Native
.loadLibrary("c", StandardC.class);
public Pointer malloc(int size);
}
接着在activity的onCreate方法里面调用:
try{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Pointer p = StandardC.INSTANCE.malloc(4);
p.setInt(0, 4);
Toast.makeText(this, "value="+p.getInt(0)+" and malloc(4)="+Pointer.nativeValue(p), 1).show();
}catch(Throwable t){
t.printStackTrace();
}
将生产的apk放在手机里面运行,运行成功的话,会弹出一个提示,里面的内容是value=4 and malloc(4)=内存地址的值。
到此,android上移植jna和调用jna就全部完成了。