Android Hackathon 2009.3.20 Android で JNI 餃子のまち 宇都宮 村沢 邦夫
Android Hackathon 2009.3.20
Android で JNI
餃子のまち 宇都宮
村沢 邦夫
必要なもの• Android SDK
– 1.1_r1で試してます
• Eclipse• android.git.kernel.org より
– toolchain• C コンパイラ arm-eabi-gcc など
– bionic• C ライブラリ / ヘッダファイル
• glibc でない
• Dynamic Linker / Loader も独自
– dalvik• jni.h
準備
• Android のソースを取得してビルド– 手順は次の URL に詳しく書いてあります
– http://source.android.com/download– 実行環境に合わせてブランチを選んでね
• 2009.3.14頃の master で試してます
• ビルドされたモノは /xxx/out に出来る– /xxx は Android ソースを置いたディレクトリ
– /xxx は自分の環境に合わせて直してね
ツールチェイン
• Android クロス開発用 toolchain– arm-eabi-gcc コマンド等
• 環境変数 PATH に設定– /xxx/prebuilt/darwin-x86/toolchain/arm-eabi-4.2.1/bin
– 自分の環境に合わせて darwin-x86 の部分は直してね
• Mac OS X は darwin-x86
• Linux は linux-x86
• Windows は windows
ライブラリ
• Android 用ライブラリ
• 必要に応じてライブラリパスに設定– /xxx/out/target/product/generic/obj/lib
ヘッダファイル
• Android 用ヘッダファイル
• 必要に応じてインクルードパスに設定– /xxx/bionic/libc/include– /xxx/bionic/libc/kernel/arch-arm– /xxx/libc/arch-arm/include– /xxx/kernel/include
JNI
• Android 用 JNI ヘッダファイル
• インクルードパスに設定– /xxx/dalvik/libnativehelper/include/nativehelper
その他(1)
• リンカスクリプト(shared library 用)– /xxx/build/core/armelf.xsc
• Dynamic Linker / Loader– /system/bin/linker
• Android のライブラリ置き場 (LD_LIBRARY_PATH)
– /system/lib
その他(2)
• agcc
– http://plausible.org/andy/agcc– コンパイル用スクリプト
– ライブラリ、インクルードパス等を自動で設定してくれるので便利
– 以降、コンパイルは agcc を使って説明
JNI 作成手順
① Java コード作成
② クラスファイル生成
③ C / C++ 用ヘッダファイル生成
④ C / C++ コード作成
⑤ 共有ライブラリ生成
JNI で Hello World
• HelloWorld クラスを作成– パッケージ名 jni_test.helloWorldJNI
• ネイティブメソッドとして get を用意– 「Hello JNI World」の文字列を返す
• ネイティブライブラリは libHelloWorld.so とする– ソースは HelloWorld.c
• Activity は HelloWorldJNI とし HelloWorld クラスの get メソッドで文字列を取得し表示
① Java コード作成 HelloWorld.java
package jni_test.helloWorldJNI;
public class HelloWorld {static {
System.loadLibrary("HelloWorld");}
public native String get();}
※System.loadLibrary で libHelloWorld.so をロード※ネイティブメソッドには native キーワードをつける
② クラスファイル生成HelloWorld.class
• Eclipse でコンパイルすればOK• コマンドラインでやるなら
$ javac jni_test/helloWorldJNI/HelloWorld.java
③ C / C++ 用ヘッダファイル生成jni_test_helloWorldJNI_HelloWorld.h
$ javah jni_test.helloWorldJNI.HelloWorld
• javah コマンドで生成– クラスファイルからヘッダファイルを生成
– jni_test_helloWorldJNI_HelloWorld.h– C / C++ コードでインクルード
• Eclipse からやる方法は知らない(汗)
生成されたヘッダファイルjni_test_helloWorldJNI_HelloWorld.h
/* DO NOT EDIT THIS FILE - it is machine generated */#include <jni.h>/* Header for class jni_test_helloWorldJNI_HelloWorld */
#ifndef _Included_jni_test_helloWorldJNI_HelloWorld#define _Included_jni_test_helloWorldJNI_HelloWorld#ifdef __cplusplusextern "C" {#endif/* * Class: jni_test_helloWorldJNI_HelloWorld * Method: get * Signature: ()Ljava/lang/String; */JNIEXPORT jstring JNICALL Java_jni_1test_helloWorldJNI_HelloWorld_get (JNIEnv *, jobject);
#ifdef __cplusplus}#endif#endif
④ C / C++ コード作成HelloWorld.c
#include "jni_test_helloWorldJNI_HelloWorld.h"
JNIEXPORT jstring JNICALL Java_jni_1test_helloWorldJNI_HelloWorld_get(JNIEnv *env, jobject obj){ return (*env)->NewStringUTF(env, "Hello JNI World");}
• HelloWorld.c を作成– 先程生成したヘッダファイルをベースに
– get メソッドに対応する部分を実装• Java_jni_helloWolrdJNI_HelloWorld_get
⑤ 共有ライブラリ生成libHelloWorld.so
$ agcc -shared -fPIC HelloWorld.c -o libHelloWorld.so -I/xxx/dalvik/libnativehelper/include/nativehelper
※実際は1行。Eclipse からやる方法は知らない(汗)
• HelloWorld.c から libHelloWorld.so を作る● agcc を使ってコンパイル● shared オプションを付けて共有ライブラリに● インクルードパスに jni.h の在処を指定
共有ライブラリを転送
$ adb shell# mount -o rw,remount /dev/block/mtdblock3 /system# exit$ adb push libHelloWorld.so /system/lib
※実機の場合は未確認 ・端末屋さんならルートファイルシステムに入れておけばいいよね ・アプリ屋さんなら jar ファイルに入れて配布? → so ファイルを取り出して LD_LIBRARY_PATH に設定かな?
• Android に libHelloWorld.so を転送– /system/lib– Read only なので R/W に変更
HelloWorld のテストコードHelloWorldJNI.java
package jni_test.helloWorldJNI;
import android.app.Activity;import android.os.Bundle;import android.widget.TextView;
public class HelloWorldJNI extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main);
TextView tv = new TextView(this); tv.setText(new HelloWorld().get()); setContentView(tv); }}
実行結果
おわり