[Android] helloworld: NDK

posted in: android, Java | 0

 

開發環境下載:

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”);

請參閱:JNI programming in C++.

由於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,因此在使用前要多加考慮。

參考:http://www.open-open.com/bbs/view/1319209398468

Leave a Reply

Your email address will not be published. Required fields are marked *