NativeActivityクラスを用いたアプリケーションを実行する。
Android SDK 2.3の公開に伴い、Android NDK Rev 5も公開され、NativeActivityクラスが利用できるようになりました。NativeActivityクラスを利用すれば、Nativeな言語(CもしくはC++)のみで、Androidアプリケーションが作成できます。
Nativeな言語で記述することで処理が高速になり、そのことで、OpenGL などを用いたアプリケーションや、グラフィカルなゲームアプリケーションの分野で特に有利になってくる可能性があります。
これまでもJNIを用いて一部の処理をNativeな言語で記述し、javaで呼び出すことで高速化を計ることは可能でしたが、NativeActivityでは、Nativeな言語のみでアプリケーションを作成することができるため、さらなる高速化が期待できます。
今回は、C言語で記述されたアプリケーションをビルドし、エミュレータで実行してみます。
また、プロジェクトの中身についても触れてみたいと思います。
続きはこちらから…
サンプルアプリケーションのビルドと実行
NativeActivityをビルドするにはまず、Android NDKをセットアップしておく必要があります。NDKのインストール方法は以下のリンクを参考にしてください。
それではまず、サンプルコードから新規プロジェクトを作成します。プロジェクトの作成方法は、新規作成→Androidプロジェクトで、Create project from existing sourceを選択し、サンプルコードを取り込みます(今回使用するサンプルコードの場所は、<NDKルート>/samples/native-activity です)。
プロジェクトを作成したら、先ほどの鈴k寿を参考に早速ソースコードをビルドしてみます。コマンドプロンプトを起動し、先ほどの<NDKルート>/samples/native-activity に移動したら、コマンドプロンプトにて、ndk-buildのコマンドを実行します。
ビルドに成功したら、通常通りeclipseからプロジェクトをデバッグ実行します。すると、ミュレータが立ち上がり、画面の色が様々に変化するサンプルアプリケーションが実行されます(上図)。
プロジェクトの中身
それではプロジェクトの中身を見てみましょう。まず、srcディレクトリの下に、javaファイルが存在しないことに気が付くと思います。
次に、jniディレクトリの中に、main.c、Android.mk、Application.mkの3つのファイルが存在していることがわかります。
main.cファイルに、今回使用するサンプルアプリケーションのメインの処理が記述されています。つまり、このサンプルコードは、Java言語ではなく、C言語で作成されているがわかります。
main.cはOpenGL ESを利用して記述されているため、それについてはここでは詳しくは解説しませんが、注目したいのはandroid_main()関数です。
以下はその一部です。
void android_main(struct android_app* state) {
struct engine engine;
// Make sure glue isn't stripped.
app_dummy();
memset(&engine, 0, sizeof(engine));
state->userData = &engine;
state->onAppCmd = engine_handle_cmd;
state->onInputEvent = engine_handle_input;
engine.app = state;
//....省略
}
android_main()関数は、アプリケーションが起動して一番はじめに呼び出される関数です。いわゆるC言語でいうところのmain()関数ですね。
この関数のプロトタイプ宣言は、以下のファイルに次のようにされています。
<NDKルート>/sources/android/native_app_glue.h
/** * This is the function that application code must implement, representing * the main entry to the app. */ extern void android_main(struct android_app* app);
さらに追って見ましょう。android_main()関数は次のファイルから呼び出されます。
<NDKルート>/sources/android/native_app_glue.c
一部を抜粋してみます。まずANativeActivity_onCreate()関数が起動します。
void ANativeActivity_onCreate(ANativeActivity* activity,
void* savedState, size_t savedStateSize) {
LOGI("Creating: %p\n", activity);
activity->callbacks->onDestroy = onDestroy;
activity->callbacks->onStart = onStart;
activity->callbacks->onResume = onResume;
activity->callbacks->onSaveInstanceState = onSaveInstanceState;
activity->callbacks->onPause = onPause;
activity->callbacks->onStop = onStop;
activity->callbacks->onConfigurationChanged = onConfigurationChanged;
activity->callbacks->onLowMemory = onLowMemory;
activity->callbacks->onWindowFocusChanged = onWindowFocusChanged;
activity->callbacks->onNativeWindowCreated = onNativeWindowCreated;
activity->callbacks->onNativeWindowDestroyed = onNativeWindowDestroyed;
activity->callbacks->onInputQueueCreated = onInputQueueCreated;
activity->callbacks->onInputQueueDestroyed = onInputQueueDestroyed;
activity->instance = android_app_create(activity, savedState, savedStateSize);
}
ここで、各種コールバック関数を登録しています。その後、android_app_create()関数が呼ばれています。
次がその一部です。
static struct android_app* android_app_create(ANativeActivity* activity,
void* savedState, size_t savedStateSize) {
struct android_app* android_app = (struct android_app*)malloc(sizeof(struct android_app));
memset(android_app, 0, sizeof(struct android_app));
android_app->activity = activity;
//....省略
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
pthread_create(&android_app->thread, &attr, android_app_entry, android_app);
//....省略
return android_app;
}
上記ソースコード中のpthread_create()関数の中で、android_app_entry()関数がさらに呼ばれています。
以下はその一部です。
static void* android_app_entry(void* param) {
struct android_app* android_app = (struct android_app*)param;
//...省略
android_main(android_app);
android_app_destroy(android_app);
return NULL;
}
上記ソースコードにて、android_main()関数が呼び出され、main.cへと処理が移ります。
Android.mkファイルについて
Android.mkファイルについても触れておきます。
今回使用したサンプルコードのAndroid.mkファイルの内容は以下のようになっています。
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := native-activity LOCAL_SRC_FILES := main.c LOCAL_LDLIBS := -llog -landroid -lEGL -lGLESv1_CM LOCAL_STATIC_LIBRARIES := android_native_app_glue include $(BUILD_SHARED_LIBRARY) $(call import-module,android/native_app_glue)
簡単に説明すると次のようになります。
Android.mkファイルは、ソースコードのビルドに必要で、本質的にはGNU makeファイルの断片です。
LOCAL_PATH := $(call my-dir)にて、カレントディレクトリのパスを変数LOCAL_PATHに代入しています。
LOCAL_MODULE変数は、モジュールを識別するために定義します。名前はユニークである必要があり、スペースは使用してはいけません。
上記ソースコードの場合、libnative-activity.soモジュールファイルbが <NDK>/samples/native-activity/libs/armeabi ディレクトリ以下にできあがります。
LOCAL_SRC_FILESは、CもしくはC++で記述されたソースファイルを定義します。ただし、ヘッダファイルやインクルードファイルは記述してはいけません。
LOCAL_LDLIBSは、追加したいリンカフラグを定義します。”-l”を忘れずに付加しましょう。
LOCAL_STATIC_LIBRARIESには、モジュールとリンクさせる静的ライブラリを宣言します。
その他にも、場合によって様々な宣言が必要です。Android.mkファイルについてはドキュメントが<NDKルート>/docsに梱包(ANDROID-MK.html)されていますので、詳しくはそちらを参照下さい(本記事もそちらを参考にしています)。


