android ndk之hello world
前言:Android NDK r7及以上的版本已經集成了Cygwin編譯環境,也就是說,我們完全可以拋棄龐大的Cygwin了。
r6及以下版本,也可以拋棄幾個G的完整版,使用精簡過的Mini-Cygwin來編譯,解壓後大小僅9M,但短小巧精悍,完全可以滿足Android NDK的開發。
下載地址:https://code.google.com/p/mini-cygwin/
Eclipse集成Cygwin編譯環境可以參考我的這篇貼:https://blog.csdn.net/codezjx/article/details/8858825
下麵進入正題,r7及以上版本,跟著我的這篇帖子,完成環境的升級吧!!!
參考官網:https://tools.android.com/recent/usingthendkplugin
1、首先確認自己的ADT版本,NDK plugin的支持是在ADT 20及以後的版本。
2、安裝Android Native Development Tools(該組件集成C/C++開發環境),既在安裝ADT工具的時候勾選NDK組件,一路next後重啟Eclipse使插件生效。
3、下載NDK工具https://developer.android.com/tools/sdk/ndk/index.html,我使用的是最新的android-ndk-r8e-windows-x86.zip,下載完後解壓縮。
4、Eclipse -> Window -> Preferences -> Android -> NDK,設置NDK為剛剛解壓縮的工具包路徑。
侯注:做到這裏時,控製台報出了一個錯誤:“Unable to launch cygpath. Is Cygwin on the path”,因為我的ndk是之前安裝的,並沒有專門設置環境變量。按照這個鏈接中的引導解決:
-
Head to the project's properties. If you're using Windows, the shortcut is
Alt + Enter
; or simply right-click the project name to find its properties. -
Go to the
C/C++ Build
section; underBuilder Settings
tab inBuild command:
text box you're likely to find something similar to that below, if it's empty then type in the similar text - namely:${NDKROOT}/ndk-build.cmd
whereNDKROOT
, as its name implies, refers to the path where your NDK root folder exists. -
Now you must inform eclipse what
NDKROOT
equates to; as in, where is the NDK root path. You can do this by heading to (in your project's properties)C/C++ Build
>Environment
> pressAdd…
-
Now add your environment variable named NDKROOT (the
Name
) with the relevant path (Value
). Note that you're to repeat this per NDK project. You would get a window similar to that below. -
Press
OK
to the New variable input window and thenOK
again to the properties window.
5、NDK環境基本上已經搭建好,新建一個普通Android項目測試NDK支持。項目右鍵->Android Tools->Add Native Support...,輸入.so庫名字後點擊Finish
(注意:若項目已經是一個Jni項目,例如NDK example裏麵的HelloJni,這一步中.so庫名字不用填)
6、現在已經可以Build我們的Jni項目了,選擇項目,Project->Build Project,編譯我們的c/c++代碼,此時項目結構如下,NDK plugin已經為我們添加好了include,已經為我們生成了相應的Android.mk以及 cpp文件。(注意:這裏插件為我們生成的是cpp文件,若你不喜歡可以改回.c,並在mk文件中進行相應更改)
7、這時,Android NDK環境已經完美搭建好,我們可以在cpp文件中流暢地書寫我們的c/c++代碼。
(而且當你Alt+/時,已經可以使用自動提示,各種爽歪歪有木有。若你不是用NDK plugin來構建JNI項目,而是選擇手動方式,Alt+/是不會提示的)
8、關於編譯,默認情況下:選擇項目,Project->Build Project,來重新編譯我們的代碼。而且每次run項目的時候,也會自動編譯.so庫。
一些問題與解決方法:
問題一:Android NDK: WARNING: APP_PLATFORM android-14 is larger than android:minSdkVersion 8 in ./AndroidManifest.xml (這個是NDK工具的一個BUG,若build Target大於minSdkVersion,則會報這個錯誤,導致無法運行)
解決方法:
android-ndk-r8e/build/core/add-application.mk第128行把__ndk_warning改為__ndk_info;然後重新build一次項目即可消除錯誤。
原文:
this problem may be safely fixed by changing this line in add-application.mk from __ndk_warning to __ndk_info
鏈接:
https://code.google.com/p/android/issues/detail?id=39752
問題二:使用c++來編寫本地庫,會有一些兼容問題。
(1)直接黏貼HelloJni的stringFromJNI函數過來測試,提示Method 'NewStringUTF' could not be resolved
解決方法:
改為:將(*env)->NewStringUTF(env, "Hello from JNI !")改為return env->NewStringUTF("Hello from JNI !")即可
原因是:
NDK plugin默認為我們生成的是cpp文件,而C與C++調用函數的參數不一致,因此找不到函數,具體參考jni.h中的定義。cpp文件中形如(*env)->Method(env, XXX)改成env->Method(XXX)即可。
(2)運行c++生成的.so庫,若報以下錯誤:(既找不到函數)
No implementation found for native Lcom/dgut/android/MainActivity;.stringFromJNI ()Ljava/lang/String;
java.lang.UnsatisfiedLinkError: stringFromJNI
at com.dgut.android.MainActivity.stringFromJNI(Native Method)
解決方法:
為供Java調用的c++函數前加入extern "C" 修飾,如:(NDK example裏麵的cpp文件也是這麼聲明的,參考hello-gl2)
- extern "C" {
- JNIEXPORT jstring JNICALL Java_com_dgut_android_MainActivity_stringFromJNI( JNIEnv* env, jobject thiz );
- }
- JNIEXPORT jstring JNICALL Java_com_dgut_android_MainActivity_stringFromJNI( JNIEnv* env, jobject thiz )
- {
- return env->NewStringUTF("Hello from JNI bear c++");
- }
被extern "C"修飾的變量和函數是按照C語言方式編譯和連接的。
首先看看C++中對類似C的函數是怎樣編譯的:作為一種麵向對象的語言,C++支持函數重載,而過程式語言C則不支持。函數被C++編譯後在符號庫中的名字與C語言的不同。例如,假設某個函數的原型為:void foo( int x, int y );該函數被C編譯器編譯後在符號庫中的名字為_foo,而C++編譯器則會產生像_foo_int_int之類的名字(不同的編譯器可能生成的名字不同,但是都采用了相同的機製,生成的新名字稱為“mangled
name”)。_foo_int_int這樣的名字包含了函數名、函數參數數量及類型信息,C++就是靠這種機製來實現函數重載的。例如,在C++中,函數voidfoo( int x, int y )與void foo( int x, float y )編譯生成的符號是不相同的,後者為_foo_int_float。
同樣地,C++中的變量除支持局部變量外,還支持類成員變量和全局變量。用戶所編寫程序的類成員變量可能與全局變量同名,我們以"."來區分。而本質上,編譯器在進行編譯時,與函數的處理相似,也為類中的變量取了一個獨一無二的名字,這個名字與用戶程序中同名的全局變量名字不同。
因此,若我們沒有使用extern "C"修飾函數,按照C語言方式編譯和連接,Jni調用將可能找不到該函數。
最後更新:2017-04-03 05:40:02