『壹』 android-ndk-r12b 在eclipse上怎麼配置環境變數
一.插件的安裝
如果你下載的是Eclipse
Standard,請下載對應版本的cdt(我直接使用的是Eclipse
IDE
for
C/C++
Developers
不要安裝cdt的哦,我人比較懶,(*^__^*)
嘻嘻……)
比如
CDT
8.1.2
for
Eclipse
Juno
cdt-master-8.1.2.zip,插入也比較簡單和adt安裝方式一樣。再次不在贅述。以前NDK的版本還要Sequoyah插件(好久不更新了)來進行本地開發,現在不需要了。
二.環境的配置
1).首先進行配置NDK的配置,打開Eclipse的
Window->preferences->Android->NDK
然後選擇路徑你的NDK路徑我的目錄如下D:\android-sdk\android-ndk-r9
這個目錄下有docs,sources,tests,samples
e等等。
然後點擊Apply,OK即可
2).首先導入hello—jni工程,然後點擊右鍵->Properties你看見如下圖所示的界面,這不是一個可以編譯的NDK工程。你需要運行Cygwin或者在Eclipse配置。
我們首先使用Cygwin來生成程序的.so文件。打開Cygwin運行命令cd
$ndk
這個ndk是之前你配置Cygwin環境變數設置的字元。然後cd
samples/hello-jni
然後$ndk/ndk-build,你就可以看見生成.so文件成功的頁面,這時候刷新或者clean工程
點擊run
as運行android程序。如下圖:
三.Eclipse
工程配置在Eclipse中開發c/c++
第二步運行完之後,你會發現你把Hello
from
JNI修改為AAA(隨便寫的)
這個字元串不好使,輸出之後還是Hello
from
JNI
。你需要再次運行Cygwin才可以。當然這樣比較麻煩,你可以在Eclipse中配置一下,就不要在運行Cygwin了。直接運行eclipse就可以了。
1)在HelloJni上右鍵->New->other(這一步一定是other)->c/c++>Convert
to
a
C/C++
Project(Adds
c/c++
Nature)->Next
然後選擇你的工程HelloJni,選擇轉化為c工程,選擇Makefile
Project
Cygwin
GCC,點擊Finish,再次點擊Finish即可
2)你發現你的工程.c文件有很多錯誤。這個時候點擊工程右鍵->Propreties這個時候你會發現和第二步的2)圖多了二項
c/c++
Build
和c/c++
General.
點擊
c/c++
Build
Use
defult
build
commond的勾去掉
在Build
command
填入bash
<NDK路徑>\ndk-build
例如bash
D:\android-sdk\android-ndk-r9\ndk-build點擊Apply
OK即可。
點擊c/c++
General
打開列表點擊Paths
and
Symbols
在Includes
你看見Assembly
GNU
C
(如果你在1)步選擇的C++project還會有GNU
C++)
.這個時候要選擇你所要的依賴庫,由於hello-jni不需要過多的庫文件所以
只需要點擊Add
D:\android-sdk\android-ndk-r9\platforms\android-9\arch-arm\usr\include
這樣你就可以直接修改在Run
as了。Ok成功運行。
『貳』 如何定位Android NDK開發中遇到的錯誤
NDK編譯生成的.so文件作為程序的一部分,在運行發生異常時同樣會造成程序崩潰。不同於java代碼異常造成的程序崩潰,在NDK的異常發生時,程序在Android設備上都會立即退出,即通常所說的閃退,而不會彈出「程序xxx無響應,是否立即關閉」之類的提示框。
NDK是使用C/C++來進行開發的,熟悉C/C++的程序員都知道,指針和內存管理是最重要也是最容易出問題的地方,稍有不慎就會遇到諸如內存無效訪問、無效對象、內存泄露、堆棧溢出等常見的問題,最後都是同一個結果:程序崩潰。例如我們常說的空指針錯誤,就是當一個內存指針被置為空(NULL)之後再次對其進行訪問;另外一個經常出現的錯誤是,在程序的某個位置釋放了某個內存空間,而後在程序的其他位置試圖訪問該內存地址,這就會產生一個無效地址錯誤。常見的錯誤類型如下:
初始化錯誤
訪問錯誤
數組索引訪問越界
指針對象訪問越界
訪問空指針對象
訪問無效指針對象
迭代器訪問越界
內存泄露
參數錯誤
堆棧溢出
類型轉換錯誤
數字除0錯誤
利用Android NDK開發本地應用的時候,幾乎所有的程序員都遇到過程序崩潰的問題,但它的崩潰會在logcat中列印一堆看起來類似天書的堆棧信息,讓人舉足無措。單靠添加一行行的列印信息來定位錯誤代碼做在的行數,無疑是一件令人崩潰的事情。在網上搜索「Android NDK崩潰」,可以搜索到很多文章來介紹如何通過Android提供的工具來查找和定位NDK的錯誤,但大都晦澀難懂。下面以一個實際的例子來說明,首先生成一個錯誤,然後演示如何通過兩種不同的方法,來定位錯誤的函數名和代碼行。
首先,看我們在hello-jni程序的代碼中做了什麼(有關如何創建或導入工程,此處略),看下圖:在JNI_OnLoad()的函數中,即so載入時,調用willCrash()函數,而在willCrash()函數中,std::string的這種賦值方法會產生一個空指針錯誤。這樣,在hello-jni程序載入時就會閃退。我們記一下這兩個行數:在61行調用了willCrash()函數;在69行發生了崩潰。
下面來看看發生崩潰(閃退)時系統列印的logcat日誌:
[plain] view plain
************************************************
Buildfingerprint:'vivo/bbk89_cmcc_jb2/bbk89_cmcc_jb2:4.2.1/JOP40D/1372668680:user/test-keys'
pid:32607,tid:32607,name:xample.hellojni>>>com.example.hellojni<<<
signal11(SIGSEGV),code1(SEGV_MAPERR),faultaddr00000000
backtrace:
#00pc00023438/system/lib/libc.so
#01pc00004de8/data/app-lib/com.example.hellojni-2/libhello-jni.so
#02pc000056c8/data/app-lib/com.example.hellojni-2/libhello-jni.so
#03pc00004fb4/data/app-lib/com.example.hellojni-2/libhello-jni.so
#04pc00004f58/data/app-lib/com.example.hellojni-2/libhello-jni.so
#05pc000505b9/system/lib/libdvm.so
#06pc00068005/system/lib/libdvm.so
#07pc000278a0/system/lib/libdvm.so
#08pc0002b7fc/system/lib/libdvm.so
#09pc00060fe1/system/lib/libdvm.so
#10pc0006100b/system/lib/libdvm.so
#11pc0006c6eb/system/lib/libdvm.so
#12pc00067a1f/system/lib/libdvm.so
#13pc000278a0/system/lib/libdvm.so
#14pc0002b7fc/system/lib/libdvm.so
#15pc00061307/system/lib/libdvm.so
#16pc0006912d/system/lib/libdvm.so
#17pc000278a0/system/lib/libdvm.so
#18pc0002b7fc/system/lib/libdvm.so
#19pc00060fe1/system/lib/libdvm.so
#20pc00049ff9/system/lib/libdvm.so
#21pc0004d419/system/lib/libandroid_runtime.so
#22pc0004e1bd/system/lib/libandroid_runtime.so
#23pc00001d37/system/bin/app_process
#24pc0001bd98/system/lib/libc.so
#25pc00001904/system/bin/app_process
stack:
beb12340012153f8
beb1234400054290
beb1234800000035
beb1234cbeb123c0[stack]
……
如果你看過logcat列印的NDK錯誤時的日誌就會知道,我省略了後面很多的內容,很多人看到這么多密密麻麻的日誌就已經頭暈腦脹了,即使是很多資深的Android開發者,在面對NDK日誌時也大都默默的選擇了無視。
「符號化」NDK錯誤信息的方法
其實,只要你細心的查看,再配合Google提供的工具,完全可以快速的准確定位出錯的代碼位置,這個工作我們稱之為「符號化」。需要注意的是,如果要對NDK錯誤進行符號化的工作,需要保留編譯過程中產生的包含符號表的so文件,這些文件一般保存在$PROJECT_PATH/obj/local/目錄下。
第一種方法:ndk-stack
這個命令行工具包含在NDK工具的安裝目錄,和ndk-build和其他一些常用的NDK命令放在一起,比如在我的電腦上,其位置是/android-ndk-r9d/ndk-stack。根據Google官方文檔,NDK從r6版本開始提供ndk-stack命令,如果你用的之前的版本,建議還是盡快升級至最新的版本。使用ndk –stack命令也有兩種方式
使用ndk-stack實時分析日誌
在運行程序的同時,使用adb獲取logcat日誌,並通過管道符輸出給ndk-stack,同時需要指定包含符號表的so文件位置;如果你的程序包含了多種CPU架構,在這里需求根據錯誤發生時的手機CPU類型,選擇不同的CPU架構目錄,如:
[plain] view plain
adbshelllogcat|ndk-stack-sym$PROJECT_PATH/obj/local/armeabi
當崩潰發生時,會得到如下的信息:
[plain] view plain
**********Crashmp:**********
Buildfingerprint:'vivo/bbk89_cmcc_jb2/bbk89_cmcc_jb2:4.2.1/JOP40D/1372668680:user/test-keys'
pid:32607,tid:32607,name:xample.hellojni>>>com.example.hellojni<<<
signal11(SIGSEGV),code1(SEGV_MAPERR),faultaddr00000000
Stackframe#00pc00023438/system/lib/libc.so(strlen+72)
Stackframe#01pc00004de8/data/app-lib/com.example.hellojni-2/libhello-jni.so(std::char_traits<char>::length(charconst*)+20):Routinestd::char_traits<char>::length(charconst*)at/android-ndk-r9d/sources/cxx-stl/stlport/stlport/stl/char_traits.h:229
Stackframe#02pc000056c8/data/app-lib/com.example.hellojni-2/libhello-jni.so(std::basic_string<char,std::char_traits<char>,std::allocator<char>>::basic_string(charconst*,std::allocator<char>const&)+44):Routinebasic_stringat/android-ndk-r9d/sources/cxx-stl/stlport/stlport/stl/_string.c:639
Stackframe#03pc00004fb4/data/app-lib/com.example.hellojni-2/libhello-jni.so(willCrash()+68):RoutinewillCrash()at/home/testin/hello-jni/jni/hello-jni.cpp:69
Stackframe#04pc00004f58/data/app-lib/com.example.hellojni-2/libhello-jni.so(JNI_OnLoad+20):RoutineJNI_OnLoadat/home/testin/hello-jni/jni/hello-jni.cpp:61
Stackframe#05pc000505b9/system/lib/libdvm.so(dvmLoadNativeCode(charconst*,Object*,char**)+516)
Stackframe#06pc00068005/system/lib/libdvm.so
Stackframe#07pc000278a0/system/lib/libdvm.so
Stackframe#08pc0002b7fc/system/lib/libdvm.so(dvmInterpret(Thread*,Methodconst*,JValue*)+180)
Stackframe#09pc00060fe1/system/lib/libdvm.so(dvmCallMethodV(Thread*,Methodconst*,Object*,bool,JValue*,std::__va_list)+272)
……(後面略)
我們重點看一下#03和#04,這兩行都是在我們自己生成的libhello-jni.so中的報錯信息,那麼會發現如下關鍵信息:
[plain] view plain
#03(willCrash()+68):RoutinewillCrash()at/home/testin/hello-jni/jni/hello-jni.cpp:69
#04(JNI_OnLoad+20):RoutineJNI_OnLoadat/home/testin/hello-jni/jni/hello-jni.cpp:61
回想一下我們的代碼,在JNI_OnLoad()函數中(第61行),我們調用了willCrash()函數;在willCrash()函數中(第69行),我們製造了一個錯誤。這些信息都被准確無誤的提取了出來!是不是非常簡單?
先獲取日誌,再使用ndk-stack分析
這種方法其實和上面的方法沒有什麼大的區別,僅僅是logcat日誌獲取的方式不同。可以在程序運行的過程中將logcat日誌保存到一個文件,甚至可以在崩潰發生時,快速的將logcat日誌保存起來,然後再進行分析,比上面的方法稍微靈活一點,而且日誌可以留待以後繼續分析。
[plain] view plain
adbshelllogcat>1.log
ndk-stack-sym$PROJECT_PATH/obj/local/armeabi–mp1.log
第二種方法:使用addr2line和objmp命令
這個方法適用於那些,不滿足於上述ndk-stack的簡單用法,而喜歡刨根問底的程序員們,這兩個方法可以揭示ndk-stack命令的工作原理是什麼,盡管用起來稍微麻煩一點,但是可以滿足一下程序員的好奇心。
先簡單說一下這兩個命令,在絕大部分的linux發行版本中都能找到他們,如果你的操作系統是linux,而你測試手機使用的是Intel x86系列,那麼你使用系統中自帶的命令就可以了。然而,如果僅僅是這樣,那麼絕大多數人要絕望了,因為恰恰大部分開發者使用的是Windows,而手機很有可能是armeabi系列。
別急,在NDK中自帶了適用於各個操作系統和CPU架構的工具鏈,其中就包含了這兩個命令,只不過名字稍有變化,你可以在NDK目錄的toolchains目錄下找到他們。以我的Mac電腦為例,如果我要找的是適用於armeabi架構的工具,那麼他們分別為arm-linux-androideabi-addr2line和arm-linux-androideabi-objmp;位置在下面目錄中,後續介紹中將省略此位置:
[plain] view plain
/Developer/android_sdk/android-ndk-r9d/toolchains/arm-linux-androideabi-4.8/prebuilt/darwin-x86_64/bin/
假設你的電腦是windows,CPU架構為mips,那麼你要的工具可能包含在這個目錄中:
[plain] view plain
D:android-ndk-r9d oolchainsmipsel-linux-android-4.8prebuiltwindows-x86_64in
『叄』 android-ndk-r9d 32位系統能用么嗎
android-ndk-r9d 32位系統可以用。
cpu的位是指一次性可處理的數據量是多少,1位元組=8位,32位處理器可以一次性處理4個位元組的數據量,依次類推。32位操作系統針對的32位的CPU設計。64位操作系統針對的64位的CPU設計。操作系統只是硬體和應用軟體中間的一個平台。CPU從原來的8位,16位,到現在的32位和64位。
『肆』 cocos2dx在windows下開發怎麼編譯成apk
下面一段是網路的說明:
Cocos2d-x 是一個支持多平台的 2D 手機游戲引擎,使用 C++ 開發,基於OpenGL ES,基於Cocos2d-iphone,支持 WOPhone, iOS 4.1, Android 2.1 及更高版本, WindowsXP & Windows7,WindowsPhone 8.[1]Cocos2d-x是一個開源的移動2D游戲框架,MIT許可證下發布的。這是一個C++ Cocos2d-iPhone項目的版本。Cocos2d-X發展的重點是圍繞Cocos2d跨平台。Cocos2d-x提供的框架外,手機游戲,可以寫 在C++或者Lua中,使用API是Cocos2d-iPhone完全兼容。Cocos2d-x[2]項目可以很容易地建立和運行iOS,Android 的三星Bada,黑莓Blackberry操作系統和更多。Cocos2d-x還支持Windows、Mac和Linux等桌面操作系統,因此,我們可以 編寫的源代碼很容易在桌面操作系統中編輯和調試。
總的來說,cocos2dx的android交叉編譯環境還是蠻麻煩的。下面內容就是我一步一步實現的過程。
第一部分 安裝
一、Cygwin安裝
這個安裝比較簡單,網上也有大把的例子,這里我只是簡述一下。
首先去官網www.cygwin.com下載安裝,安裝包分32位和64位兩種.視情況選擇了。因為只是編譯,這里選擇的是32位版本。
下載地址:http://cygwin.com/setup-x86.exe
然後默認安裝,在安裝源的時候,選擇
http://mirrors.sohu.com/cygwin 或 http://mirrors.163.com/cygwin ,哪個好用,就哪個。
安裝的時候,把開發包都選上就可以了。
我這里是把cygwin安裝在c:\cygwin目錄下面了。
註:這里默認的vi不是我們常用的vim,使用起來會很不習慣,你可以根據實際情況下載,在安裝的時候,選擇編輯器的vim,然後就可以用了。如果沒有vim,你用的時候在vi命令行輸入set nocp,就可以正常習慣使用了。
二、 adt-bundle安裝
現在adt-bundle都集成了下面的功能,這樣就不用再去配置eclipse插件了。
l Eclipse + ADT plugin
l Android SDK Tools
l Android Platform-tools
l The latest Android platform
l The latest Android system image for the emulator
下載地址:
Adt-bundle也同樣分32位和64位的,在這里照樣下載32位的版本
http://dl.google.com/android/adt/adt-bundle-windows-x86-20131030.zip。64位的地址我也加上http://dl.google.com/android/adt/adt-bundle-windows-x86_64-20131030.zip
下載完成後,解壓就可以使用。我這里是解壓到e:\adt-bundle
裡面有eclipse,sdk兩個目錄和一個SDK Manager管理軟體。運行SDK Manager可以下載你需要的SDK版本。
注意:你下載的java也要對應的32位和64位的環境,否則eclipse將會啟動不了。牢騷一句,oracle把java和mysql並入後,真是開源的最大悲劇。你去下載它,就明白了。祝願oracle早點倒閉。
三、ndk安裝
下載地址:
32位下載地址
http://dl.google.com/android/ndk/android-ndk-r9b-windows-x86-legacy-toolchains.zip
http://dl.google.com/android/ndk/android-ndk-r9b-windows-x86.zip
64位下載地址
http://dl.google.com/android/ndk/android-ndk-r9b-windows-x86_64-legacy-toolchains.zip
http://dl.google.com/android/ndk/android-ndk-r9b-windows-x86_64.zip
然後解壓就可以了,我這里解壓到
E:\android-ndk-r9b
四、cocos2dx安裝
我這里下載的版本是cocos2dx v2.2。同時還有一個免費的編輯器CocoStudio,它提供了UI和動畫,資源編輯功能。可以不用mac下面那個收費的軟體了。
今天突然http://www.cocos2d-x.org/怎麼都下載不了,沒辦法,只好到https://github.com/cocos2d/cocos2d-x/這里下載了。找到對應的2.2版本。
Cocos2dx下載地址:
https://codeload.github.com/cocos2d/cocos2d-x/zip/cocos2d-x-2.2
Cocostudio下載地址:
http://d001.download.appget.cn/CocoStudio/DownLoad/v1.0.0.1/CocoStudio_Full_V1.0.0.1.exe
然後cocostudio默認安裝就可以了,我這里把cocos2dx解壓到e:/cocos2dx
五、python安裝
因為,cocos2dx創建工程,需要python,所以這里還需要下載python,去他的官網http://www.python.org/下載就可以了
下載地址:
http://www.python.org/ftp/python/2.7.5/python-2.7.5.msi 這個下載32位版本就可以了。然後默認安裝。我這里安裝到了C:\Python27
六、ant安裝
這個是一個將軟體編譯、測試、部署等步驟聯系在一起加以自動化的一個工具,大多用於Java環境中的軟體開發。這里在生成apk的時候,也用到了。所以也要下載安裝。
http://mirrors.cnnic.cn/apache//ant/binaries/apache-ant-1.9.2-bin.zip
解壓到c:\ant就可以了
到這里,所以需要的軟體,都已經下載,並安裝了。
第二部分:配置
第一步:新建環境變數
點windows的開始菜單-〉計算機 右鍵菜單選擇屬性,彈出屬性對話框。然後選擇高級系統設置,如下圖
然後選擇環境變數
增加
ANT_HOME=C:\ant
PYTHON_HOME=C:\Python27
JAVA_HOME=C:\Program Files (x86)\Java\jdk1.6.0_16
NDK_ROOT=E:\android-ndk-r9b
ANDROID_SDK=E:\adt-bundle\sdk
然後編輯環境變數path,追加 %JAVA_HOME%\bin;%ANT_HOME%\bin;%PYTHON_HOME%;%NDK_ROOT%;%ANDROID_SDK%\tools;%ANDROID_SDK%\platform-tools;
這樣,就可以在CMD窗口執行相關的命令了。
第三部分 cocos2dx的工程准備
第一步:增加android_update.bat
在ant生成apk的時候,會需要知道,你編譯的目標是什麼版本和src.dir等信息。創建這個工程,就是為了自動更新它。批處理的內容是
android update project -p . -t android-10
該文件建好後,命名為android_update.bat,放在E:\cocos2dx\template\multi-platform-cpp\proj.android
注意,請先確認你的ANDROID_SDK是否安裝了對就的版本,android-10是對應的是android2.3,其他的版本請用對應的數字。執行e:\adt-bundle\SDK Manager.exe,如下圖所示:
這里的數字是19
那麼批處理的內容改為
android update project -p . -t android-19
第二步:修改build_native.sh
該文件在E:\cocos2dx\template\multi-platform-cpp\proj.android目錄中,因為在執行build_native.sh的時候,會出現的許可權的問題,所以在這個腳本中,增加了許可權設置,來解決這個問題。
在第67行後面,也就是復制資源的後面增加下面一行
chmod 666 -R "$APP_ANDROID_ROOT"/assets就可以了。如下圖所示
第三步,創建工程復制批處理
在cocos2dx的2.2中,已經少了一些批處理來創建工程,現在全部改用python來創建工程。
現在到E:\cocos2dx\tools\project-creator下,創建一個批處理demo.bat,內容如下
python create_project.py -project %~n0% -package com.android.zdhsoft -language cpp
x ..\..\cocos2dx\platform\android\java\src\* ..\..\projects\%~n0%\proj.android\src /E
上面批處理的
第一行的意思是,建一個工程為%~n0%的工程,包名是com.android.zdhsoft,使用的語言是cpp。%~n0%在批處理表示的是 批處理的文件名,在demo.bat中,%~n0%的值是demo。使用該批處理的好處是,如果建議某一個工程,只有把批處理復制一個,然後重新命名就可 以了。如命名為sample.bat,執行後,就可以行到一個sample的cocos2dx工程。
創建的工程在E:\cocos2dx\projects目錄中。
第二行的意思,復制編譯andiroid工程需要的java文件。否則編譯的時候,會提示找不到對應的類。
『伍』 opencv 編譯的時候找不到文件 但是文件夾下確實存在啊
很明顯 你在編譯的時候 確實是少了個文件 可能被隱藏了 用管理員的身份打開試試呢
『陸』 如何定位Android NDK開發中遇到的錯誤
Android NDK是什麼,為什麼我們要用NDK?
Android NDK 是在SDK前面又加上了「原生」二字,即Native Development Kit,因此又被Google稱為「NDK」。眾所周知,Android程序運行在Dalvik虛擬機中,NDK允許用戶使用類似C / C++之類的原生代碼語言執行部分程序。NDK包括了:
從C / C++生成原生代碼庫所需要的工具和build files。
將一致的原生庫嵌入可以在Android設備上部署的應用程序包文件(application packages files,即.apk文件)中。
支持所有未來Android平台的一些列原生系統頭文件和庫
為何要用到NDK?概括來說主要分為以下幾種情況:
代碼的保護,由於apk的java層代碼很容易被反編譯,而C/C++庫反匯難度較大。
在NDK中調用第三方C/C++庫,因為大部分的開源庫都是用C/C++代碼編寫的。
便於移植,用C/C++寫的庫可以方便在其他的嵌入式平台上再次使用。
Android JNI是什麼?和NDK是什麼關系?
Java Native Interface(JNI)標準是java平台的一部分,它允許Java代碼和其他語言寫的代碼進行交互。JNI是本地編程介面,它使得在 Java 虛擬機(VM) 內部運行的 Java 代碼能夠與用其它編程語言(如 C、C++和匯編語言)編寫的應用程序和庫進行交互操作。
簡單來說,可以認為NDK就是能夠方便快捷開發.so文件的工具。JNI的過程比較復雜,生成.so需要大量操作,而NDK就是簡化了這個過程。
NDK的異常會不會導致程序Crash,NDK的常見的有哪些類型異常?
NDK編譯生成的.so文件作為程序的一部分,在運行發生異常時同樣會造成程序崩潰。不同於Java代碼異常造成的程序崩潰,在NDK的異常發生時,程序在Android設備上都會立即退出,即通常所說的閃退,而不會彈出「程序xxx無響應,是否立即關閉」之類的提示框。
NDK是使用C/C++來進行開發的,熟悉C/C++的程序員都知道,指針和內存管理是最重要也是最容易出問題的地方,稍有不慎就會遇到諸如內存無效訪問、無效對象、內存泄露、堆棧溢出等常見的問題,最後都是同一個結果:程序崩潰。例如我們常說的空指針錯誤,就是當一個內存指針被置為空(NULL)之後再次對其進行訪問;另外一個經常出現的錯誤是,在程序的某個位置釋放了某個內存空間,而後在程序的其他位置試圖訪問該內存地址,這就會產生一個無效地址錯誤。常見的錯誤類型如下:
初始化錯誤
訪問錯誤
數組索引訪問越界
指針對象訪問越界
訪問空指針對象
訪問無效指針對象
迭代器訪問越界
內存泄露
參數錯誤
堆棧溢出
類型轉換錯誤
數字除0錯誤
NDK錯誤發生時,我們能拿到什麼信息?
利用Android NDK開發本地應用的時候,幾乎所有的程序員都遇到過程序崩潰的問題,但它的崩潰會在logcat中列印一堆看起來類似天書的堆棧信息,讓人舉足無措。單靠添加一行行的列印信息來定位錯誤代碼做在的行數,無疑是一件令人崩潰的事情。在網上搜索「Android NDK崩潰」,可以搜索到很多文章來介紹如何通過Android提供的工具來查找和定位NDK的錯誤,但大都晦澀難懂。下面以一個實際的例子來說明,首先生成一個錯誤,然後演示如何通過兩種不同的方法,來定位錯誤的函數名和代碼行。
首先,看我們在hello-jni程序的代碼中做了什麼(有關如何創建或導入工程,此處略),看下圖:在JNI_OnLoad()的函數中,即so載入時,調用willCrash()函數,而在willCrash()函數中, std::string的這種賦值方法會產生一個空指針錯誤。這樣,在hello-jni程序載入時就會閃退。我們記一下這兩個行數:在61行調用了willCrash()函數;在69行發生了崩潰。
下面來看看發生崩潰(閃退)時系統列印的logcat日誌:
[plain] view plain
*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
Build fingerprint: 'vivo/bbk89_cmcc_jb2/bbk89_cmcc_jb2:4.2.1/JOP40D/1372668680:user/test-keys'
pid: 32607, tid: 32607, name: xample.hellojni >>> com.example.hellojni <<<
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 00000000
r0 00000000 r1 beb123a8 r2 80808080 r3 00000000
r4 5d635f68 r5 5cdc3198 r6 41efcb18 r7 5d62df44
r8 4121b0c0 r9 00000001 sl 00000000 fp beb1238c
ip 5d635f7c sp beb12380 lr 5d62ddec pc 400e7438 cpsr 60000010
backtrace:
#00 pc 00023438 /system/lib/libc.so
#01 pc 00004de8 /data/app-lib/com.example.hellojni-2/libhello-jni.so
#02 pc 000056c8 /data/app-lib/com.example.hellojni-2/libhello-jni.so
#03 pc 00004fb4 /data/app-lib/com.example.hellojni-2/libhello-jni.so
#04 pc 00004f58 /data/app-lib/com.example.hellojni-2/libhello-jni.so
#05 pc 000505b9 /system/lib/libdvm.so
#06 pc 00068005 /system/lib/libdvm.so
#07 pc 000278a0 /system/lib/libdvm.so
#08 pc 0002b7fc /system/lib/libdvm.so
#09 pc 00060fe1 /system/lib/libdvm.so
#10 pc 0006100b /system/lib/libdvm.so
#11 pc 0006c6eb /system/lib/libdvm.so
#12 pc 00067a1f /system/lib/libdvm.so
#13 pc 000278a0 /system/lib/libdvm.so
#14 pc 0002b7fc /system/lib/libdvm.so
#15 pc 00061307 /system/lib/libdvm.so
#16 pc 0006912d /system/lib/libdvm.so
#17 pc 000278a0 /system/lib/libdvm.so
#18 pc 0002b7fc /system/lib/libdvm.so
#19 pc 00060fe1 /system/lib/libdvm.so
#20 pc 00049ff9 /system/lib/libdvm.so
#21 pc 0004d419 /system/lib/libandroid_runtime.so
#22 pc 0004e1bd /system/lib/libandroid_runtime.so
#23 pc 00001d37 /system/bin/app_process
#24 pc 0001bd98 /system/lib/libc.so
#25 pc 00001904 /system/bin/app_process
stack:
beb12340 012153f8
beb12344 00054290
beb12348 00000035
beb1234c beb123c0 [stack]
……
如果你看過logcat列印的NDK錯誤時的日誌就會知道,我省略了後面很多的內容,很多人看到這么多密密麻麻的日誌就已經頭暈腦脹了,即使是很多資深的Android開發者,在面對NDK日誌時也大都默默的選擇了無視。
「符號化」NDK錯誤信息的方法
其實,只要你細心的查看,再配合Google 提供的工具,完全可以快速的准確定位出錯的代碼位置,這個工作我們稱之為「符號化」。需要注意的是,如果要對NDK錯誤進行符號化的工作,需要保留編譯過程中產生的包含符號表的so文件,這些文件一般保存在$PROJECT_PATH/obj/local/目錄下。
第一種方法:ndk-stack
這個命令行工具包含在NDK工具的安裝目錄,和ndk-build和其他一些常用的NDK命令放在一起,比如在我的電腦上,其位置是/android-ndk-r9d/ndk-stack。根據Google官方文檔,NDK從r6版本開始提供ndk-stack命令,如果你用的之前的版本,建議還是盡快升級至最新的版本。使用ndk –stack命令也有兩種方式
使用ndk-stack實時分析日誌
在運行程序的同時,使用adb獲取logcat日誌,並通過管道符輸出給ndk-stack,同時需要指定包含符號表的so文件位置;如果你的程序包含了多種CPU架構,在這里需求根據錯誤發生時的手機CPU類型,選擇不同的CPU架構目錄,如:
[plain] view plain
adb shell logcat | ndk-stack -sym $PROJECT_PATH/obj/local/armeabi
當崩潰發生時,會得到如下的信息:
[plain] view plain
********** Crash mp: **********
Build fingerprint: 'vivo/bbk89_cmcc_jb2/bbk89_cmcc_jb2:4.2.1/JOP40D/1372668680:user/test-keys'
pid: 32607, tid: 32607, name: xample.hellojni >>> com.example.hellojni <<<
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 00000000
Stack frame #00 pc 00023438 /system/lib/libc.so (strlen+72)
Stack frame #01 pc 00004de8 /data/app-lib/com.example.hellojni-2/libhello-jni.so (std::char_traits<char>::length(char const*)+20): Routine std::char_traits<char>::length(char const*) at /android-ndk-r9d/sources/cxx-stl/stlport/stlport/stl/char_traits.h:229
Stack frame #02 pc 000056c8 /data/app-lib/com.example.hellojni-2/libhello-jni.so (std::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&)+44): Routine basic_string at /android-ndk-r9d/sources/cxx-stl/stlport/stlport/stl/_string.c:639
Stack frame #03 pc 00004fb4 /data/app-lib/com.example.hellojni-2/libhello-jni.so (willCrash()+68): Routine willCrash() at /home/testin/hello-jni/jni/hello-jni.cpp:69
Stack frame #04 pc 00004f58 /data/app-lib/com.example.hellojni-2/libhello-jni.so (JNI_OnLoad+20): Routine JNI_OnLoad at /home/testin/hello-jni/jni/hello-jni.cpp:61
Stack frame #05 pc 000505b9 /system/lib/libdvm.so (dvmLoadNativeCode(char const*, Object*, char**)+516)
Stack frame #06 pc 00068005 /system/lib/libdvm.so
Stack frame #07 pc 000278a0 /system/lib/libdvm.so
Stack frame #08 pc 0002b7fc /system/lib/libdvm.so (dvmInterpret(Thread*, Method const*, JValue*)+180)
Stack frame #09 pc 00060fe1 /system/lib/libdvm.so (dvmCallMethodV(Thread*, Method const*, Object*, bool, JValue*, std::__va_list)+272)
……(後面略)
我們重點看一下#03和#04,這兩行都是在我們自己生成的libhello-jni.so中的報錯信息,那麼會發現如下關鍵信息:
[plain] view plain
#03 (willCrash()+68): Routine willCrash() at /home/testin/hello-jni/jni/hello-jni.cpp:69
#04 (JNI_OnLoad+20): Routine JNI_OnLoad at /home/testin/hello-jni/jni/hello-jni.cpp:61
回想一下我們的代碼,在JNI_OnLoad()函數中(第61行),我們調用了willCrash()函數;在willCrash()函數中(第69行),我們製造了一個錯誤。這些信息都被准確無誤的提取了出來!是不是非常簡單?
先獲取日誌,再使用ndk-stack分析
這種方法其實和上面的方法沒有什麼大的區別,僅僅是logcat日誌獲取的方式不同。可以在程序運行的過程中將logcat日誌保存到一個文件,甚至可以在崩潰發生時,快速的將logcat日誌保存起來,然後再進行分析,比上面的方法稍微靈活一點,而且日誌可以留待以後繼續分析。
[plain] view plain
adb shell logcat > 1.log
ndk-stack -sym $PROJECT_PATH/obj/local/armeabi –mp 1.log
第二種方法:使用addr2line和objmp命令
這個方法適用於那些,不滿足於上述ndk-stack的簡單用法,而喜歡刨根問底的程序員們,這兩個方法可以揭示ndk-stack命令的工作原理是什麼,盡管用起來稍微麻煩一點,但是可以滿足一下程序員的好奇心。
先簡單說一下這兩個命令,在絕大部分的linux發行版本中都能找到他們,如果你的操作系統是linux,而你測試手機使用的是Intel x86系列,那麼你使用系統中自帶的命令就可以了。然而,如果僅僅是這樣,那麼絕大多數人要絕望了,因為恰恰大部分開發者使用的是Windows,而手機很有可能是armeabi系列。
別急,在NDK中自帶了適用於各個操作系統和CPU架構的工具鏈,其中就包含了這兩個命令,只不過名字稍有變化,你可以在NDK目錄的toolchains目錄下找到他們。以我的Mac電腦為例,如果我要找的是適用於armeabi架構的工具,那麼他們分別為arm-linux-androideabi-addr2line和arm-linux-androideabi-objmp;位置在下面目錄中,後續介紹中將省略此位置:
[plain] view plain
/Developer/android_sdk/android-ndk-r9d/toolchains/arm-linux-androideabi-4.8/prebuilt/darwin-x86_64/bin/
假設你的電腦是windows, CPU架構為mips,那麼你要的工具可能包含在這個目錄中:
[plain] view plain
D:\ android-ndk-r9d\toolchains\mipsel-linux-android-4.8\prebuilt\windows-x86_64\bin\
好了言歸正傳,如何使用這兩個工具,下面具體介紹:
1. 找到日誌中的關鍵函數指針
其實很簡單,就是找到backtrace信息中,屬於我們自己的so文件報錯的行。
首先要找到backtrace信息,有的手機會明確列印一行backtrace(比如我們這次使用的手機),那麼這一行下面的一系列以「#兩位數字 pc」開頭的行就是backtrace信息了。有時可能有的手機並不會列印一行backtrace,那麼只要找到一段以「#兩位數字 pc 」開頭的行,就可以了。
其次要找到屬於自己的so文件報錯的行,這就比較簡單了。找到這些行之後,記下這些行中的函數地址
2. 使用addr2line查找代碼位置
執行如下的命令,多個指針地址可以在一個命令中帶入,以空格隔開即可
[plain] view plain
arm-linux-androideabi-addr2line –e obj/local/armeabi/libhello-jni.so 00004de8 000056c8 00004fb4 00004f58
結果如下
[plain] view plain
/android-ndk-r9d/sources/cxx-stl/stlport/stlport/stl/char_traits.h:229
/android-ndk-r9d/sources/cxx-stl/stlport/stlport/stl/_string.c:639
/WordSpaces/hello-jni/jni/hello-jni.cpp:69
/WordSpaces hello-jni/jni/hello-jni.cpp:6
從addr2line的結果就能看到,我們拿到了我們自己的錯誤代碼的調用關系和行數,在hello-jni.cpp的69行和61行(另外兩行因為使用的是標准函數,可以忽略掉),結果和ndk-stack是一致的,說明ndk-stack也是通過addr2line來獲取代碼位置的。
3. 使用objmp獲取函數信息
通過addr2line命令,其實我們已經找到了我們代碼中出錯的位置,已經可以幫助程序員定位問題所在了。但是,這個方法只能獲取代碼行數,並沒有顯示函數信息,顯得不那麼「完美」,對於追求極致的程序員來說,這當然是不夠的。下面我們就演示怎麼來定位函數信息。
使用如下命令導出函數表:
[plain] view plain
arm-linux-androideabi-objmp –S obj/local/armeabi/libhello-jni.so > hello.asm
在生成的asm文件中查找剛剛我們定位的兩個關鍵指針00004fb4和00004f58
從這兩張圖可以清楚的看到(要注意的是,在不同的NDK版本和不同的操作系統中,asm文件的格式不是完全相同,但都大同小異,請大家仔細比對),這兩個指針分別屬於willCrash()和JNI_OnLoad()函數,再結合剛才addr2line的結果,那麼這兩個地址分別對應的信息就是:
[plain] view plain
00004fb4: willCrash() /WordSpaces/hello-jni/jni/hello-jni.cpp:69
00004f58: JNI_OnLoad()/WordSpaces/hello-jni/jni/hello-jni.cpp:61
相當完美,和ndk-stack得到的信息完全一致!
使用Testin崩潰分析服務定位NDK錯誤
以上提到的方法,只適合在開發測試期間,如果你的應用或者游戲已經發布上線,而用戶經常反饋說崩潰、閃退,指望用戶幫你收集信息定位問題,幾乎是不可能的。這個時候,我們就需要用其他的手段來捕獲崩潰信息。
目前業界已經有一些公司推出了崩潰信息收集的服務,通過嵌入SDK,在程序發生崩潰時收集堆棧信息,發送到雲服務平台,從而幫助開發者定位錯誤信息。在這方面,處於領先地位的是國內的Testin和國外的crittercism,其中crittercism需要付費,而且沒有專門的中國開發者支持,我們更推薦Testin,其崩潰分析服務是完全免費的。
Testin從1.4版本開始支持NDK的崩潰分析,其最新版本已經升級到1.7。當程序發生NDK錯誤時,其內嵌的SDK會收集程序在用戶手機上發生崩潰時的堆棧信息(主要就是上面我們通過logcat日誌獲取到的函數指針)、設備信息、線程信息等等,SDK將這些信息上報至Testin雲服務平台,只要登陸到Testin平台,就可以看到所有用戶上報的崩潰信息,包括NDK;並且這些崩潰做過歸一化的處理,在不同系統和ROM的版本上列印的信息會略有不同,但是在Testin的網站上這些都做了很好的處理,避免了我們一些重復勞動。
上圖的紅框部分,就是從用戶手機上報的,我們自己的so中報錯的函數指針地址堆棧信息,就和我們開發時從logcat讀到的日誌一樣,是一些晦澀難懂的指針地址,Testin為NDK崩潰提供了符號化的功能,只要將我們編譯過程中產生的包含符號表的so文件上傳(上文我們提到過的obj/local/目錄下的適用於各個CPU架構的so),就可以自動將函數指針地址定位到函數名稱和代碼行數。符號化之後,看起來就和我們前面在本地測試的結果是一樣的了,一目瞭然。
而且使用這個功能還有一個好處:這些包含符號表的so文件,在每次我們自己編譯之後都會改變,很有可能我們剛剛發布一個新版本,這些目錄下的so就已經變了,因為開發者會程序的修改程序;在這樣的情況下,即使我們拿到了崩潰時的堆棧信息,那也無法再進行符號化了。所以我們在編譯打包完成後記得備份我們的so文件。這時我們可以將這些文件上傳到Testin進行符號化的工作,Testin會為我們保存和管理不同版本的so文件,確保信息不會丟失。來看一下符號化之後的顯示:
『柒』 android ndk r9如何運行ndk程序.so文件沒有生成
打開cmd,進入android ndk r9 所在的文件夾,執行命令:ndk-build -C [path] 就可以了,path是你的工程路徑。這樣就會生成.so文件的
『捌』 用cocos Creator發布到android平台報錯,這是什麼原因
原生打包環境的注意事項:
1. 檢查路徑
在偏好設置中設置好的路徑必須確保是正確的,比如:ant 路徑需要設置到 ant 安裝目錄內的 bin 目錄下,NDK 是其根目錄,而 Android SDK 的目錄下應該包含 build-tools、platforms 等文件夾。
2. 檢查 Xcode 和 Visual Studio
打包 Mac 版本和 iOS 版本需要 Xcode 支持。打包 Windows 版本需要安裝 Visual Studio,同時在安裝 Visual Studio 時,默認並沒有勾選 C++ 編譯組件。如果沒有安裝,則需要重新安裝並選擇 C++ 相關編譯組件。
3. 檢查 NDK 版本
不論使用什麼版本的引擎,都需要使用 NDK r9b 以上版本。如果使用了 Cocos2d-x framework, 請使用 NDK r10 以上的版本,推薦 r10e。
4. Windows 平台需要檢查 JAVA_HOME 環境變數
如果使用 Windows 平台,請確認你的環境變數中包含 JAVA_HOME,可以通過右鍵點擊我的電腦,選擇屬性,打開高級選項卡中來查看和修改環境變數。Windows 平台可能需要重啟電腦才會生效。
參考[如何設置或更改 JAVA 系統環境變數?](https://www.java.com/zh_CN/download/help/path.xml)
5. 檢查 JAVA 環境
在 Mac 終端或者 Windows 命令行工具中輸入下面代碼來查看:
```
java -version
```
如果顯示為 JAVA SE 則沒有問題,如果系統中使用的是 JRE,則需要安裝 [JAVA SE 運行環境]
6. 包名問題
檢查構建發布面板中的包名,包含空格,`-`等都是非法的包名。
『玖』 如何定位Android NDK開發中遇到的錯誤
只要你細心的查看,再配合Google 提供的工具,完全可以快速地准確定位出錯的代碼位置,這個工作我們稱之為「符號化」。需要注意的是,如果要對NDK錯誤進行符號化的工作,需要保留編譯過程中產生的包含符號表的so文件,這些文件一般保存在$PROJECT_PATH/obj/local/目錄下。
第一種方法:ndk-stack
這個命令行工具包含在NDK工具的安裝目錄,和ndk-build及其他常用的一些NDK命令放在一起,比如在我的電腦上,其位置是/android-ndk-r9d/ndk-stack。根據Google官方文檔,NDK從r6版本開始提供ndk-stack命令,如果你用的之前的版本,建議還是盡快升級至最新的版本。使用ndk –stack命令也有兩種方式
實時分析日誌
在運行程序的同時,使用adb獲取logcat日誌,並通過管道符輸出給ndk-stack,同時需要指定包含符號表的so文件位置;如果你的程序包含了多種CPU架構,在這里需求根據錯誤發生時的手機CPU類型,選擇不同的CPU架構目錄,如:
當崩潰發生時,會得到如下的信息: