JNIを使用したアプリケーションの作成( eclipse juno + ADT-0.21.x + NDK r8d )

2015年4月19日

eclipseは拡張機能の1つ1つをPespectiveとして提供していて、メニューからWindow >  Open Pespective > …と辿っていき必要な機能を開くことで作業を進めていきます。
20130116_DS5-CE_00_Open Perspective00

Other…
20130116_DS5-CE_00_Open Perspective01
ここではC/C++のPerspectiveのスクリーンショットを提示しますが、必要に応じてPerspectiveを開き作業を進めていきます。
各Perspectiveを選択している状態では、メニューが専用にカスタマイズされるのでプロジェクトに関する各操作が捗ります。
20130116_CC++PerspectiveMenu

 

 

今回は、NDKのサンプルにあったJNI(Java Native Interface)を使用したアプリケーションと同等の物を、CではなくC++で作成する工程を解説します。

まずはベースとなる通常のアプリケーションを作成します。
初回の記事で作ったHello worldと同様のアプリケーションでいいでしょう。
JavaのPerspectiveを開き、Android Application Projectを作成します。設定はターゲットはAPI 17(4.2 Jelly Bean)ミニマムはAPI 8(2.2 Froyo)、詳細設定は全てデフォルト通りとします。
20130116_JNI_Project00

作成したプロジェクトを右クリックして開くメニューからAdd Android Native Support…を選択します。
20130116_select_add_native_support

続 いてライブラリ名の指定。これはデフォルトでプロジェクト名となっていますが、今はこのままで構いません。プロジェクトの構成に従い名前を決定しましょ う。Finishをクリックするとjniフォルダが作られ、ライブラリ名.cppとAndroid.mkが作成されます。
20130116_add_native_support
再 び、プロジェクトから右クリックでメニューを出すと、C/C++のBuild関連の項目が追加されていることが確認できます。ADTの旧バージョンで行う 必要のあったBuild関連の雑多な設定も全て整っているので興味のある人はPropertiesで確認してみましょう。
20130116_add_native_support_add_menu

ヘッダのインクルードしか書かれていないAndroidAppJNITest.cppを以下のように書き換えました。

#include <jni.h>
#include <string.h>

#include "AndroidAppJNITest.h"

jstring JNICALL Java_com_planetleaf_androidappjnitest_MainActivity_TestFunction(JNIEnv *env, jobject)
{
	return env->NewStringUTF("Native Test String");
}

Cに比べてずいぶんとすっきりした記述が出来ます。

AndroidAppJNITest.hをプロジェクトのjniフォルダへ追加します。

#ifndef ANDROIDAPPJNITEST_H_
#define ANDROIDAPPJNITEST_H_


#ifdef __cplusplus
extern "C" {
#endif

JNIEXPORT jstring JNICALL Java_com_planetleaf_androidappjnitest_MainActivity_TestFunction(JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif


#endif /* ANDROIDAPPJNITEST_H_ */

・・・と、eclipseのC/C++Perspectiveのファイル追加で自動的にヘッダファイルのテンプレートを出してくれたわけですが、ここで気になってNDKのGCCのバージョンを確認してみました。
20130117_ndk_gcc_version
2013年1月17日現在配布されているNDK(r8c)ではGCC 4.4.3となっています。
つまり#pragma onceが使用できます。書き換えることにしましょう。
移植性の問題を提示している方もいますが、それを捨ててもなお、利便性や可読性等の恩恵のほうが大きいと考えます。
AndroidAppJNITest.h

#pragma once

#ifdef __cplusplus
extern "C" {
#endif

JNIEXPORT jstring JNICALL Java_com_planetleaf_androidappjnitest_MainActivity_TestFunction(JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif

ここでの詳細な解説は控えますが、定義するうえで重要な点は、エクスポートする関数名です。これはスクリーンショットで見たほうが分かりやすいでしょう。
20130117_export_name
一目瞭然ですが、アクティビティのクラス名=エクスポート名(ドットはアンダースコアに置換)です。JNIEXPORTで定義した関数は、モジュールを読み込むことで呼び出すことが出来ます。
Android.mkは以下の通り、ほぼ全自動なので依存関係の記述等は要りません。詳しくはNDKのドキュメントを参照の事。

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := AndroidAppJNITest
LOCAL_SRC_FILES := AndroidAppJNITest.cpp

include $(BUILD_SHARED_LIBRARY)

呼び出し側のMainActivity.Javaにコードを挿入します。デフォルトで作成されたレイアウトは破棄して、エクスポート関数が返す文字列のみを画面に出力します。

package com.planetleaf.androidappjnitest;

import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
import android.widget.TextView;

public class MainActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
        TextView  tv = new TextView(this);
        tv.setText( TestFunction() );
        setContentView(tv);		
	}

	public native String  TestFunction();
	static {
		System.loadLibrary("AndroidAppJNITest");
	}	
	
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.activity_main, menu);
		return true;
	}

}

動作確認のために、導入時に使用したNexus One相当のAVD(Android Virtual Device)を立ち上げます。ARMサイトの導入マニュアル通り、設定(Settings) > セキュリティ(Security)から提供元不明のアプリ(Unknown sources)、{}開発者向けオプション({} Developer options)からUSBデバッグ機能(USB debugging)にチェックが入っていることを確認します。設定は保存されますが、実機デバイスの時も同様にチェックしましょう。

ツールボタンのRUNボタンでネイティブコードのコンパイルも含めてデバイス上で実行してくれます。
20130117_ndk_build

20130117_ndk_run

ツー ルボタンのDebugボタンで、javaのコードレベルでブレイクポイントを設置してのデバッグも、この段階で行えます。初回は以下のウインドウが開きま すが、次回からはデバッグコンフィグレーションが作成され、即時にデバッグが開始されます。NativeやDS-5のデバッグを行う場合はボタン右のド ロップダウンから新しいコンフィグレーションを作成して選択します。
20130123_debug_as_default_debugger

ブレイクポイントはCtrl+Shift+B、又はスクリーンショットの赤丸の位置で右クリックをして出せるポップアップメニューからも設定できます。アプリケーションのスレッドの再開、停止等は、左上のツールバーの各ボタンから行えます。
20130123_default_debugger