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 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)されていますので、詳しくはそちらを参照下さい(本記事もそちらを参考にしています)。