您的位置:首页 > 其它

关于NDK的若干入门问题

2014-05-19 21:23 197 查看
国内博客各种抄,不知道浪费了新手多少时间走弯路,操蛋。

使用NDK,就是希望能在java代码里调用下层的so库。

一个普通的eclipse安卓工程,基本是这些文件夹

,对应

,因为ndk编译依赖文件结构(ndk会解析上层AndroidManifest.xml文件,会在libs/和obj/下生成文件,所以jni文件夹要放在工程的根目录下),所以如果是gradle工程,为了确保正确生成,还是请在eclipse下重新实现一次。

确保NDK路径在path里,方便以后直接输入ndk-bulid。在你的工程目录下新建一个jni文件夹,放入你的c或者cpp(函数名自然是jni格式的函数名,以配合jni调用的本地函数)。

回过头说说javah,这里有几个经验,一是找不到android.app.*类时,可以用-bootclasspath指定出来:javah -classpath bin/classes -bootclasspath/home/liuzx/workspace/appcompat_v7/libs/android-support-v7-appcompat.jar:/home/liuzx/workspace/appcompat_v7/libs/android-support-v4.jar:/home/liuzx/adt-bundle-linux-x86_64-20140321/sdk/platforms/android-19/android.jar
-d jni com.abc.def.MainActivity,这是我碰到问题之后的解决方法,上面的几个jar都在工程的appcompat_v7文件夹里,而android.jar在sdk里,jar之间用:隔开。当然,如果安卓程序只是简单的继承一个Activity,那就不会提示缺少前面两个包了。

二是完全没有找到类,提示类名不存在,那就是路径没有写对。像javah -classpath bin/classes 说明com/..../..../..Activity.class是在bin/classes/下面的,-d jni生成的文件会保存到当前文件夹下的jni里,当前文件夹就是工程根目录,实在没有办法了,也可以不从class直接从java代码生成.h文件:javah -d ../jni com.abc.def.MainActivity,自然是在bin/classes下运行这个命令。

之后写一个Android.mk的makefile文件(NDK下的sample下有很多示例工程,可以直接用eclipse导入,一边修改一边理解。安卓的makefile文件与linux语法规则一致,如果你有复杂的编译要求,请深究,否则直接参考hellojni的写法即可),最后在jni文件夹下ndk-build回车,即可在上层的libs/armeabi里生成lib???.so文件了。

当然这些步骤的前提是你在Java代码里写了本地方法的定义public native....,不然javah可没法分析出jni的函数名称,你也就没法在c,cpp里写函数了。

如果真机不是arm的cpu,那么so文件要放在libs/mips或者libs/x86里,不然会报找不到so文件的错的。在jni下建立一个Application.mk,写入APP_ABI := all就可以生成全部类型的so了。

如果你一直提示找不到so,先将hellojni的so拿过来试试,不定义本地方法也行,只是测试

static {
        System.loadLibrary("hello-jni");
    }


是否可用,一般情况下,正确的so正确地放在libs/对应cpu文件夹下,是可以正确引入的,当然你不能调用,因为so里面的函数名是以hellojni的包名写的Java_com_example_test_MainActivity_stringFromJNI

你要用得把工程路径改成和他一样或者改c里面的源码,把函数名改成javah分析你的工程之后得到的函数名,才能调用,否则还是会出error。基本上ndk入门会碰到的问题就是这些了,本来门槛不高,可是网上搜到的资料实在坑,门槛就高了,还是希望有更多有价值的分享。

还有,在将一个apk卸载前,data/data/com.abc.def/lib/下面的so文件只要被创建了就不会自动删除的,所以有些人碰到的莫名其妙的没有导入so却依然可以调用的情况,原因就是你之前导入过。

又想到一个,当你要使用c++文件时,只需在mk里把文件名改为.cpp就行,但是实现时要注意,一是env的写法,二是在函数名的定义上外头加一个extern "C"框起来,这样编译会按照c的函数编译方法完成,不会因为c++的重载特性导致编译的函数无法被jni识别从而又出现java.lang.UnsatisfiedLinkError: stringFromJNI错误。当然你也可以把javah生成的.h包括到你的本地代码里,因为在.h文件里,就有这个结构:

#ifdef __cplusplus
extern "C" {
#endif
//code
#ifdef __cplusplus
}
#endif
这样就可以避免jni找不到相应函数的问题了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: