Native Activityの実装方法(Native Activities and Applications和訳)

Android NDK r5の docs/NATIVE-ACTIVITY.HTMLを訳して、まとめてみました。Native Activityの実装方法は2通りあり、その方法はやや難解ですが、ヘルパーライブラリを利用すれば幾分楽に実装ができそうです。

I.概要

Android SDKが提供するヘルパークラスーNativeActivityにより、完全なネイティブアプリケーションを記述することが可能となりました。NativeActivityはAndroidフレームワークとネイティブコードの間のコミュニケーションを処理するので、それらのサブクラスやメソッドをコールする必要はありません。必要なことはAndroidManifest.xmlファイルにネイティブなアプリケーションを宣言し、そのネイティブアプリケーションを作成することです。

ネイティブアクティビティの概念が導入されても、下記の事実に何ら変更はありません。

このため、JNIを通じてAndroidフレームワークAPIにアクセスすることは、これまでと同様に行えます。一方で、センサ・入力イベント、そしてアセットのようなものにアクセスするネイティブなインタフェースも利用可能です。何がサポートされているかに関する詳しい情報は、/docs/STABLE-APIS.HTMLを参照して下さい。

ネイティブアクティビティを開発するなら今まで通り、Eclipseを用いて、あるいは、"android create project"コマンドによってプロジェクトを生成するべきでしょう。通常のAndroidビルドツールでネイティブアプリケーションのビルドやパッケージを行うと、ビルドシステムは正確な構造を持つAndroidプロジェクトだけをビルドすることができます。androidツールやEclipseを利用することで、それらを確実に行うことができます。

Android NDKはネイティブアクティビティを実装する方法として、2種類の方法を用意しています:

  • native_activity.hを直接利用する方法
  • ヘルパーライブラリを利用する方法
native_activity.hを直接利用する方法

native_activity.h ヘッダはNativeActivityクラスのネイティブバージョンを定義しており、ネイティブアクティビティを生成するのに必要なコールバックインタフェースとデータ構造を有しています。アプリケーションのメインスレッドがコールバックを制御するため、コールバックの実装はブロックされないようにする必要があります。もしこれらがブロックされた場合は、コールバックから戻るまでメインスレッドの応答がなくなるので、ANR(Application Not Responding)エラーを受け取ることになるでしょう。詳細は、/platforms/android-9/arch-arm/usr/include/android/native_activity.h のコメントを参照下さい。

ヘルパーライブラリを利用する方法

android_native_app_glue.h ファイルは、native_activity.h インタフェース上で構築された静的なヘルパーライブラリを定義しています。コールバックや入力イベントのようなものを処理するために別スレッドを生成します。そのため、種々のコールバックによってメインスレッドがブロックされることを防ぐと共に、コールバックの実装に若干の柔軟性を加えることができるため、このプログラミングモデルは実装が若干簡単に感じられるかもしれません。
/sources/android/native_app_glue/android_native_app_glue.c のソースも公開されているため、必要であれば実装を修正することも可能です。詳細は、/sources/android/native_app_glue/android_native_app_glue.h のコメントを参照下さい。

II.native-activity.h インタフェースを使う

完全なネイティブアクティビティを実装するために、native-activity.h インタフェースを利用することができます。このインタフェースを利用すると、メインのUIスレッドがブロックされないようにコールバックを実装することを確実に行わなければなりません。詳細は、/platforms/android-9/arch-arm/usr/include/android/native_activity.hを参照下さい。

メインUIスレッドとは別のスレッドでイベントループ中のコールバックを制御できる native_app_glue 静的ヘルパーライブラリを利用する方が容易かもしれません。この静的ライブラリの使用方法は、native-activity サンプルアプリケーションを参照下さい。

native-activity.h インタフェースを用いてネイティブアクティビティを実装する方法:

  • 1/ "android create project"コマンドかEclipseからプロジェクトを作成します。プロジェクトのルートディレクトリ中に、jni/ディレクトリを作成します。このディレクトリに全てのネイティブコードを格納します。
  • 2/ AndroidManifest.xml ファイルにネイティブアクティビティを宣言します。記述例は下記の通り:
      <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.example.native_activity"
        android:versionCode="1"
        android:versionName="1.0">  
        
        <uses-sdk android:minSdkVersion="8" />
        
        <application android:label="@string/app_name" android:hasCode="false">
        
          <activity android:name="android.app.NativeActivity"
            android:label="@string/app_name"
            android:configChanges="orientation|keyboardHidden">
        
          <meta-data android:name="android.app.lib_name"
            android:value="native-activity" />
            <intent-filter>
              <action android:name="android.intent.action.MAIN" />
              <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
          </activity>
        </application>
      </manifest>
    • 注意すべき主な点:
      • activityタグの android:name 属性は必ず android.app.NativeActivityに設定します。NativeActivityのサブクラスにすることも可能ですが、そのようにするのであれば、代わりにそのサブクラスの名前を指定します。
      • meta-detaタグの android:name 属性は必ず android.app.lib_name の形式にします。lib_name はlibプリフィックスと.soサフィックスを除いたモジュール名です。
  • 3/ ネイティブアクティビティのためのファイルを生成し、ANativeActivity_onCreate() 関数を実装します。この関数はネイティブアクティビティが開始された際に呼び出されます。この関数はANativeActivity構造体のポインタを受けとり、種々のコールバック実装の関数のポインタを含むように記述する必要があります。ANativeActivity->callbacks に、適切にコールバック関数のポインタをセットします。
  • 4/ ANativeActivity->instance フィールドに、利用したい特定データのインスタンスのアドレスを設定します。
  • 5/ 開始時に実行したいその他の処理を実装します。
  • 6/ ANativeActivity->callbacks に設定したコールバックの残りを実装します。呼ばれるコールバックに関する情報は、SDKドキュメントのアクティビティライフサイクルを参照して下さい。コールバックの実装はブロックされてはならないことを覚えておきましょう。さもなくば、コールバックが戻るまでメインのUIスレッドが待たされることにより、ANRエラーが発生することになるでしょう。
  • 7/ アプリケーションの残りの部分を開発します。
  • 8/ ビルドシステムにネイティブモジュールを宣言するため、プロジェクトのjni/ディレクトリ中にAndroid.mk ファイルを生成します。Android.mkファイルは、本質的にはGNU makeファイルの断片です。例:
       LOCAL_PATH := $(call my-dir) 
       include $(CLEAR_VARS)
       LOCAL_MODULE    := my_native_module
       LOCAL_SRC_FILES := my_native_code.c
       include $(BUILD_SHARED_LIBRARY)
    • Android.mk ファイルの生成と変数の意味に関する情報は、/docs/ANDROID-MK.TXT ファイルを参照して下さい。
  • 9/ Android.mk ファイルを一度生成したら、"ndk-build"コマンドを使用してネイティブコードをコンパイルします。
       cd path/to/project
       <ndk_root>/ndk-build
  • 10/ AntかEclipseを使用して、通常どおりAndroidのプロジェクトをビルドし、インストールします。jni/ディレクトリが存在するなら、ビルドでは自動的にネイティブコードが.apkファイルの中にパッケージされます。

文責:技術部 瀬戸 直喜(前・日本Androidの会四国支部長/情報セキュリティスペシャリスト)