閱讀970 返回首頁    go 阿裏雲 go 技術社區[雲棲]


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

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

        首先上麵的部分是2014年的時候寫的,也比較老了,還在用cywin等。

Android studio的ndk的正確姿勢

1,環境配置
首先我們要確認我們的sdk目錄下包含ndk的環境,安裝好的NDk一般位於你的sdk文件夾下的ndk-bundle。如若沒有的直接更新下就好了。我們需要對ndk的路徑進行環境配置



在係統環境變量裏麵創建NDK_HOME,然後將將NDK_ROOT追加到Path環境變量下-->;%NDK_HOME%。
使用ndk-build命令,如果出現下麵的畫麵說明環境變量配置成功。

接下來我們開始真正的寫代碼的了,很簡單。

1,新建項目,修改build.gradle腳本文件(加入ndk的支持)
defaultConfig {
        applicationId "com.xzh.ndkdemo"
        minSdkVersion 10
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

        ndk{
            moduleName "NDKDEMO"         //生成的so名字
            ldLibs "log", "z", "m"       //鏈接時使用到的庫
            abiFilters "armeabi", "armeabi-v7a", "x86"  //輸出指定三種abi體係結構下的so庫。目前可有可無
        }
    }
同時需要修改下gradle.properties的配置,加入如下代碼
android.useDeprecatedNdk=true
2,新建MainActivity,新增本地Native方法(為了簡單,我們隻做一個hello word的調用,複雜情況就不在這裏講解)

 public native String getStringFromNative();
3,新建jni文件

3.編譯該類得到對應的.h文件,打開studio自帶的Terminal麵板,javah -jni 具體的類名路徑,如我這裏的就是
由於我這裏編譯不成功,所以我就拷貝了之前的。
其實編譯成功後,刷新下工程可以看到編譯出的.h文件,該文件隻是為了輔助我們寫出相應的.c文件,使用完了即可刪除。如果你對C比較了解,可以自己手寫,或者找下會c的同事。




最後更新:2017-04-03 05:40:02

  上一篇:go 取出視頻中有移動物體的幀
  下一篇:go [Hibernate開發之路](3)基礎配置