今日は自宅合宿

昨日動作しなかったソレは直前エントリの通り、リソース解放したら動いた。で、今日の課題としては、C 側で static な領域を引き継ぐ事ができるかどうかの実験。

プロジェクト

もっかい一から作るか。とりあえず SDK1.6 でナニ。プロジェクトを作ったら、プロジェクトの root ($project と表記) 直下に jni なソースを投入するディレクトリを掘る。

$ mkdir jni

eclipse では refresh したら掘ったディレクトリが出てきます。で、javah でヘッダを出力するためには呼び出し元を先に作っておかないと駄目なのか。なんつーか面倒だなぁ。
とりあえず

  • 初期化
    • static な領域を初期化
  • apply
    • static なカウンタ操作とか
  • 値の取り出し
    • static な領域とカウンタを文字列にして戻すか

というあたりが i/f ってコトでナニ。先に java なソレを書く。ボタン付けたりとか面倒なのでそのままってコトで以下なカンジ。凄く横着。

package com.example.android.ndkTest2;

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

public class ndkTestActivity extends Activity {
	static {
		System.loadLibrary("HelloNDK");
	}
	private native int xx_init();
	private native int xx_apply();
	private native String xx_ret();

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        int ret = xx_init();
        if(0 != ret) {
        	setContentView(R.layout.main);
        }
        
        ret = xx_apply();
        if(0 != ret) {
        	setContentView(R.layout.main);
        }
        
        TextView textView = new TextView(this);
        textView.setText(xx_ret());
        setContentView(textView);
    }
}

で、これを保存しといて javah して中身を確認。

$ javah com.example.android.ndkTest2.ndkTestActivity
$ cat com.example.android.nddkTest2_ndkTestActivity.h 
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_example_android_ndkTest2_ndkTestActivity */

#ifndef _Included_com_example_android_ndkTest2_ndkTestActivity
#define _Included_com_example_android_ndkTest2_ndkTestActivity
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_example_android_ndkTest2_ndkTestActivity
 * Method:    xx_init
 * Signature: ()I
 */
JNIEXPORT jint JNICALL Java_com_example_android_ndkTest2_ndkTestActivity_xx_1init
  (JNIEnv *, jobject);

/*
 * Class:     com_example_android_ndkTest2_ndkTestActivity
 * Method:    xx_apply
 * Signature: ()I
 */
JNIEXPORT jint JNICALL Java_com_example_android_ndkTest2_ndkTestActivity_xx_1apply
  (JNIEnv *, jobject);

/*
 * Class:     com_example_android_ndkTest2_ndkTestActivity
 * Method:    xx_ret
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_example_android_ndkTest2_ndkTestActivity_xx_1ret
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif
$

で、これを元にソースをヒリ出してみる。jni 配下に HelloNDK.c を作成。以下?

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_example_android_ndkTest2_ndkTestActivity */

#ifndef _Included_com_example_android_ndkTest2_ndkTestActivity
#define _Included_com_example_android_ndkTest2_ndkTestActivity
#ifdef __cplusplus
extern "C" {
#endif

	static char [255] stringProperty;
	static int intProperty;

/*
 * Class:     com_example_android_ndkTest2_ndkTestActivity
 * Method:    xx_init
 * Signature: ()I
 */
JNIEXPORT jint JNICALL Java_com_example_android_ndkTest2_ndkTestActivity_xx_1init
  (JNIEnv *env, jobject this)
{
	memset(stringProperty, '\0', sizeof(property));
	intProperty = 0;
	return (jint)0;
}

/*
 * Class:     com_example_android_ndkTest2_ndkTestActivity
 * Method:    xx_apply
 * Signature: ()I
 */
JNIEXPORT jint JNICALL Java_com_example_android_ndkTest2_ndkTestActivity_xx_1apply
  (JNIEnv *env, jobject this)
{
	intProperty++;
	strcpy(stringProperty, "hello ndkTest");

	return (jint)0;
}

/*
 * Class:     com_example_android_ndkTest2_ndkTestActivity
 * Method:    xx_ret
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_example_android_ndkTest2_ndkTestActivity_xx_1ret
  (JNIEnv *env, jobject this)
{
	return (* env)->NewStringUTF(env, stringProperty);
}

#ifdef __cplusplus
}
#endif
#endif

で、ndk の root 配下の Application.mk 作って

$ cat apps/helloNDK2/Application.mk
APP_PROJECT_PATH := /Users/hoge/Documents/workspace/ndkTest2
APP_MODULES := HelloNDK
$

$project/jni/Android.mk 作って

$ cat jni/Android.mk
   LOCAL_PATH := $(call my-dir)

   include $(CLEAR_VARS)

   LOCAL_MODULE    := HelloNDK
   LOCAL_SRC_FILES := HelloNDK.c

   include $(BUILD_SHARED_LIBRARY)
$

まずライブラリを作る、って char な配列の定義が大ウソ。正しくは以下か。

	static char stringProperty[255];

とほほ。他にも細かい文法エラーあり。真似される方は適宜修正して下さひ。コンパイル通ったのが以下。

$ make APP=helloNDK2
Android NDK: Building for application 'helloNDK2'    
Compile thumb  : HelloNDK <= /Users/hoge/Documents/workspace/ndkTest2/jni/HelloNDK.c
SharedLibrary  : libHelloNDK.so
Install        : libHelloNDK.so => /Users/hoge/Documents/workspace/ndkTest2/libs/armeabi
$

で、実行してみたんですが、.apk が install されぬ。リトライしたら動いた。がしかし出力される文字列が "こんにちわ NDK" なんですが。。。

何度かリトライしたら

動きました。メニューバーの Run から起動してみたんですが、理由は不明ッス。で、.c から戻す文字列を変更かけて make してみてプロジェクトから右クリックで Run 選んだら違うナニがまだ動きます。どうしたものやら。再度メニューバーの Run からリトライ。
動作するも出力文字列変わらず。明示的に build とかしてやんないと駄目なのか。プロジェクト選択して、Project -> Clean して再度 Run で反映されました。このあたり、ハマリ所かも。

追記

c な手続きから int の値を戻してるんですが、

	return (jint)0;

これはあまりにも乱暴すぎんか、とゆー事で戻り値をデバッガで確認。しようとしたら .apk が install されない。再起動してみようかなぁ。とりあえず一旦 Project を clean してリトライするも動作は微妙。MacBook が悪いのか使い方が微妙なのか不明。
でびあんな thinkpad でヤろうかなぁ。Run じゃなくて Debug なのかなぁ、と思いトライしてみたらようやく Installing が出力された。やれやれ。
と思ったら toggle した breakpoint で止まってないし。大体自動で Debug なパースペクティブに切り替わるはずなんですがそれも無いな。仕方が無いので以下な形にして

        if(0 != ret) {
        	Log.v(TAG, "ret = " + ret);
        }

Run してみたんですが、LogCat に何も出てない。if の中身に入ってないのかどうかも微妙なので、onCreate の先頭と末端にも Log.v でナニ。Project -> clean して Run したら、再び Installing なメセジが出ぬ。そろそろキレそうです。
リトライしたら動きましたが、ログは出力されておらず。とりあえずメシ食お。

再起動

したら LogCat に情報出力されているのが確認できた。あんま文句言いたくないけど (ry
で、LogCat 確認した所では上記の形で良い模様。よく考えたら java の int はオブジェクトじゃないんだった。

てか

キャストしなくても良いんじゃねぇか。どうもナチュラル爆発気味だなぁ。。(とほほ