JNI学习之一:怪异的JNI报错:Caused by: java.lang.UnsatisfiedLinkError

来源:互联网 发布:电玩巴士淘宝 编辑:程序博客网 时间:2024/06/07 08:49

最近开始研究Android NDK了,参照教程一步一步写:

(吐个槽:貌似AndroidStudio还不能用来做JNI,于是只好又回到我的eclipse平台上来,谷歌的工程师么,你们倒是赶紧的给studio加上这个功能啊)

1、新建一个工程NDKHelloWorld

2、新建一个folder,名称jni,在其中新建一个c代码文件,Hello.c。

3、Hello.c的内容:

#include <stdio.h>#include <jni.h>jstring Java_sungoku_ndkhelloworld_DemoActivity_helloFromJni(JNIEnv* env, jobject obj) {return (*(*env)).NewStringUTF(env, "hello world, jni.");}

就是大家都喜闻乐见的helloworld。。。注意那个奇怪无比的函数名,开头必须是Java,然后是程序的包名,然后是调用函数的那个Activity名,最后是函数真正的名字。

4、添加Android.mk文件,内容如下:

   LOCAL_PATH := $(call my-dir)   include $(CLEAR_VARS)   LOCAL_MODULE    := Hello   LOCAL_SRC_FILES := Hello.c   include $(BUILD_SHARED_LIBRARY)

这里指定模块名称是Hello,要编译的源文件是Hello.c,注意大小写。

5、好了,赶紧用ndk-build命令交叉编译一下,嗯,还好,编译通过,比较顺利啊。这时,工程目录下的libs目录里面应该出现armeabi目录,可以看到我们的JNI模块已经被编译成了libHello.so,是个二进制文件。这个模块的文件名字就是根据mk中指定的模块名,加上lib前缀和.so扩展名组成的。

6、接下来,回到Android的Java代码部分,在DemoActivity中加入静态代码块,加载刚刚编译好的本地库,并且声明一下本地方法,注意方法名称的一致。

static{//libHello.soSystem.loadLibrary("Hello");}public native String helloFromJni();

7、添加一个Button,让应用点击这个按钮就调用本地方法,显示一个Toast:

Toast.makeText(this, helloFromJni(), Toast.LENGTH_LONG).show();
好了,该部署测试一下了。嗯???模拟器运行应用报错了。

10-06 01:48:38.507: ERROR/AndroidRuntime(1442): Caused by: java.lang.UnsatisfiedLinkError: Couldn't load Hello: findLibrary returned null
神马情况?仔细检查了一下,木有问题啊。于是我想到导入NDK的示例代码试试。导入那个hellojni的示例代码。run as android application,神奇的事情发生了,编译就报错了。
[2014-10-06 10:19:57 - Dex Loader] Unable to execute dex: java.nio.BufferOverflowException. Check the Eclipse log for stack trace.[2014-10-06 10:19:57 - HelloJni] Conversion to Dalvik format failed: Unable to execute dex: java.nio.BufferOverflowException. Check the Eclipse log for stack trace.

我心里千万只草泥马奔腾而过。。。

这个意思似乎是说eclipse出错,缓冲区溢出?经过折腾以及百度之后,好吧,导入的工程出这个问题,从build path中去掉AndroidDependences就好了。不过我自己实验发现在工程属性中把buildTarget改成4.4也行。

然后,再次编译,运行。呃!!!还是出错!!!

和我之前自己一步一步写的工程一样的错误,加载本地库时找不到本地库。我看了看hellojni工程目录,发现了问题,没有生成那个libs/armeabi目录,也就是说没有对本地代码部分编译。难道Eclipse的build project不能自动编译工程的JNI代码?那我就手动吧,ndk-build一下。

然后,我再颤抖着点下运行,你能相信我看到模拟器运行出错,还是提示找不到本地库的时候,我的心情吗?大概相当于被无限的草泥马踩踏至死的感觉吧。

为毛?!?!没有那里出问题,怎么就是不行?

好吧,我去问问度娘。度娘的回答基本上属于就是名称写错啊,编译不对啊等等之类的,后来记不清在哪里看到一个提问者提描述问题时提到自己的模拟器用的x86镜像,我顿时心中闪过一道闪电啊,万千草泥马都被这道闪电劈死了有木有。

是啊,为了让模拟器运行快一点,我一直以来都是用的x86镜像,加上intel的HAX加速。而现在我用ndk-build出来的本地库是基于arm平台的运行库啊,我应该切换到armeabi镜像的模拟器运行才是。

想到就赶紧尝试。

这回终于都好了。忍不住狂笑一声,记录下来这个过程吧,希望能让同我一样遭遇的哥们有个提示就好。


=========================================================补充内容的分割线==================================================
如果就想编译出x86架构的运行库怎么办呢?两个方法:

1、编写application.mk文件,在其中的APP_ABI 行加入 x86这个选项。

例如:APP_ABI := armeabi armeabi-v7a x86   这里是可以添加好几个选项的,如果不添加这个APP_ABI选项,默认编译的是armeabi平台。
2、如果只是简单的程序,那么也可以不用编写完整的application.mk文件,在使用ndk-build命令的时候加入这个参数:
> ndk-build   APP_ABI="armeabi armeabi-v7a x86"
然后同样会编译生成3中平台的本地库。
=========================================================补充内容的结束线==================================================

0 0