2013年12月25日 星期三

撰寫Android NDK程式

一開始知道Android NDK時只覺得:好棒,可以用C/C++寫Android程式。
可是安裝與設定方式一直都沒看懂過。
只大概知道概略流程:
windows要裝cygwin的環境,然後用命令列或批次檔去編譯出.so檔
最後再與Android的專案做連結成為可用的程式。
後來工作繁重就也沒再深入了解,
最近買新電腦,自然又燃起重新切入的欲望。

現在新的NDK與套件已經變得既不用安裝cygwin,也不用進行太多的命令列執行
然而,也是要有對於JNI(Java Native Interface)的基本了解
進行上才會順利一些,不然就會像我一樣
一直搞不懂為什麼編譯明明顯示成功,在執行時還是會掛點
直到我開了logcat上網查才發現有地方不對
不過進度卡最兇的反而不是JNI的部分
以下是一點小小筆記

Android NDK的基本運作就是用JNI來處理,
簡單地說,是一個連結C語言與Java的工具。
在進行連結時需要特別的語法來進行,
例如說,string要轉jstring,int要轉jint之類的。
此外還有很多的規則,如JNI函數的命名要以
Java_(package名稱)_(class名稱)_(java裡的函數名稱)這樣的型式命名
同時裡面有(.)的都要換成底線(_)
以一般來說,com.example.hellojni是package名稱
MainActivity是class名稱,stringFromJNI是用在Java裡的函數名稱
最後寫在C/C++就會變成
Java_com_example_hellojni_MainActivity_stringFromJNI這麼長的東西
當然這樣的命名連結方式以手打很容易出錯,
所以JDK本身有工具可以協助處理
但我還沒成功過,這次就不寫了 XD

而我遇到的問題主要有三個,其中兩個互相影響到
造成卡了快一週才解決,
不然正常上網找答案應該是兩、三天就完成的
首先是C++的函數格式問題
當我照著網路上範例所寫,開了Native support後
出現的檔案自然就是cpp檔
我也很天真地就把NDK裡的hello-jni.c的內容直接拿到cpp裡用
第一個就遇到Method 'NewStringUTF' could not be resolved
照著網路上的說明更改如下
 //將最有問題的c語言這行
return (*env)->>NewStringUTF(env, "Hello from JNI!");
//換成c++格式
return (env)->>NewStringUTF( "Hello from JNI!");
執行編譯也沒問題,但是就沒辦法執行
跳出這樣的畫面

最後爬文到這一篇,說要加上extern "C"參數才行
那我加上去…結果也是失敗
究其原因,跟最後一個問題有關,但當時並沒有查覺
只有再去網路爬了一些文章,
仔細看都是用純C語言寫,而NDK裡的Sample也多是C語言
就改用C看看

結果改用C也沒比較順利,一直卡在程式語意問題
也就是這一行:Method 'NewStringUTF' could not be resolved
照之前查到的都是改用C++格式處理
可是改成C++格式編譯是不會過的
最後爬兩篇文章,都可以解決這個問題
一個是把語意分析的部分關掉,參照這篇的第二個回答
一個是把產生的錯誤刪除,或者說是略過
請參照這一篇

然後,終於第一個JNI範例成功啦!!!
可是這個成功是在公司電腦上跑的
回家之後就失敗了,失敗的瞬間我才起來一件事
模擬器的Image檔是不同的

因為我覺得模擬器速度實在太慢了
就算現在這台是i7等級的,還是嫌它慢
所以上網找到了新的解決辦法是用Intel x86 ATOM的Image檔
這下好了,預設的編譯型式是ARM的指令,不是x86的
就這樣這一個禮拜主要就卡死在這裡
因為就算最後我寫對了,也因為編譯型式的不同,最終執行是失敗的
而公司電腦因為等級不高,我是用舊的Android 2.x與3.x去跑模擬
反正這樣成功了

要解決x86的模擬器不能跑JNI,主要就是在編譯的參數中加入
APP_ABI :=armeabi armeabi-v7a x86
讓產生的執行檔符合每一種CPU指令
至於加在哪裡最快,
那就是新增一個Application.mk的檔案在JNI的目錄裡

並且把上面紅字那一行放進去,萬事OK!

真的是搞死人的NDK!
下一步就是Ogre的Android編譯了吧。

沒有留言: