JNI學習之一:怪異的JNI報錯:Caused by: java.lang.UnsatisfiedLinkError

最近開始研究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.so
		System.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中平臺的本地庫。
=========================================================補充內容的結束線==================================================

發佈了14 篇原創文章 · 獲贊 3 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章