ラベル JNI の投稿を表示しています。 すべての投稿を表示
ラベル JNI の投稿を表示しています。 すべての投稿を表示

2009年7月8日水曜日



f:id:bs-android:20090707164602p:image


先日発表されたAndroidのNDK(Native Development Kit)のお陰でapkファイルにライブラリを同梱するためにソースコードをすべてダウンロードする必要がなくなった上に、Windowsだけでも作業できるようになり、開発がかなり楽になりました。


今回はNDKに付属しているhello-jniという単純なサンプルプロジェクトをWindows上でビルド、実行するまでの手順をまとめてみました。











準備


cygwinのダウンロード、インストール



  • ここからhttp://www.cygwin.com/cygwin-icon.gifのアイコンをクリックしてsetup.exeをダウンロードします。

  • setup.exeを実行すると以下のような画面になるので、「次へ(N)」を押して先に進みます。


f:id:bs-android:20090707131141g:image













  • さらに「次へ(N)」を押して先に進みます。


f:id:bs-android:20090707131332g:image













  • cygwinのrootディレクトリを変更する必要がなければ、何も変更せずに「次へ(N)」を押して先に進みます。


f:id:bs-android:20090707131437g:image













  • cygwinパッケージのディレクトリを変更する必要がなければ、何も変更せずに「次へ(N)」を押して先に進みます。


f:id:bs-android:20090707131556g:image













  • インターネット接続にProxyを設定していなければ「Direct Connection」、IEと同じ設定を使用する場合は「Use IE5 Settings」、Proxyを手動で設定する場合は「Use HTTP/FTP Proxy」を設定し、「次へ(N)」を押して先に進みます。


f:id:bs-android:20090707131634g:image













  • ダウンロード先を選んで「次へ(N)」を押して先に進みます。日本のドメインを選んだ方がスピードは速いです。また、ftpだと繋がらないことが多いので、httpのものを選んだほうが良いようです。


f:id:bs-android:20090707131848g:image













  • 以下のような画面がでるので、しばらく待ちます。


f:id:bs-android:20090707132444g:image













  • インストールするパッケージを選択する画面になります。


f:id:bs-android:20090707132531g:image













  • スクロールさせて、「Devel」の「+」ボタンを押してDevelのカテゴリのリストを展開します。


f:id:bs-android:20090707133945g:image













  • さらにスクロールし、gccのパッケージを選択(※赤丸のしるしの部分をクリック)します。自動的にgcc-coreなどにもチェックが入りますが、そのままにしておきます。


f:id:bs-android:20090707134133g:image













  • さらに下にスクロールし、makeのパッケージを選択(※赤丸のしるしの部分をクリック)します。同じく自動的に他のパッケージにもチェックが入りますが、そのままにしておきます。そして「次へ(N)」を押して先に進みます。


f:id:bs-android:20090707135430g:image













  • すると以下のような画面になるので、完了するまで待ちます。


f:id:bs-android:20090707135634g:image













  • この画面が出れば完了です。「完了」を押して終了します。


f:id:bs-android:20090707135721g:image











Windows環境変数の設定

デスクトップ上にできるhttp://www.cygwin.com/cygwin-icon.gifのアイコンから起動させると、WindowsのユーザーアカウントのディレクトリがHOMEディレクトリになり、Windows XPの場合はパスにスペースが含まれるため、後の作業がうまくいきません。


その為、環境変数「HOME」を設定して、あらかじめ任意のHOMEディレクトリを指定しておきます。





環境変数の設定方法




  • マイコンピュータのプロパティを開く


f:id:bs-android:20090707140338g:image













  • システムのプロパティ画面が出るので、「詳細設定」タブを選択します。


f:id:bs-android:20090707141333g:image













  • 詳細設定の画面で「環境変数」ボタンを押します。


f:id:bs-android:20090707141430g:image













  • 環境変数のユーザー環境変数の「新規」ボタンを押します。


f:id:bs-android:20090707141456g:image













  • 変数名に「HOME」、変数値に「/home/ユーザー名」(※ユーザー名部分は任意)を入力し、OKで抜けます。


f:id:bs-android:20090707141558g:image





これでWindows側の環境変数の設定は完了です。


デスクトップ上にできているhttp://www.cygwin.com/cygwin-icon.gifのアイコンから起動させると、



c:\cygwin\home\ユーザー名


ディレクトリが自動的に生成され、ここがHOMEディレクトリになります。











NDK1.5のダウンロード、インストール



  • ここからNDK1.5のandroid-ndk-1.5_r1-windows.zipをダウンロードします。





ダウンロードが終わったら、android-ndk-1.5_r1-windows.zipを先程自動生成された



c:\cygwin\home\ユーザー名


のディレクトリに展開します。





展開後に



c:\cygwin\home\ユーザー名\android-ndk-1.5_r1-windows


のようになるようにしてください。



c:\cygwin\home\ユーザー名\


に.bashrcファイルが自動生成されている筈なので、そのファイルの最後尾に



export ANDROID_NDK_ROOT=C:/cygwin/home/ユーザー名/android-ndk-1.5_r1-windows


を追加して保存します。


そして、再度、cygwinのシェルを起動させます。


cygwinシェル上で、



$ cd andoird-ndk-1.5_r1


としてNDKのディレクトリに移動し、



$ sh build/host-setup.sh


と入力します。


成功するとこのように出力されます。



$ sh build/host-setup.sh
Detecting host toolchain.

CC : compiler check ok (gcc)
LD : linker check ok (gcc)
CXX : C++ compiler check ok (g++)
Generate : out/host/config.mk
Toolchain : Checking for arm-eabi-4.2.1 prebuilt binaries

Host setup complete. Please read docs/OVERVIEW.TXT if you don't know what to do.





これでNDKのインストールは完了です。











hello-jniライブラリのビルド


NDKのappsディレクトリにhello-jniのサンプルアプリがあります。今回はこれをビルドしてみます。


cygwinシェルのandoird-ndk-1.5_r1ディレクトリで以下のようにコマンドを実行します。



$ make APP=hello-jni





成功すると以下のように出力されます。



$ make APP=hello-jni
Android NDK: Building for application 'hello-jni'
Compile thumb : hello-jni <= sources/samples/hello-jni/hello-jni.c
SharedLibrary : libhello-jni.so
Install : libhello-jni.so => apps/hello-jni/project/libs/armeabi





この段階ではライブラリファイルの生成しか行われず、apkファイルは作成されません。


apkファイルを作成する為に、今回はEclipseでhello-jniプロジェクトを取り込みます。





Eclipseにhello-jniプロジェクトを取り込む




  • Eclipseが起動している状態で、Ctrl+Nキーを押下して新規プロジェクト作成画面を開き、「Android Project」を選択して「Next」ボタンを押します。


f:id:bs-android:20090707160908g:image













  • 「Create project from existiong source」を選びます。


f:id:bs-android:20090707161300g:image













  • 「Browse」ボタンを押します。


f:id:bs-android:20090707161645g:image













  • NDKを解凍したディレクトリの下の「apps/hello-jni/project」を選択して「OK」ボタンを押します。


f:id:bs-android:20090707161722g:image













  • ソースコードがインポートされ、以下のような画面になるので、「Finish」ボタンを押します。


f:id:bs-android:20090707161836g:image













  • HelloJniプロジェクトが取り込まれたところ。libsの下にlibhello-jni.soファイルが含まれています。


f:id:bs-android:20090707161943g:image











ここからはいつも通り、普通のAndroidプロジェクトとして実行可能です。





もしライブラリ側のソースコード(~/android-ndk-1.5_r1/sources/samples/hello-jni/hello-jni.c)を更新した場合は、cygwinシェルから



$ make APP=hello-jni


を再度実行してライブラリファイルを更新した後、Eclipseの「Project Explorer」のHelloJniプロジェクトのルートを選択してF5キーを押下すると、ライブラリファイルが更新されたことが検知され、再コンパイルが走ります。


この手順を忘れると、apkファイルが再生成されないので注意が必要です。





Good luck!






AndroidのNDK 1.5でHelloJNIを動かす手順



f:id:bs-android:20090707164602p:image


先日発表されたAndroidのNDK(Native Development Kit)のお陰でapkファイルにライブラリを同梱するためにソースコードをすべてダウンロードする必要がなくなった上に、Windowsだけでも作業できるようになり、開発がかなり楽になりました。


今回はNDKに付属しているhello-jniという単純なサンプルプロジェクトをWindows上でビルド、実行するまでの手順をまとめてみました。











準備


cygwinのダウンロード、インストール



  • ここからhttp://www.cygwin.com/cygwin-icon.gifのアイコンをクリックしてsetup.exeをダウンロードします。

  • setup.exeを実行すると以下のような画面になるので、「次へ(N)」を押して先に進みます。


f:id:bs-android:20090707131141g:image













  • さらに「次へ(N)」を押して先に進みます。


f:id:bs-android:20090707131332g:image













  • cygwinのrootディレクトリを変更する必要がなければ、何も変更せずに「次へ(N)」を押して先に進みます。


f:id:bs-android:20090707131437g:image













  • cygwinパッケージのディレクトリを変更する必要がなければ、何も変更せずに「次へ(N)」を押して先に進みます。


f:id:bs-android:20090707131556g:image













  • インターネット接続にProxyを設定していなければ「Direct Connection」、IEと同じ設定を使用する場合は「Use IE5 Settings」、Proxyを手動で設定する場合は「Use HTTP/FTP Proxy」を設定し、「次へ(N)」を押して先に進みます。


f:id:bs-android:20090707131634g:image













  • ダウンロード先を選んで「次へ(N)」を押して先に進みます。日本のドメインを選んだ方がスピードは速いです。また、ftpだと繋がらないことが多いので、httpのものを選んだほうが良いようです。


f:id:bs-android:20090707131848g:image













  • 以下のような画面がでるので、しばらく待ちます。


f:id:bs-android:20090707132444g:image













  • インストールするパッケージを選択する画面になります。


f:id:bs-android:20090707132531g:image













  • スクロールさせて、「Devel」の「+」ボタンを押してDevelのカテゴリのリストを展開します。


f:id:bs-android:20090707133945g:image













  • さらにスクロールし、gccのパッケージを選択(※赤丸のしるしの部分をクリック)します。自動的にgcc-coreなどにもチェックが入りますが、そのままにしておきます。


f:id:bs-android:20090707134133g:image













  • さらに下にスクロールし、makeのパッケージを選択(※赤丸のしるしの部分をクリック)します。同じく自動的に他のパッケージにもチェックが入りますが、そのままにしておきます。そして「次へ(N)」を押して先に進みます。


f:id:bs-android:20090707135430g:image













  • すると以下のような画面になるので、完了するまで待ちます。


f:id:bs-android:20090707135634g:image













  • この画面が出れば完了です。「完了」を押して終了します。


f:id:bs-android:20090707135721g:image











Windows環境変数の設定

デスクトップ上にできるhttp://www.cygwin.com/cygwin-icon.gifのアイコンから起動させると、WindowsのユーザーアカウントのディレクトリがHOMEディレクトリになり、Windows XPの場合はパスにスペースが含まれるため、後の作業がうまくいきません。


その為、環境変数「HOME」を設定して、あらかじめ任意のHOMEディレクトリを指定しておきます。





環境変数の設定方法




  • マイコンピュータのプロパティを開く


f:id:bs-android:20090707140338g:image













  • システムのプロパティ画面が出るので、「詳細設定」タブを選択します。


f:id:bs-android:20090707141333g:image













  • 詳細設定の画面で「環境変数」ボタンを押します。


f:id:bs-android:20090707141430g:image













  • 環境変数のユーザー環境変数の「新規」ボタンを押します。


f:id:bs-android:20090707141456g:image













  • 変数名に「HOME」、変数値に「/home/ユーザー名」(※ユーザー名部分は任意)を入力し、OKで抜けます。


f:id:bs-android:20090707141558g:image





これでWindows側の環境変数の設定は完了です。


デスクトップ上にできているhttp://www.cygwin.com/cygwin-icon.gifのアイコンから起動させると、



c:\cygwin\home\ユーザー名


ディレクトリが自動的に生成され、ここがHOMEディレクトリになります。











NDK1.5のダウンロード、インストール



  • ここからNDK1.5のandroid-ndk-1.5_r1-windows.zipをダウンロードします。





ダウンロードが終わったら、android-ndk-1.5_r1-windows.zipを先程自動生成された



c:\cygwin\home\ユーザー名


のディレクトリに展開します。





展開後に



c:\cygwin\home\ユーザー名\android-ndk-1.5_r1-windows


のようになるようにしてください。



c:\cygwin\home\ユーザー名\


に.bashrcファイルが自動生成されている筈なので、そのファイルの最後尾に



export ANDROID_NDK_ROOT=C:/cygwin/home/ユーザー名/android-ndk-1.5_r1-windows


を追加して保存します。


そして、再度、cygwinのシェルを起動させます。


cygwinシェル上で、



$ cd andoird-ndk-1.5_r1


としてNDKのディレクトリに移動し、



$ sh build/host-setup.sh


と入力します。


成功するとこのように出力されます。



$ sh build/host-setup.sh
Detecting host toolchain.

CC : compiler check ok (gcc)
LD : linker check ok (gcc)
CXX : C++ compiler check ok (g++)
Generate : out/host/config.mk
Toolchain : Checking for arm-eabi-4.2.1 prebuilt binaries

Host setup complete. Please read docs/OVERVIEW.TXT if you don't know what to do.





これでNDKのインストールは完了です。











hello-jniライブラリのビルド


NDKのappsディレクトリにhello-jniのサンプルアプリがあります。今回はこれをビルドしてみます。


cygwinシェルのandoird-ndk-1.5_r1ディレクトリで以下のようにコマンドを実行します。



$ make APP=hello-jni





成功すると以下のように出力されます。



$ make APP=hello-jni
Android NDK: Building for application 'hello-jni'
Compile thumb : hello-jni <= sources/samples/hello-jni/hello-jni.c
SharedLibrary : libhello-jni.so
Install : libhello-jni.so => apps/hello-jni/project/libs/armeabi





この段階ではライブラリファイルの生成しか行われず、apkファイルは作成されません。


apkファイルを作成する為に、今回はEclipseでhello-jniプロジェクトを取り込みます。





Eclipseにhello-jniプロジェクトを取り込む




  • Eclipseが起動している状態で、Ctrl+Nキーを押下して新規プロジェクト作成画面を開き、「Android Project」を選択して「Next」ボタンを押します。


f:id:bs-android:20090707160908g:image













  • 「Create project from existiong source」を選びます。


f:id:bs-android:20090707161300g:image













  • 「Browse」ボタンを押します。


f:id:bs-android:20090707161645g:image













  • NDKを解凍したディレクトリの下の「apps/hello-jni/project」を選択して「OK」ボタンを押します。


f:id:bs-android:20090707161722g:image













  • ソースコードがインポートされ、以下のような画面になるので、「Finish」ボタンを押します。


f:id:bs-android:20090707161836g:image













  • HelloJniプロジェクトが取り込まれたところ。libsの下にlibhello-jni.soファイルが含まれています。


f:id:bs-android:20090707161943g:image











ここからはいつも通り、普通のAndroidプロジェクトとして実行可能です。





もしライブラリ側のソースコード(~/android-ndk-1.5_r1/sources/samples/hello-jni/hello-jni.c)を更新した場合は、cygwinシェルから



$ make APP=hello-jni


を再度実行してライブラリファイルを更新した後、Eclipseの「Project Explorer」のHelloJniプロジェクトのルートを選択してF5キーを押下すると、ライブラリファイルが更新されたことが検知され、再コンパイルが走ります。


この手順を忘れると、apkファイルが再生成されないので注意が必要です。





Good luck!






2009年3月24日火曜日



AndroidでJNIを使う方法をドキュメントにまとめました。


PDF版はこちら


Androidのコンパイル環境を構築されていることが前提です。


よろしければAndroidのコンパイル環境を整える方法にあるPDFのコンパイル環境構築資料をご覧ください。



AndroidでJNI – Android meets JNI


株式会社ブリリアントサービス 勉どろいどチーム 


                   和泉憲二


                   門口敏広


                   藤田竜史





 このドキュメントでは、androidアプリケーション(Dalvik VM)からJNI(Java Native Interface)を使用して、C/C++言語で作成した共有ライブラリのJNIメソッドをコールする、一連の方法について解説します。


開発環境

本ドキュメントでは、以下の開発環境が用意されている事を前提に、説明を進めます。








用意する環境本ドキュメントにおける確認済みの環境
androidアプリケーション開発環境(eclipse + android ADT)WindowsXP SP3上
JDK開発環境WindowsXP SP3上のJDK 1.6.0.12
android build環境*1WindowsXP SP3のVMware上にUbuntu Linux 8.04日本語版で構築

本ドキュメントで作成する、サンプルアプリケーションの説明


androidアプリ側からJNIメソッドをコールし、JNIメソッドより返却された文字列をandroidアプリのTextViewで描画します。







androidアプリケーションJNItestJNIメソッドから返却された文字列をTextViewを使用して画面上に表示する。
共有ライブラリモジュールlibJNItestNativeコールされた戻り値として、文字列を返却する。

また今回作成するネイティブメソッドの仕様は、以下の通りとします。









ネイティブメソッド名getTestStringFromNative
引数なし
戻り値String
説明戻り値として、“from Native Code String”という文字列を返却します。




1.android アプリケーション JNItest の作成

androidアプリケーションの作成

まず、JNIヘッダの生成を行うために、ライブラリのロードとネイティブメソッド定義を含むソースコードを作成します。


eclipseのandroid ADTを使用し、以下の構成で新規プロジェクトの作成を行います。









プロジェクト名JNItest
パッケージ名jp.co.brilliantservice.JNItestPkg
アクティビティー名JNItest
アプリケーション名JNItest

f:id:bs-android:20090324114821p:image


次に、TextViewにJNIメソッドからの返却文字列を設定するために、レイアウトファイル(main.xml)の編集を行います。


パッケージエクスプローラーの [JNItest]-[res]-[layout]-main.xmlを以下のように編集します。



01 : <?xml version="1.0" encoding="utf-8"?>
02 : <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
03 : android:orientation="vertical"
04 : android:layout_width="fill_parent"
05 : android:layout_height="fill_parent"
06 : >
07 : <TextView
08 : android:id="@+id/txtTest"
09 : android:layout_width="fill_parent"
10 : android:layout_height="wrap_content"
11 : />
12 : </LinearLayout>


8行目にandroid id txtTest定義を付加し、ウィザードで自動生成されるhello world文字列の表示を削除します。


次に、JNItest.javaの編集を行います。



01 : package jp.co.brilliantservice.JNItestPkg;
02 :
03 : import android.app.Activity;
04 : import android.os.Bundle;
05 : import android.widget.TextView;
06 :
07 : public class JNItest extends Activity {
08 : static {
09 : // ライブラリをロード
10 : System.loadLibrary("JNItestNative");
11 : }
12 : // ネイティブメソッドを定義
13 : public native String getTestStringFromNative();
14 :
15 : /** Called when the activity is first created. */
16 : @Override
17 : public void onCreate(Bundle savedInstanceState) {
18 : super.onCreate(savedInstanceState);
19 : setContentView(R.layout.main);
20 :
21 : // ネイティブメソッドをコールして文字列を取得
22 : String strText = getTestStringFromNative();
23 :
24 : // 取得文字列をTextViewに設定
25 : TextView txtTest = (TextView)findViewById(R.id.txtTest);
26 : txtTest.setText(strText);
27 : }
28 : }


10行目で共有ライブラリのロードを行います。また、13行目ではネイティブメソッドの定義を行っています。





JNIヘッダファイルの生成

「androidアプリケーションの作成」で、eclipse上でのコンパイルが正常に行われ、以下のディレクトリにJNItest.classが生成されている事を確認します。


JNItest\bin\jp\co\brilliantservice\JNItestPkg\


次に、javah(JDK付属のJNIヘッダ作成ツール)を使用して、JNI実装用のC/C++言語ヘッダを生成します。


コマンドプロンプトを起動し、eclipseプロジェクトのJNItestディレクトリにcd で移動の上、以下のコマンドを実行します。



JNItest>javah -classpath bin -d jni jp.co.brilliantservice.JNItestPkg.JNItest


ディレクトリJNItest\jniにjp_co_brilliantservice_JNItestPkg_JNItest.h が生成されます。このファイルが、JNIヘッダファイルです。



JNIヘッダファイル jp_co_brilliantservice_JNItestPkg_JNItest.h



01 : /* DO NOT EDIT THIS FILE - it is machine generated */
02 : #include <jni.h>
03 : /* Header for class jp_co_brilliantservice_JNItestPkg_JNItest */
04 :
05 : #ifndef _Included_jp_co_brilliantservice_JNItestPkg_JNItest
06 : #define _Included_jp_co_brilliantservice_JNItestPkg_JNItest
07 : #ifdef __cplusplus
08 : extern "C" {
09 : #endif
10 : /*
11 : * Class: jp_co_brilliantservice_JNItestPkg_JNItest
12 : * Method: getTestStringFromNative
13 : * Signature: ()Ljava/lang/String;
14 : */
15 : JNIEXPORT jstring JNICALL Java_jp_co_brilliantservice_JNItestPkg_JNItest_getTestStringFromNative
16 : (JNIEnv *, jobject);
17 :
18 : #ifdef __cplusplus
19 : }
20 : #endif
21 : #endif






2.共有ライブラリ libJNItestNative.so の作成

文字列を返却するメソッドを、android build環境上にC言語で実装します。


その際、「JNIヘッダファイルの生成」 で自動生成した、JNIヘッダで宣言されているJNI関数プロトタイプに合わせて実装します。





本ドキュメントにおける、android build環境(ソースコードツリー)のビルドルート及び共有ライブラリ作成位置は以下の通りとします。








ビルドルート~/mydroid
共有ライブラリ作成位置~/mydroid/external/libJNItestNative
共有ライブラリファイル名libJNItestNative.so

上記共有ライブラリ作成位置には以下のファイルを作成または用意します。本項ではJNIメソッドの実装・Makefileの作成方法について説明します。








JNIメソッドソースファイル名GetTestStringFromNative.c
MakefileAndroid.mk
JNIヘッダファイルjp_co_brilliantservice_JNItestPkg_JNItest.h




JNIメソッドの実装

JNIのメソッドは以下のようなソースになります。


メソッド実行時に、単純に文字列を返却するのみのコードです。



getTestStringFromNative.c



1 : #include "jp_co_brilliantservice_JNItestPkg_JNItest.h"
2 :
3 : JNIEXPORT jstring JNICALL Java_jp_co_brilliantservice_JNItestPkg_JNItest_getTestStringFromNative
4 : ( JNIEnv *env, jobject obj )
5 : {
6 : return (*env)->NewStringUTF(env, (char *)"from Native Code String");
7 : }






Makefile 「Android.mk」の作成

androidの個別ビルド用Makefile 「Android.mk」 の作成を行います。



Android.mk



01 : LOCAL_PATH:= $(call my-dir)
02 :
03 : include $(CLEAR_VARS)
04 :
05 : LOCAL_SRC_FILES := \
06 : getTestStringFromNative.c
07 :
08 : LOCAL_C_INCLUDES := \
09 : $(JNI_H_INCLUDE) \
10 :
11 : LOCAL_MODULE := libJNItestNative
12 :
13 : LOCAL_PRELINK_MODULE := false
14 :
15 : include $(BUILD_SHARED_LIBRARY)



5・6行目にソースファイル名、11行目にライブラリモジュール名を定義しています。


上記をふまえ、androidでJNIを実現する上において、最も注目すべき点は、以下の3点です。




  • JNIヘッダをインクルードするための定義(9行目)

  • prelinkを解除するための定義(13行目)

  • 共有ライブラリをビルドするための定義(15行目)


prelink関連の情報については、後述のNote:でまとめます。





共有ライブラリのビルド

端末コンソールを起動の上、以下のコマンドを実行し、ディレクトリ内のみのビルドを行えるように、~/mydroid/build/ にある、envsetup.sh を評価しておきます。



$cd ~/mydroid
$. build/envsetup.sh


続いて、以下のコマンドを実行し、共有ライブラリ位置のビルドを行います。



$cd external/libJNItestNative/
$mm


ビルドが成功すると、以下のようにログが表示されます。



make: ディレクトリ `/home/kenken/mydroid' に入ります
build/core/product_config.mk:211: WARNING: adding test OTA key
============================================
TARGET_PRODUCT=generic
TARGET_BUILD_VARIANT=eng
TARGET_SIMULATOR=
TARGET_BUILD_TYPE=release
TARGET_ARCH=arm
HOST_ARCH=x86
HOST_OS=linux
HOST_BUILD_TYPE=release
BUILD_ID=
============================================
build/core/main.mk:180: implicitly installing apns-conf_sdk.xml
target thumb C: libJNItestNative <= /home/kenken/mydroid/external/libJNItestNative/getTestStringFromNative.c
target SharedLib: libJNItestNative (out/target/product/generic/obj/SHARED_LIBRARIES/libJNItestNative_intermediates/LINKED/libJNItestNative.so)
target Non-prelinked: libJNItestNative (out/target/product/generic/symbols/system/lib/libJNItestNative.so)
target Strip: libJNItestNative (out/target/product/generic/obj/lib/libJNItestNative.so)
Install: out/target/product/generic/system/lib/libJNItestNative.so
Finding NOTICE files: out/target/product/generic/obj/NOTICE_FILES/hash-timestamp
Combining NOTICE files: out/target/product/generic/obj/NOTICE.html
gzip -c out/target/product/generic/obj/NOTICE.html > out/target/product/generic/obj/NOTICE.html.gz
make: ディレクトリ `/home/kenken/mydroid' から出ます


共有ライブラリ libJNItestNative.so は以下のディレクトリに格納されます。


~mydroid/out/target/product/generic/system/lib/





Note: prelink map 及び、共有ライブラリにおけるprelink定義について

androidの共有ライブラリはデフォルトでprelink mapというテキストファイルに、メモリマップテーブルに固定アドレスでマッピングするように構成されています。


(~/mydroid/build/core/prelink-linux-arm.map)


これは、メモリ上への頻繁なロード・アンロードを避け、速度アップをするための処置となっています。



~/mydroid/build/core/prelink-linux-arm.map (抜粋)



001 :
002 : # 0xC0000000 - 0xFFFFFFFF Kernel
003 : # 0xB0100000 - 0xBFFFFFFF Thread 0 Stack
004 : # 0xB0000000 - 0xB00FFFFF Linker
005 : # 0xA0000000 - 0xBFFFFFFF Prelinked System Libraries
006 : # 0x90000000 - 0x9FFFFFFF Prelinked App Libraries
007 : # 0x80000000 - 0x8FFFFFFF Non-prelinked Libraries
008 : # 0x40000000 - 0x7FFFFFFF mmap'd stuff
009 : # 0x10000000 - 0x3FFFFFFF Thread Stacks
010 : # 0x00000000 - 0x0FFFFFFF .text / .data / heap
011 :
012 : # core system libraries
013 : libdl.so 0xAFF00000



112 : libctest.so 0x9A700000
113 : libUAPI_jni.so 0x9A500000
114 : librpc.so 0x9A400000
115 : libtrace_test.so 0x9A300000
116 : libsrec_jni.so 0x9A200000



上記のように、prelink mapに登録するモジュール名及び固定アドレスを列挙して定義を行います。


しかし、固定アドレスにマッピングされるという事は、ビルドの度にシステムイメージまで作成し、入れ替えないといけないという事を意味しています。


これではJNIを使用するアプリケーションはインストールを自由に行うことが極めて困難であると言わざるを得ません。


Android.mkでは、デフォルト動作がprelink map有効となっており、prelink mapに共有ライブラリを登録しないと、ビルドエラーが発生してしまいます。



ビルドエラーの例



01 : ~/mydroid/external/libJNItestNative$ mm
02 : make: ディレクトリ `/home/kenken/mydroid' に入ります
03 : build/core/product_config.mk:211: WARNING: adding test OTA key
04 : ============================================
05 : TARGET_PRODUCT=generic
06 : TARGET_BUILD_VARIANT=eng
07 : TARGET_SIMULATOR=
08 : TARGET_BUILD_TYPE=release
09 : TARGET_ARCH=arm
10 : HOST_ARCH=x86
11 : HOST_OS=linux
12 : HOST_BUILD_TYPE=release
13 : BUILD_ID=
14 : ============================================
15 : build/core/main.mk:180: implicitly installing apns-conf_sdk.xml
16 : target thumb C: libJNItestNative <= /home/kenken/mydroid/external/libJNItestNative/getTestStringFromNative.c
17 : target SharedLib: libJNItestNative (out/target/product/generic/obj/SHARED_LIBRARIES/libJNItestNative_intermediates/LINKED/libJNItestNative.so)
18 : target Prelink: libJNItestNative (out/target/product/generic/symbols/system/lib/libJNItestNative.so)
19 : build/tools/apriori/prelinkmap.c(137): library 'libJNItestNative.so' not in prelink map
20 : make: *** [out/target/product/generic/symbols/system/lib/libJNItestNative.so] エラー 1
21 : make: ディレクトリ `/home/kenken/mydroid' から出ます



上記のビルドエラーの場合、19行目に、「prelink mapに含まれていない」(library 'libJNItestNative.so' not in prelink map)というエラーが確認できます。


prelink mapに登録せず、ビルドを行うためには、Android.mkに以下の1行を追加し、ビルドを行います。(prelink 無効化の定義)



LOCAL_PRELINK_MODULE := false





3.アプリケーションの実行

androidアプリ実行の前に、共有ライブラリ libJNItestNative.so を、あらかじめエミュレータの/sysytem/lib ディレクトリにコピーします。


1. コマンドプロンプトを起動し、androidエミュレータを以下のコマンドで起動します。



>start emulator


2. エミュレータ上のディレクトリ /system/lib への書き込みを有効にするために、以下のコマンドを実行します。



>adb remount


3. 共有ライブラリを /system/lib ディレクトリにコピーします。カレントディレクトリに、「2. 共有ライブラリ libJNItestNative.so の作成」で作成したモジュールをあらかじめ用意した上、以下のコマンドを実行します。



>adb push libJNItestNative.so /system/lib


4. androidエミュレータの起動を維持したままの状態で、eclipseからandroidアプリJNItestを


起動します。


起動に成功すると、以下のように実行結果が表示されます。


f:id:bs-android:20090324120809p:image



以上です。


もし、記述間違いなどがありましたらご指摘いただけると幸いです。




*1:androidのソースコードツリーでシステムイメージがbuild出来る環境。ソースの入手方法・ビルド環境構築方法については、こちらを参照。





AndroidでJNIを使う方法



AndroidでJNIを使う方法をドキュメントにまとめました。


PDF版はこちら


Androidのコンパイル環境を構築されていることが前提です。


よろしければAndroidのコンパイル環境を整える方法にあるPDFのコンパイル環境構築資料をご覧ください。



AndroidでJNI – Android meets JNI


株式会社ブリリアントサービス 勉どろいどチーム 


                   和泉憲二


                   門口敏広


                   藤田竜史





 このドキュメントでは、androidアプリケーション(Dalvik VM)からJNI(Java Native Interface)を使用して、C/C++言語で作成した共有ライブラリのJNIメソッドをコールする、一連の方法について解説します。


開発環境

本ドキュメントでは、以下の開発環境が用意されている事を前提に、説明を進めます。








用意する環境本ドキュメントにおける確認済みの環境
androidアプリケーション開発環境(eclipse + android ADT)WindowsXP SP3上
JDK開発環境WindowsXP SP3上のJDK 1.6.0.12
android build環境*1WindowsXP SP3のVMware上にUbuntu Linux 8.04日本語版で構築

本ドキュメントで作成する、サンプルアプリケーションの説明


androidアプリ側からJNIメソッドをコールし、JNIメソッドより返却された文字列をandroidアプリのTextViewで描画します。







androidアプリケーションJNItestJNIメソッドから返却された文字列をTextViewを使用して画面上に表示する。
共有ライブラリモジュールlibJNItestNativeコールされた戻り値として、文字列を返却する。

また今回作成するネイティブメソッドの仕様は、以下の通りとします。









ネイティブメソッド名getTestStringFromNative
引数なし
戻り値String
説明戻り値として、“from Native Code String”という文字列を返却します。




1.android アプリケーション JNItest の作成

androidアプリケーションの作成

まず、JNIヘッダの生成を行うために、ライブラリのロードとネイティブメソッド定義を含むソースコードを作成します。


eclipseのandroid ADTを使用し、以下の構成で新規プロジェクトの作成を行います。









プロジェクト名JNItest
パッケージ名jp.co.brilliantservice.JNItestPkg
アクティビティー名JNItest
アプリケーション名JNItest

f:id:bs-android:20090324114821p:image


次に、TextViewにJNIメソッドからの返却文字列を設定するために、レイアウトファイル(main.xml)の編集を行います。


パッケージエクスプローラーの [JNItest]-[res]-[layout]-main.xmlを以下のように編集します。



01 : <?xml version="1.0" encoding="utf-8"?>
02 : <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
03 : android:orientation="vertical"
04 : android:layout_width="fill_parent"
05 : android:layout_height="fill_parent"
06 : >
07 : <TextView
08 : android:id="@+id/txtTest"
09 : android:layout_width="fill_parent"
10 : android:layout_height="wrap_content"
11 : />
12 : </LinearLayout>


8行目にandroid id txtTest定義を付加し、ウィザードで自動生成されるhello world文字列の表示を削除します。


次に、JNItest.javaの編集を行います。



01 : package jp.co.brilliantservice.JNItestPkg;
02 :
03 : import android.app.Activity;
04 : import android.os.Bundle;
05 : import android.widget.TextView;
06 :
07 : public class JNItest extends Activity {
08 : static {
09 : // ライブラリをロード
10 : System.loadLibrary("JNItestNative");
11 : }
12 : // ネイティブメソッドを定義
13 : public native String getTestStringFromNative();
14 :
15 : /** Called when the activity is first created. */
16 : @Override
17 : public void onCreate(Bundle savedInstanceState) {
18 : super.onCreate(savedInstanceState);
19 : setContentView(R.layout.main);
20 :
21 : // ネイティブメソッドをコールして文字列を取得
22 : String strText = getTestStringFromNative();
23 :
24 : // 取得文字列をTextViewに設定
25 : TextView txtTest = (TextView)findViewById(R.id.txtTest);
26 : txtTest.setText(strText);
27 : }
28 : }


10行目で共有ライブラリのロードを行います。また、13行目ではネイティブメソッドの定義を行っています。





JNIヘッダファイルの生成

「androidアプリケーションの作成」で、eclipse上でのコンパイルが正常に行われ、以下のディレクトリにJNItest.classが生成されている事を確認します。


JNItest\bin\jp\co\brilliantservice\JNItestPkg\


次に、javah(JDK付属のJNIヘッダ作成ツール)を使用して、JNI実装用のC/C++言語ヘッダを生成します。


コマンドプロンプトを起動し、eclipseプロジェクトのJNItestディレクトリにcd で移動の上、以下のコマンドを実行します。



JNItest>javah -classpath bin -d jni jp.co.brilliantservice.JNItestPkg.JNItest


ディレクトリJNItest\jniにjp_co_brilliantservice_JNItestPkg_JNItest.h が生成されます。このファイルが、JNIヘッダファイルです。



JNIヘッダファイル jp_co_brilliantservice_JNItestPkg_JNItest.h



01 : /* DO NOT EDIT THIS FILE - it is machine generated */
02 : #include <jni.h>
03 : /* Header for class jp_co_brilliantservice_JNItestPkg_JNItest */
04 :
05 : #ifndef _Included_jp_co_brilliantservice_JNItestPkg_JNItest
06 : #define _Included_jp_co_brilliantservice_JNItestPkg_JNItest
07 : #ifdef __cplusplus
08 : extern "C" {
09 : #endif
10 : /*
11 : * Class: jp_co_brilliantservice_JNItestPkg_JNItest
12 : * Method: getTestStringFromNative
13 : * Signature: ()Ljava/lang/String;
14 : */
15 : JNIEXPORT jstring JNICALL Java_jp_co_brilliantservice_JNItestPkg_JNItest_getTestStringFromNative
16 : (JNIEnv *, jobject);
17 :
18 : #ifdef __cplusplus
19 : }
20 : #endif
21 : #endif






2.共有ライブラリ libJNItestNative.so の作成

文字列を返却するメソッドを、android build環境上にC言語で実装します。


その際、「JNIヘッダファイルの生成」 で自動生成した、JNIヘッダで宣言されているJNI関数プロトタイプに合わせて実装します。





本ドキュメントにおける、android build環境(ソースコードツリー)のビルドルート及び共有ライブラリ作成位置は以下の通りとします。








ビルドルート~/mydroid
共有ライブラリ作成位置~/mydroid/external/libJNItestNative
共有ライブラリファイル名libJNItestNative.so

上記共有ライブラリ作成位置には以下のファイルを作成または用意します。本項ではJNIメソッドの実装・Makefileの作成方法について説明します。








JNIメソッドソースファイル名GetTestStringFromNative.c
MakefileAndroid.mk
JNIヘッダファイルjp_co_brilliantservice_JNItestPkg_JNItest.h




JNIメソッドの実装

JNIのメソッドは以下のようなソースになります。


メソッド実行時に、単純に文字列を返却するのみのコードです。



getTestStringFromNative.c



1 : #include "jp_co_brilliantservice_JNItestPkg_JNItest.h"
2 :
3 : JNIEXPORT jstring JNICALL Java_jp_co_brilliantservice_JNItestPkg_JNItest_getTestStringFromNative
4 : ( JNIEnv *env, jobject obj )
5 : {
6 : return (*env)->NewStringUTF(env, (char *)"from Native Code String");
7 : }






Makefile 「Android.mk」の作成

androidの個別ビルド用Makefile 「Android.mk」 の作成を行います。



Android.mk



01 : LOCAL_PATH:= $(call my-dir)
02 :
03 : include $(CLEAR_VARS)
04 :
05 : LOCAL_SRC_FILES := \
06 : getTestStringFromNative.c
07 :
08 : LOCAL_C_INCLUDES := \
09 : $(JNI_H_INCLUDE) \
10 :
11 : LOCAL_MODULE := libJNItestNative
12 :
13 : LOCAL_PRELINK_MODULE := false
14 :
15 : include $(BUILD_SHARED_LIBRARY)



5・6行目にソースファイル名、11行目にライブラリモジュール名を定義しています。


上記をふまえ、androidでJNIを実現する上において、最も注目すべき点は、以下の3点です。




  • JNIヘッダをインクルードするための定義(9行目)

  • prelinkを解除するための定義(13行目)

  • 共有ライブラリをビルドするための定義(15行目)


prelink関連の情報については、後述のNote:でまとめます。





共有ライブラリのビルド

端末コンソールを起動の上、以下のコマンドを実行し、ディレクトリ内のみのビルドを行えるように、~/mydroid/build/ にある、envsetup.sh を評価しておきます。



$cd ~/mydroid
$. build/envsetup.sh


続いて、以下のコマンドを実行し、共有ライブラリ位置のビルドを行います。



$cd external/libJNItestNative/
$mm


ビルドが成功すると、以下のようにログが表示されます。



make: ディレクトリ `/home/kenken/mydroid' に入ります
build/core/product_config.mk:211: WARNING: adding test OTA key
============================================
TARGET_PRODUCT=generic
TARGET_BUILD_VARIANT=eng
TARGET_SIMULATOR=
TARGET_BUILD_TYPE=release
TARGET_ARCH=arm
HOST_ARCH=x86
HOST_OS=linux
HOST_BUILD_TYPE=release
BUILD_ID=
============================================
build/core/main.mk:180: implicitly installing apns-conf_sdk.xml
target thumb C: libJNItestNative <= /home/kenken/mydroid/external/libJNItestNative/getTestStringFromNative.c
target SharedLib: libJNItestNative (out/target/product/generic/obj/SHARED_LIBRARIES/libJNItestNative_intermediates/LINKED/libJNItestNative.so)
target Non-prelinked: libJNItestNative (out/target/product/generic/symbols/system/lib/libJNItestNative.so)
target Strip: libJNItestNative (out/target/product/generic/obj/lib/libJNItestNative.so)
Install: out/target/product/generic/system/lib/libJNItestNative.so
Finding NOTICE files: out/target/product/generic/obj/NOTICE_FILES/hash-timestamp
Combining NOTICE files: out/target/product/generic/obj/NOTICE.html
gzip -c out/target/product/generic/obj/NOTICE.html > out/target/product/generic/obj/NOTICE.html.gz
make: ディレクトリ `/home/kenken/mydroid' から出ます


共有ライブラリ libJNItestNative.so は以下のディレクトリに格納されます。


~mydroid/out/target/product/generic/system/lib/





Note: prelink map 及び、共有ライブラリにおけるprelink定義について

androidの共有ライブラリはデフォルトでprelink mapというテキストファイルに、メモリマップテーブルに固定アドレスでマッピングするように構成されています。


(~/mydroid/build/core/prelink-linux-arm.map)


これは、メモリ上への頻繁なロード・アンロードを避け、速度アップをするための処置となっています。



~/mydroid/build/core/prelink-linux-arm.map (抜粋)



001 :
002 : # 0xC0000000 - 0xFFFFFFFF Kernel
003 : # 0xB0100000 - 0xBFFFFFFF Thread 0 Stack
004 : # 0xB0000000 - 0xB00FFFFF Linker
005 : # 0xA0000000 - 0xBFFFFFFF Prelinked System Libraries
006 : # 0x90000000 - 0x9FFFFFFF Prelinked App Libraries
007 : # 0x80000000 - 0x8FFFFFFF Non-prelinked Libraries
008 : # 0x40000000 - 0x7FFFFFFF mmap'd stuff
009 : # 0x10000000 - 0x3FFFFFFF Thread Stacks
010 : # 0x00000000 - 0x0FFFFFFF .text / .data / heap
011 :
012 : # core system libraries
013 : libdl.so 0xAFF00000



112 : libctest.so 0x9A700000
113 : libUAPI_jni.so 0x9A500000
114 : librpc.so 0x9A400000
115 : libtrace_test.so 0x9A300000
116 : libsrec_jni.so 0x9A200000



上記のように、prelink mapに登録するモジュール名及び固定アドレスを列挙して定義を行います。


しかし、固定アドレスにマッピングされるという事は、ビルドの度にシステムイメージまで作成し、入れ替えないといけないという事を意味しています。


これではJNIを使用するアプリケーションはインストールを自由に行うことが極めて困難であると言わざるを得ません。


Android.mkでは、デフォルト動作がprelink map有効となっており、prelink mapに共有ライブラリを登録しないと、ビルドエラーが発生してしまいます。



ビルドエラーの例



01 : ~/mydroid/external/libJNItestNative$ mm
02 : make: ディレクトリ `/home/kenken/mydroid' に入ります
03 : build/core/product_config.mk:211: WARNING: adding test OTA key
04 : ============================================
05 : TARGET_PRODUCT=generic
06 : TARGET_BUILD_VARIANT=eng
07 : TARGET_SIMULATOR=
08 : TARGET_BUILD_TYPE=release
09 : TARGET_ARCH=arm
10 : HOST_ARCH=x86
11 : HOST_OS=linux
12 : HOST_BUILD_TYPE=release
13 : BUILD_ID=
14 : ============================================
15 : build/core/main.mk:180: implicitly installing apns-conf_sdk.xml
16 : target thumb C: libJNItestNative <= /home/kenken/mydroid/external/libJNItestNative/getTestStringFromNative.c
17 : target SharedLib: libJNItestNative (out/target/product/generic/obj/SHARED_LIBRARIES/libJNItestNative_intermediates/LINKED/libJNItestNative.so)
18 : target Prelink: libJNItestNative (out/target/product/generic/symbols/system/lib/libJNItestNative.so)
19 : build/tools/apriori/prelinkmap.c(137): library 'libJNItestNative.so' not in prelink map
20 : make: *** [out/target/product/generic/symbols/system/lib/libJNItestNative.so] エラー 1
21 : make: ディレクトリ `/home/kenken/mydroid' から出ます



上記のビルドエラーの場合、19行目に、「prelink mapに含まれていない」(library 'libJNItestNative.so' not in prelink map)というエラーが確認できます。


prelink mapに登録せず、ビルドを行うためには、Android.mkに以下の1行を追加し、ビルドを行います。(prelink 無効化の定義)



LOCAL_PRELINK_MODULE := false





3.アプリケーションの実行

androidアプリ実行の前に、共有ライブラリ libJNItestNative.so を、あらかじめエミュレータの/sysytem/lib ディレクトリにコピーします。


1. コマンドプロンプトを起動し、androidエミュレータを以下のコマンドで起動します。



>start emulator


2. エミュレータ上のディレクトリ /system/lib への書き込みを有効にするために、以下のコマンドを実行します。



>adb remount


3. 共有ライブラリを /system/lib ディレクトリにコピーします。カレントディレクトリに、「2. 共有ライブラリ libJNItestNative.so の作成」で作成したモジュールをあらかじめ用意した上、以下のコマンドを実行します。



>adb push libJNItestNative.so /system/lib


4. androidエミュレータの起動を維持したままの状態で、eclipseからandroidアプリJNItestを


起動します。


起動に成功すると、以下のように実行結果が表示されます。


f:id:bs-android:20090324120809p:image



以上です。


もし、記述間違いなどがありましたらご指摘いただけると幸いです。




*1:androidのソースコードツリーでシステムイメージがbuild出来る環境。ソースの入手方法・ビルド環境構築方法については、こちらを参照。





Related Posts Plugin for WordPress, Blogger...