Android新版NDK環境配置(免Cygwin)

原文鏈接:http://blog.csdn.net/codezjx/article/details/8879670


前言:Android NDK r7及以上的版本已經集成了Cygwin編譯環境,也就是說,我們完全可以拋棄龐大的Cygwin了。


r6及以下版本,也可以拋棄幾個G的完整版,使用精簡過的Mini-Cygwin來編譯,解壓後大小僅9M,但短小巧精悍,完全可以滿足Android NDK的開發。

下載地址:https://code.google.com/p/mini-cygwin/

Eclipse集成Cygwin編譯環境可以參考我的這篇貼:http://blog.csdn.net/codezjx/article/details/8858825



下面進入正題,r7及以上版本,跟着我的這篇帖子,完成環境的升級吧!!!

參考官網:http://tools.android.com/recent/usingthendkplugin

1、首先確認自己的ADT版本,NDK plugin的支持是在ADT 20及以後的版本。

2、安裝Android Native Development Tools(該組件集成C/C++開發環境),既在安裝ADT工具的時候勾選NDK組件,一路next後重啓Eclipse使插件生效。


3、下載NDK工具http://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是之前安裝的,並沒有專門設置環境變量。按照這個鏈接中的引導解決:

  1. 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.

  2. Go to the C/C++ Build section; under Builder Settings tab in Build 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 where NDKROOT, as its name implies, refers to the path where your NDK root folder exists. enter image description here

  3. 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 > press Add…

  4. 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. enter image description here

  5. Press OK to the New variable input window and then OK 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)

[java] view plaincopy
  1. extern "C" {  
  2.     JNIEXPORT jstring JNICALL Java_com_dgut_android_MainActivity_stringFromJNI( JNIEnv* env, jobject thiz );  
  3. }  
  4.   
  5. JNIEXPORT jstring JNICALL Java_com_dgut_android_MainActivity_stringFromJNI( JNIEnv* env, jobject thiz )  
  6. {  
  7.     return env->NewStringUTF("Hello from JNI bear c++");  
  8. }  
原因是:

        被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調用將可能找不到該函數。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章