開發環境下載:
JDK
cywin
eclipse: plugin ADT(for android) + CDT(for C/C++)
android sdk
android ndk
編輯/home/your_account/.bash_profile,並加入以下環境變數宣告(請依sdk的存放位置改變路徑)
ANDROID_TOOLS=/android/android-sdk/tools/
ANDROID_PLATFORM_TOOLS=/android/android-sdk/platform-tools/
NDK_ROOT=/android/android-ndk-r8b/
NDK_SAMPLE=/android/android-ndk-r8b/samples/
export PATH=$PATH:$ANDROID_TOOLS:$ANDROID_PLATFORM_TOOLS:$NDK_ROOT:$NDK_SAMPLE
打開Cygwin Bash Shell,在windows下,因為換行的格式不同(\r\n),因此在存檔後需將格式換回unix,請執行以下指令:dos2unix .bash_profile
這樣子在每次打開Cygwin Bash Shell的時候,因為系統就會自動去執行.bash_profile這個檔案,所以便能夠自動將正確的路徑導入到環境變數之中。
試著執行: adb version,如果可以執行便表示環境變數順利載入,shell可以找到ANDROID_PLATFORM_TOOLS底下的工具。
android hello world:
新建一個android application project,然後run as Android application.
那麼,我們到底該怎麼使用NDK來呼叫C/C++呢?
首先,在專案目錄下建立一個資料夾:jni,接著在這個資料夾中加入三個檔案:
Android.mk:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_LDLIBS := -llog
LOCAL_MODULE := myNative
LOCAL_SRC_FILES := native.cpp
include $(BUILD_SHARED_LIBRARY)
Application.mk:
APP_STL := stlport_static
native.cpp (這個可以隨自己高興命名,但必須命名一致其中functin的命名規則必須遵照一定的格式):
#include <jni.h>
#include <string>
#include <android/log.h>
#include <stdlib.h>
#include <vector>
#include <iostream>
#define DEBUG_TAG "NDK_AndroidNDKMainActivity"
#ifdef __cplusplus
extern "C" {
#endif
using namespace std;
void Java_com_horizon_AndroidHelloWorld_MainActivity_helloLog(JNIEnv * env, jobject obj, jstring logThis){
jboolean isCopy;
std::vector<std::string> vec;
const char * szLogThis = env->GetStringUTFChars(logThis, &isCopy);
__android_log_print(ANDROID_LOG_DEBUG, DEBUG_TAG, "NDK:LC: [%s]", szLogThis);
env->ReleaseStringUTFChars(logThis, szLogThis);
}
#ifdef __cplusplus
}
#endif
Java端如下:
package com.horizon.AndroidHelloWorld;
import com.horizon.AndroidHelloWorld.R;
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
public class MainActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
private native void helloLog(String logThis);
public void onClickButton(View v){
helloLog("This will log to LogCat via the native call.");
}
static {
System.loadLibrary("myNative");
}
}
之後打開cygwin bash shell,到project的目錄底下,執行以下指令:
ndk-build
如果執行成功,則會產生檔案至project/obj/local/armeabi/libmyNative.so
名稱則是Android.mk中LOCAL_MODULE中加上前綴lib及後綴.so,由於這個特性,因此在命名LOCAL_MODULE的時候,千萬不能以lib作為開頭,因為NDK會沒辦法產生正確的.so檔。
之後,就可以執行android程式來進行測試。
另外,請注意任何在android.mk中的space都會導致路徑無法找到
例如:
“LOCAL_PATH := $(call my-dir)”與”LOCAL_PATH := $(call my-dir) “是不同的,後者會導致 no rule to make target的錯誤。
如果產生
expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘.’ token 之類的錯誤
則表示我們用到gcc去編譯C++的檔案了,解決方法有二種:
1.改用g++來編譯:.c的檔案會使用gcc而.cpp會使用g++。
2.加入extern “C”在C的CODE中來兼容C的語法
只是,在jni中,其提供了二個不同的界面給C及C++使用,例如同樣一個function:
在C中:(*env)->NewStringUTF(env, “Hello from JNI”);
在C++中:env->NewStringUTF(“Hello from JNI”);
由於C是C++的子集合,而C++在語法規則上又比C更加的嚴格,因此在開發上最好統一用C++來進行開發。(意即所有的檔名都是以.h或.cpp結尾)
在初始的設定上,其實NDK並不支持stl,為了效能的問題,在預設計只會使用最少的C++函式庫。如果要使用stl的話,我們就必須在Application.mk中加入以下的宣告:
APP_STL := stlport_static
其實還有其他的stlport,各有不同特性的支援:
stlport_static :目前推薦的方法,但在現時點並不支援exception及RTTI。
stlport_shared:作為動態函式庫,可能會有兼容性的問題,目前不推薦使用。
gnustl_static:其使用GNU libstdc++作為靜態庫,可支援exception及RTTI,但由於這個library的license是基於GPLv3,因此在使用前要多加考慮。
Leave a Reply