JNI: c言語側からJava呼び出し。 

3,373 views
Skip to first unread message

zhangho

unread,
Oct 15, 2009, 5:14:35 AM10/15/09
to 日本Androidの会
javaのcom.ServersServiceクラスのgetIpv6を
C言語側から呼び出しでいますけど
何とか連携まではできだ感じです。(Method not found: 'getIpv6' '(I;)I' のようなエラーは既に解決済み)

問題なのはCallIntMethod()でgetIpv6関数の戻り値をファイルに出力してみたら
[99]じゃ無くで「-1091242504」とか「11212」とか
変な数値ばっかり出力されています。

getIpv6関数のLog.i()ログも吐き出していないです。
本当に呼び出していないと思いますが、原因はなんでしょうか?

よろしくお願いします。



javaのコード:
public void onCreate() {
setConnecCallback2();
}

public static native void setConnecCallback2();

public int getIpv6(int ipv6){

Log.i("JNI", "Callback from Native Messenger to Java Hello Chat");

return 99;
}

static {
System.loadLibrary("mm");
}


c側コード:
#include <string.h>
#include <jni.h>
#include <stdio.h>
#include <pthread.h>


JNIEXPORT void JNICALL Java_com_ServersService_setConnecCallback2
(JNIEnv *env, jobject this) {

FILE *fd;
    fd = fopen("/sdcard/made.log", "w");

jclass jcCallback = (*env)->FindClass(env,"com.ServersService");

jmethodID mid = (*env)->GetMethodID(env,jcCallback,"getIpv6","(I)I");

int ii = (int)(*env)->CallIntMethod(env, jcCallback ,mid, 444 );


fprintf(fd, "iii is : [%d] \n", ii);
fclose(fd);

return;
}

koba

unread,
Oct 15, 2009, 5:41:26 AM10/15/09
to 日本Androidの会
kobaです。
ちょっと見ただけですが、Java側のgetIpv6関数は呼び出されていないようにみえます。
これはインスタンスメソッドですから、C言語から呼び出すときにthis(かそれに相当するもの)を引数として渡さなければ
ならないはずです。
JNIのCallIntMethodの使い方が正しいか再度調べてみたらいかがでしょうか?
Message has been deleted

wassie

unread,
Oct 15, 2009, 9:18:11 AM10/15/09
to 日本Androidの会
CallIntMethodの前に一旦NewObjectされてみてはいかがでしょうか。

NewObjectですと関数Callされてしまい、また戻り値はとれませんが、
その後NewObjectで取得したオブジェクトをCallIntMethodに指定すれば
戻り値が取れるような気がします。

だめならCallStaticIntMethodを試されてみるとか。。。

uenokuma

unread,
Oct 15, 2009, 9:46:13 AM10/15/09
to android-g...@googlegroups.com
> javaのcom.ServersServiceクラスのgetIpv6を

これは自作のクラスですか?
見つけられなかったのですが。。
http://developer.android.com/intl/ja/search.html#q=getIpv6&t=0

> --~--~---------~--~----~------------~-------~--~----~
> このメールは次の Google グループの参加者に送られています: 日本Androidの
> 会
> このグループにメールで投稿: android-g...@googlegroups.com
> このグループから退会する: android-group-j...@googlegroups.com
> その他のオプションについては、次の URL からグループにアクセスしてくださ
> い。 http://groups.google.co.jp/group/android-group-japan?hl=ja
> -~----------~----~----~----~------~----~------~--~---
>
>

zhangho

unread,
Oct 16, 2009, 2:07:28 AM10/16/09
to 日本Androidの会
kobaさん、wassieさんありがとうございます。
この件、何とか解決しました。

インスタンスメソッドじゃ駄目でしたね。

getIpv6を static にして

GetStaticMethodID()
CallStaticIntMethod()
の二つのStatic関数でちゃんと戻り値が得るようになりました。

mizmit1222

unread,
Oct 16, 2009, 3:29:37 AM10/16/09
to 日本Androidの会
みずの@組み込みWGです。

えっと。getIpv6が何をさすのかわかりませんが、The
IPv6のことだと仮定します。

AndroidのカーネルはIPv6サポートがオフの設定です。
エミュレータでも端末でも使用できないと思いますが。

発売されている端末すべてが該当するかどうか分かり
ませんが、少なくともデフォルトのカーネル設定では
オフですし、HT-03Aのconfig.gzでもオフになってい
ます。

というか、そもそもlongじゃないとおかしいですね。

以上、ご一報まで。

# マルチポストはお控えください。


On 10月16日, 午後3:07, zhangho <cui.chang...@gmail.com> wrote:

zhangho

unread,
Oct 16, 2009, 3:53:05 AM10/16/09
to 日本Androidの会
getIpv6はただの自分で作ったもので カーネルの ipv6と関係ないです

hc.nakahara

unread,
Oct 16, 2009, 8:05:54 AM10/16/09
to 日本Androidの会
中原です。

終わった話しみたいですが、個人的に気になったのと、CallStaticIntMethod()と
CallIntMethod()はちょっと違うものなのでCallIntMethod()を使ったコードにして
みた例も記載してみます。
あくまで参考までに・・・
---
javaのコード
public void onCreate() {
setConnecCallback2(this);
}
public static native void setConnecCallback2(ServersService
me);
public int getIpv6(int ipv6){
Log.i("JNI", "Callback from Native Messenger to Java Hello
Chat");
return 99;
}
static {
System.loadLibrary("mm");
}

c側コード:
#include <string.h>
#include <jni.h>
#include <stdio.h>
#include <pthread.h>
JNIEXPORT void JNICALL Java_com_ServersService_setConnecCallback2
(JNIEnv *env, jobject this, jobject cb) {
FILE *fd;
    fd = fopen("/sdcard/made.log", "w");
jclass jcCallback = (*env)->GetObjectClass(env,cb);
jmethodID mid = (*env)->GetMethodID(env,
jcCallback,"getIpv6","(I)I");
int ii = (int)(*env)->CallIntMethod(env, cb ,mid, 444 );
fprintf(fd, "iii is : [%d] \n", ii);
fclose(fd);
return;
---
変えたところとしては
パラメータに自分のクラスを渡すようにした
ぐらいでしょうか?
もとのCallIntMethod()の第二引数がjclassを渡しているのが気になるところです。
(これで呼ばれるの?)
確かにCallStaticIntMethod()の引数はjclassですが・・・
以上、宜しくお願いします。

On 10月16日, 午後3:07, zhangho <cui.chang...@gmail.com> wrote:
Message has been deleted

zhangho

unread,
Oct 19, 2009, 2:11:17 AM10/19/09
to 日本Androidの会
中原さんの方法は既に試してみたものですが、
僕の環境ではそのServersServiceme自分のクラスがjobject cbに渡されないのが確認しました。

wassie

unread,
Oct 19, 2009, 2:46:38 AM10/19/09
to 日本Androidの会
いくつか確認を。
・前にも議論されましたが、FindClassで指定するクラスは独自クラスであることを説明したほうが良いかとおもいます。
・「アプリに死にます」では分かりません。(おそらく)dalvikのエラー文言も転載いただけませんでしょうか。

本題。
エラー内容が分からないので想像でコメントします。
--------------------------------------------
static JNIEnv gEnv;
memcpy(&gEnv, env, sizeof(env));
--------------------------------------------
JNIEnvはただのポインタではありません。
ポインタ+スレッド毎の情報も格納されるはずですので
これではenvの全情報がコピーされないのではと思います。

gEnvをポインタ型にするか、コールバックの引数にenvを渡しては
だめでしょうか?


zhangho

unread,
Oct 19, 2009, 3:35:07 AM10/19/09
to 日本Androidの会
崔です。

すみません。説明不足です。
com.f*****.s*********.s*********LighttpdServiceは独自クラスです。

まず、エラーの一部です。

10-19 16:13:22.150: INFO/DEBUG(9927): *** *** *** *** *** *** *** ***
*** *** *** *** *** *** *** ***
10-19 16:13:22.150: INFO/DEBUG(9927): Build fingerprint: 'jdd/jdd/
sapphire/sapphire:1.6/DRC83/14721:user/adp,test-keys'
10-19 16:13:22.150: INFO/DEBUG(9927): pid: 10199, tid: 10199 >>>
com.f***.s*** <<<
10-19 16:13:22.160: INFO/DEBUG(9927): signal 11 (SIGSEGV), fault addr
00000000
10-19 16:13:22.160: INFO/DEBUG(9927): r0 412a9bb0 r1 ad082c50 r2
00000000 r3 0000fe30
10-19 16:13:22.160: INFO/DEBUG(9927): r4 0000a9c8 r5 43225170 r6
80645260 r7 4104edc0
10-19 16:13:22.160: INFO/DEBUG(9927): r8 bef4f6b8 r9 4104edb8 10
4104eda8 fp 00000000
10-19 16:13:22.160: INFO/DEBUG(9927): ip ad084058 sp bef4f6a8 lr
ad041493 pc 804425fc cpsr 20000010
10-19 16:13:23.000: INFO/DEBUG(9927): #00 pc 000425fc /data/
data/com.f****.s***/lib/libmm.so
10-19 16:13:23.010: INFO/DEBUG(9927): #01 pc 0000e434 /
system/lib/libdvm.so
......
10-19 16:13:23.071: INFO/DEBUG(9927): #18 pc b000163c /
system/bin/linker
10-19 16:13:23.071: INFO/DEBUG(9927): stack:
10-19 16:13:23.071: INFO/DEBUG(9927): bef4f668 ad083e1c /system/
lib/libdvm.so
10-19 16:13:23.080: INFO/DEBUG(9927): bef4f66c ad05df61 /system/
lib/libdvm.so
10-19 16:13:23.080: INFO/DEBUG(9927): bef4f670 412a9bb0 /dev/
ashmem/dalvik-LinearAlloc (deleted)
10-19 16:13:23.080: INFO/DEBUG(9927): bef4f674 412a9bb0 /dev/
ashmem/dalvik-LinearAlloc (deleted)
10-19 16:13:23.080: INFO/DEBUG(9927): bef4f678 43225170 /dev/
ashmem/mspace/dalvik-heap/2 (deleted)
10-19 16:13:23.080: INFO/DEBUG(9927): bef4f67c ad041493 /system/
lib/libdvm.so
....

>JNIEnvはただのポインタではありません。
>ポインタ+スレッド毎の情報も格納されるはずですので
あれ、そうしますと、スレッド情報はどうすればいいですかね?

下記のようにgEnvをポインタ型しても同じエラーです。


c側コード:
#include <string.h>
#include <jni.h>
#include <stdio.h>
#include <pthread.h>

static int iscall = 0;
static JNIEnv *gEnv;
static jclass gJcCallback;
static jmethodID gMid;

void connect_cb(char *addr)
{

FILE *fd;
    fd = fopen("/sdcard/mm.log", "w");

  fprintf(fd, "iii is : [%d] \n", iscall);
fclose(fd);

    jstring *retSTR = (*gEnv)->NewStringUTF(gEnv, addr);
int ii = (int)(*gEnv)->CallStaticIntMethod(gEnv,
gJcCallback , gMid, retSTR);

}

extern void setConnecCallback(void (*cb)(char *addr));

JNIEXPORT void JNICALL
Java_com_f*****_s*********_s*********LighttpdService_setConnecCallback2
(JNIEnv *env, jobject this) {

jclass jcCallback = (*env)->FindClass
(env,"com.f*****.s*********.s*********LighttpdService");

jmethodID mid = (*env)->GetStaticMethodID
(env,jcCallback,"getIpv6","(Ljava/lang/String;)I");

    memcpy(gEnv, env, sizeof(env));
    memcpy(&gJcCallback,&jcCallback, sizeof(jcCallback));
    memcpy(&gMid,&mid,sizeof(mid));

    iscall = 1;

if(mid == 0) {
return;
}
else{
}

setConnecCallback(connect_cb);
}

wassie

unread,
Oct 19, 2009, 4:05:15 AM10/19/09
to 日本Androidの会
static JNIEnv *gEnv;

としたら

memcpy(gEnv, env, sizeof(env));

ではなく

gEnv = env;

でよいのでは^^;


また、
エラーダンプの前に
-----------------------
JNI WARNING
-----------------------
は発生しておりませんでしょうか?

zhangho

unread,
Oct 19, 2009, 4:52:01 AM10/19/09
to 日本Androidの会
>JNI WARNING
こんなエラーではないですね。

koba

unread,
Oct 19, 2009, 5:47:43 AM10/19/09
to 日本Androidの会
kobaです。
便乗させていただいて。

On 10月19日, 午後3:46, wassie <wassie0...@gmail.com> wrote:
> いくつか確認を。
> ・前にも議論されましたが、FindClassで指定するクラスは独自クラスであることを説明したほうが良いかとおもいます。
> ・「アプリに死にます」では分かりません。(おそらく)dalvikのエラー文言も転載いただけませんでしょうか。
>
> 本題。
> エラー内容が分からないので想像でコメントします。
> --------------------------------------------
> static JNIEnv gEnv;
> memcpy(&gEnv, env, sizeof(env));
> --------------------------------------------
> JNIEnvはただのポインタではありません。
> ポインタ+スレッド毎の情報も格納されるはずですので
> これではenvの全情報がコピーされないのではと思います。

これですね。
JNIEnvはVMの中でのスレッドの情報があります。

JNIEnvはそのネイティブメソッドの中でのみ有効です。
これをグローバル変数に保持しておいて、後から使おうとしてもダメです。

もし強引にこれをやればVMの内部の情報が壊れますから、この後どうなるかは予測できません。



zhangho

unread,
Oct 19, 2009, 6:16:16 AM10/19/09
to 日本Androidの会
なるほど。
そういえば、setConnecCallback(connect_cb); 関数にJNIenvパラメタを追加しても同じ結果ですね。

wassie

unread,
Oct 19, 2009, 6:18:40 AM10/19/09
to 日本Androidの会
> これですね。
> JNIEnvはVMの中でのスレッドの情報があります。
>
> JNIEnvはそのネイティブメソッドの中でのみ有効です。
> これをグローバル変数に保持しておいて、後から使おうとしてもダメです。
>
> もし強引にこれをやればVMの内部の情報が壊れますから、この後どうなるかは予測できません。

ありがとうございます。
おっしゃるとおりです。

通常、envをグローバルに持つことはしないですが、
「スレッドが変わらず、トリガはNative関数だから問題ないか。」
と思い、あえて突っ込んでいませんでした^^;

On 10月19日, 午後6:47, koba <tetsu.k...@gmail.com> wrote:

hc.nakahara

unread,
Oct 19, 2009, 6:19:48 AM10/19/09
to 日本Androidの会
クラスオブジェクトが渡ってこないんですか・・・
(一応、SDK 1.6/NDK1.6環境で動作確認してから書いたんですがorz)
もしかしてコールバックするタイミングがJNI関数呼び出しと違うタイミングとか?
渡ってくるjobject cbは参照がローカル参照なので、別の関数で使う場合などは一度
グローバル参照にしてやる必要があります。
gCb = (*env)->NewGlobalRef(env,cb);
あとはgCbを使用して、最後に開放してやるぐらいでしょうか?
(*env)-> DeleteGlobalRef(env,gCb);

ちなみにJNIEnvは自分のクラスが生きてるあいだは変わらないので
gEnv = env;
で問題ないですね
(jclassやjmethodIDなんていつでも取れるので置いとく理由がわかりませんが)

というか、Androidの話しから外れている気がしますが大丈夫なんだろうか?
(ほんとはandroid-ndkで書きたいが英語が苦手なのでこちらですいません)
ご存知かと思いますが念のため・・・
JNIに関してはこんな所の内容を参照すると良いかもしれません。
http://java.sun.com/javase/ja/6/docs/ja/technotes/guides/jni/index.html
あと、どこまで動いているか等はログで確認する事ができます。
こんな感じで
#include <android/log.h>
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG ,
"ServersServiceJNI", __VA_ARGS__)
LOGD("### JNI setConnecCallback2 Call(%p) ####", cb);
で、Android.mkに
LOCAL_LDLIBS := -llog

如何でしょうか?

zhangho

unread,
Oct 19, 2009, 7:58:50 AM10/19/09
to 日本Androidの会
あれ?GDDで問題ないのにSmartQ5で実行すると下記のようなエラーが出ますね。

10-19 20:50:20.179: WARN/dalvikvm(9651): JNI WARNING: illegal class
name 'com.f*******.sf*******.ServersService' (Check_FindClass)
。。。
10-19 20:50:30.814: ERROR/dalvikvm(9666): VM aborting
。。。
10-19 20:50:31.209: INFO/ActivityManager(600): Low Memory: No more
background processes.

zhangho

unread,
Oct 20, 2009, 9:00:34 PM10/20/09
to 日本Androidの会
> これですね。
> JNIEnvはVMの中でのスレッドの情報があります。
>
> JNIEnvはそのネイティブメソッドの中でのみ有効です。
> これをグローバル変数に保持しておいて、後から使おうとしてもダメです。
>
> もし強引にこれをやればVMの内部の情報が壊れますから、この後どうなるかは予測できません。


共有します。
この問題は解決しました。

AttachCurrentThreadで現在のスレッドを Java VM へ接続します。

ググッてみてください。

http://sdc.sun.co.jp/java/docs/j2se/1.4/ja/docs/ja/guide/jni/spec/invocation.doc.html
Reply all
Reply to author
Forward
0 new messages