Hatena::Groupandroid

naoya_t@android RSSフィード

 Android入門記兼備忘録 since 2009.6.9
 | 

2009-07-01

NDK付属のサンプル(その1)hello-jni

| 01:11 | NDK付属のサンプル(その1)hello-jni - naoya_t@android を含むブックマーク はてなブックマーク - NDK付属のサンプル(その1)hello-jni - naoya_t@android

共有ライブラリに実装されたネイティブメソッドから文字列をロードし、それをアプリケーションのUIに表示するだけの簡単なアプリケーション。

android-ndk-1.5_r1> 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

解剖

sources/samples/hello-jni/Android.mk:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := hello-jni
LOCAL_SRC_FILES := hello-jni.c

include $(BUILD_SHARED_LIBRARY)

sources/samples/hello-jni/hello-jni.c:

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

jstring Java_com_example_hellojni_HelloJni_stringFromJNI( JNIEnv* env, jobject thiz )
{
    return (*env)->NewStringUTF(env, "Hello from JNI !");
}

apps/hello-jni/Application.mk:

APP_PROJECT_PATH := $(call my-dir)/project
APP_MODULES      := hello-jni

apps/hello-jni/project/src/com/example/hellojni/HelloJni.java:

package com.example.hellojni;

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

public class HelloJni extends Activity
{
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        TextView  tv = new TextView(this);
        tv.setText( stringFromJNI() );
        setContentView(tv);
    }

    public native String  stringFromJNI();
    public native String  unimplementedStringFromJNI(); // 実装してないメソッドを定義してもよい(が、呼び出したら java.lang.UnsatisfiedLinkError を食らうよ)

    // ちなみにライブラリはインストール時に /data/data/com.example.HelloJni/lib/libhello-jni.so に配置されます
    static {
        System.loadLibrary("hello-jni");
    }
}
Eclipseに取り込む
  • 新規 / プロジェクト
    • Android Project
      • Create project from existing source ... <ndk>/apps/hello-jni/project/ から

プロジェクトで

Conversion to Dalvik format failed with error 1

エラーが出たら「プロジェクトのクリーンを実行

http://img.f.hatena.ne.jp/images/fotolife/n/n4_t/20090702/20090702024609.png

NDK付属のサンプル(その2)two-libs

10:53 | NDK付属のサンプル(その2)two-libs - naoya_t@android を含むブックマーク はてなブックマーク - NDK付属のサンプル(その2)two-libs - naoya_t@android

動的に共有ライブラリをロードし、そのライブラリが提供するネイティブメソッドを1つ呼び出すだけの簡単なアプリケーション。今回は、メソッドは共有ライブラリがインポートする静的ライブラリで実装されている。

android-ndk-1.5_r1> make APP=two-libs 
Android NDK: Building for application 'two-libs'    
Compile thumb  : twolib-second <= sources/samples/two-libs/second.c
Compile thumb  : twolib-first <= sources/samples/two-libs/first.c
StaticLibrary  : libtwolib-first.a
SharedLibrary  : libtwolib-second.so
Install        : libtwolib-second.so => apps/two-libs/project/libs/armeab

解剖

sources/samples/two-libs/Android.mk:

LOCAL_PATH:= $(call my-dir)

## ライブラリ其の1。静的にビルド
include $(CLEAR_VARS)

LOCAL_MODULE    := libtwolib-first
LOCAL_SRC_FILES := first.c

include $(BUILD_STATIC_LIBRARY)

## ライブラリ其の2。其の1に依存し、其の1をincludeする
include $(CLEAR_VARS)

LOCAL_MODULE    := libtwolib-second
LOCAL_SRC_FILES := second.c

LOCAL_STATIC_LIBRARIES := libtwolib-first

include $(BUILD_SHARED_LIBRARY)

sources/samples/two-libs/first.c:

#include "first.h"

int first(int x, int y)
{
    return x + y;
}

sources/samples/two-libs/first.h:

extern int first(int  x, int  y);

sources/samples/two-libs/second.c:

#include "first.h"
#include <jni.h>

jint Java_com_example_twolibs_TwoLibs_add( JNIEnv* env, jobject this, jint x, jint y )
{
    return first(x, y);
}

apps/two-libs/Application.mk:

APP_PROJECT_PATH := $(call my-dir)/project
APP_MODULES      := twolib-first twolib-second

apps/two-libs/project/src/com/example/twolibs/TwoLibs.java:

package com.example.twolibs;

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

public class TwoLibs extends Activity
{
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);

        TextView  tv = new TextView(this);
        int       x  = 1000;
        int       y  = 42;

        // ライブラリを実行時ネイティブメソッドの呼び出し前に動的にロード
        System.loadLibrary("twolib-second");

        int  z = add(x, y);

        tv.setText("The sum of "+ x +" and "+ y +" is "+ z);
        setContentView(tv);
    }

    public native int add(int x, int y);
}

http://img.f.hatena.ne.jp/images/fotolife/n/n4_t/20090702/20090702024610.png


NDK development in practice

00:10 | NDK development in practice - naoya_t@android を含むブックマーク はてなブックマーク - NDK development in practice - naoya_t@android

docs/OVERVIEW.TXT, III. NDK development in practice

1. Configuring the NDK:

  • NDKをインストールしたら build/host-setup.sh スクリプトでNDKを設定
  • ホストシステムとか必要条件とか調べるやつです
  • NDKでのビルド中に使われる out/host/config-host.mk などのコンフィギュレーションファイルを生成します
  • 途中で、あなたの開発プラットフォーム用のビルド済みツールチェインバイナリををダウンロードするよう指示される場合がある
  • このステップを忘れると、NDKはビルド試行に対しエラーを返す

2. C/C++ソースを配置

NDKのビルドシステムは、トップレベルの 'sources' ディレクトリにあなたのソースがあることを期待する。

まず sources の下に

    $NDK/sources/<mysrc>/

的ディレクトリを掘るべし。

sources の中身は好きなようにしていい。ディレクトリ名とその構造は最終的に生成されるアプリケーションパッケージに影響を与えないので、アプリケーションパッケージ名でやるような com.<mycompany>.<myproject> のようなユニークな名前を使ったりとか気にしなくていい。

NDKの sources/samples 以下にサンプルモジュールをいくつか入れてあるよ。

C と C++ がサポートされている。NDKがサポートする C++ ファイルのデフォルト拡張子は '.cpp' だが、他の拡張子でも行けるお(詳しくは docs/ANDROID-MK.TXT 参照のこと)

$NDK/sources/<mysrc> からシンボリックリンクをはっておけば、個々のソースを別の場所に置くこともできる。

あなたのソースファイルやビルドスクリプトを、NDKビルドシステムが $NDK/sources から「見える」ようにしておけばよい。

3. ビルドスクリプト Android.mk を書く

Android.mk ファイルは、NDKビルドシステムにあなたのソースについて知らせるための小さなビルドスクリプト。文法の詳細は docs/ANDROID-MK.TXT 参照。

NDKはあなたのソースを「モジュール」にまとめる。各モジュールは

  • 静的ライブラリ
  • 共有ライブラリ

のいずれかになる。1つの Android.mk の中で複数のモジュールを定義してもよいし、 モジュールを1つ1つ定義する Android.mk ファイルを複数書いてもよい。

すべての Android.mk ファイルは、ビルドシステムによってビルド前に読み込まれる。各 Android.mk は何度も読まれるので、ある変数が定義されていないこととか仮定しないように。

デフォルトで、NDKは $NDK/sources/*/Android.mk にマッチするすべてのファイルを探索する。

Android.mk ファイルをサブディレクトリで定義したい場合、サブディレクトリに置いたものをトップレベルの Android.mk から明示的にincludeすべし。これを行うヘルパ関数もあって、

   include $(call all-subdir-makefiles)

のように使える。これは、カレントビルドファイルのパスのサブディレクトリにある全ての Android.mk ファイルをincludeする。

4. ビルドファイル Application.mk を書く

Android.mk ファイルではモジュールを記述するが、アプリケーションや、アプリに必要なモジュールの記述は $NDK/apps/<myapp>/Application.mk ファイルで行う。ここで <myapp> はあなたのアプリケーションの短い名前で、NDKビルドを呼び出すために使われる。(APKにはこの名前は入らない)

このファイルは以下の情報をNDKビルドに提供する:

  • あなたのAndroidプロジェクトのパス位置
  • あなたのアプリが必要とするNDKモジュールリスト。これは「共有ライブラリ」モジュールのリストであるべし。
  • リリースビルドなのかデバッグビルドなのかとか、具体的なC/C++コンパイラフラグとか、のようなオプショナルな情報
  • (これは計画中。現時点では1つしかサポートしない)明示的にターゲットとする具体的なプラットフォーム/CPUのリスト

Application.mk の文法はとても単純。docs/APPLICATION-MK.TXT 参照。

同じアプリの異なるビルドに対応し、複数の Application.mk を定義することができる:

  $NDK/apps/release/Application.mk
  $NDK/apps/debug/Application.mk

5. NDKビルドシステムを起動

コマンドラインで、NDKのトップレベルディレクトリに移動し、ビルドシステムを呼び出す:

   make APP=<myapp>

ここで 'make' はGNU makeで、<myapp> は '$NDK/apps/' のサブディレクトリ名のうちの1つ。

これはすべてのモジュールとApplication.mk にて列挙されている共有ライブラリをビルドしようとする。

成功した場合、共有ライブラリをstripしたものをアプリケーションプロジェクトパスにコピーする。

(stripされていない版はデバッグ目的に保存されるが、デバイスにこれをコピーする必要はない。)

NDKの文書

00:45 | NDKの文書 - naoya_t@android を含むブックマーク はてなブックマーク - NDKの文書 - naoya_t@android

http://developer.android.com/sdk/ndk/1.5_r1/index.html

の方が見やすいし分かりやすかった

Android NDK Overview

22:48 | Android NDK Overview - naoya_t@android を含むブックマーク はてなブックマーク - Android NDK Overview - naoya_t@android

docs/OVERVIEW.TXT

イントロダクション

【重要事項】 NDKは Cupcake (1.5) 以降のプラットフォームにしか対応していないんだからね

I. Android NDKのゴール

Android VMでは、ネイティブコードで実装されたメソッドをJNI経由で自分のアプリから呼び出すことができる

  • メソッドは、ネイティブコード経由で実装されていることを示す 'native'キーワードで宣言される:
      native byte[]  loadFile(String  filePath);

こうしたメソッドの実装を含むネイティブ共有ライブラリを提供する。(アプリの .apk にパッケージする)

標準的なunixの慣例に従って lib<ほにゃらら>.so という名前で。

標準JNIエントリポイント(後述)を含む。

  • ライブラリは明示的に呼び出さないといけない。アプリ起動時に libFileLoader.so をロードするなら
      static {
        System.loadLibrary("FileLoader");
      }

とかソースコードに付け足す。(※static {} って何それ)

Android NDKはAndroid SDKを補完するものである。

  • ARM CPU上のAndroid 1.5以降のプラットフォーム上で動作するJNI互換の共有ライブラリを生成する
  • 生成した共有ライブラリを、アプリケーションのプロジェクトパスの適切な場所にコピーする(ので自動的に .apks に入る)
  • NDKではそのうちリモートgdb接続経由でネイティブコードをデバッグできるツールとか用意しちゃうかもしれないもんね

さらに

  • ネイティブARMバイナリを生成するためのクロスツールチェイン(コンパイラ、リンカ等等)一式
  • Androidプラットフォームがサポートしている、安定しているネイティブAPIのリストに対応するシステムヘッダ一式
  • これは今後のリリースでもサポートされることが保証されていることを意味する

【重要事項】Androidシステムイメージに含まれるネイティブシステムライブラリの大半はまだ固まっておらず、今後のリリースで大幅に変更されたりとか削除されたりさえするかもしれない点を留意されたし

  • ビルドファイルの記述が短くすむ仕組み

II. Android NDKのゴールでないこと

NDKはAndroidデバイスで動くジェネリックなネイティブコードを書くのに良い方法ではない。

"Application Not Responding" ダイアログを回避したりとか、アプリケーションのライフサイクルを処理したりとかするために、アプリはJavaで書かないとだめ。

あ、でも、洗練されたアプリケーションをネイティブコードで書いて、適切に起動/終了させるための「アプリケーション・ラッパー」を付けるのは有り。

JNIをよく理解していたほうがよい(JNIわかりません…オワタ><…あ、でもFFIみたいな事か)

この環境での操作の多くには、開発者からの特定のアクションが求められる:

  • ダイレクトネイティブポインタ経由でのVMオブジェクト直指しができない。例えば、文字列オブジェクトの16ビットchar配列へのポインタを安全に得て、ループでiterateするとか無理ですから
  • ネイティブコードがJNI呼び出し間でVMオブジェクトへのハンドルを保持したい場合、明示的な参照管理が必要

III. NDK development in practice

Android NDKでネイティブコードを開発する大まかな手順はこんな感じ:

  1. build/host-setup.sh を実行し、NDKのコンフィギュレーション
  2. ソースは sources/<mysrc>/ 以下に
  3. sources/<mysrc>/Android.mk で、ソースをビルドシステムに指示する
  4. apps/<myapp>/Application.mk でアプリケーションと、アプリが必要とするネイティブソースをNDKのビルドシステムに指示する
  5. NDKのトップレベルディレクトリで make APP=<myapp> を実行し、ネイティブコードをビルドする

※詳細はこの次(上)のエントリに

IV. Debugging support

今回のNDKリリースでは、ネイティブコードのデバッグについては大雑把なまま…そのうち良くなるから

NenaNena2012/06/06 14:52Surpriisgnly well-written and informative for a free online article.

gyeiaqkqggyeiaqkqg2012/06/07 05:55S8wjdc <a href="http://mremafnqxmjv.com/">mremafnqxmjv</a>

naadipjinaadipji2012/06/07 09:523ShT6q , [url=http://sctncahwoijj.com/]sctncahwoijj[/url], [link=http://wtrzppaaanne.com/]wtrzppaaanne[/link], http://qrizvtrokevw.com/

srfhuxnfsrfhuxnf2012/06/07 15:55CZ4Ooc <a href="http://krtfugzereuz.com/">krtfugzereuz</a>

skpyzywkuloskpyzywkulo2012/06/13 06:28NIb8FT , [url=http://mskkplzktjpn.com/]mskkplzktjpn[/url], [link=http://zbmapvwnsrxh.com/]zbmapvwnsrxh[/link], http://qfidfqskjxyt.com/

トラックバック - http://android.g.hatena.ne.jp/n4_t/20090701
 |