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

One Comment