TLDR:JavaメソッドをCから呼び出すとNPEを取得しますが、原因として飛び出すことはありません。有効なポインタを使用してCからメソッドを呼び出した後のJava JNI NullPointerException
私はシステムコールpoll
(添付のデバイスからの通知を得るため)を呼び出すためにLinux上でいくつかのJNIコードを書いています。
ほとんどの場合、かなりうまく動作しています。私のCのメソッドが呼び出されている、私はJavaから文字列を取得しているポインタを返します(int
...私は知っているので、私を訴えて!)、そして他のメソッドに正常に渡します。これを確認するには多くのprintf
があります。
下のCメソッドJava_NativePoller_poll
に問題が発生しています。 CallVoidMethod
の行は、printf
の後に続くprintf
のように動作しますが、はと呼ばれますが、コールしようとしているJavaメソッドが呼び出されずにNullPointerException
がスローされます。
public class NativePoller {
public interface NativePollEventHandler {
void handleEvent();
}
/* Opens a file to prepare to poll its status */
public native int watchFile(String fileName, NativePollEventHandler handler);
/* Poll a file previously opened */
public synchronized native void poll(int fd);
/* Clean up */
public native void stopWatching(int fd);
}
、問題のCコード:
は、ここでのJavaコードです
struct Poller {
struct pollfd fd;
jobject handler;
jclass objclass;
jmethodID method;
};
int wasEx(JNIEnv* env) {
jthrowable ex = (*env)->ExceptionOccurred(env);
if (ex) {
printf("Got an exception!");
(*env)->ExceptionDescribe(env);
(*env)->ExceptionClear(env);
return 1;
}
return 0;
}
JNIEXPORT jint JNICALL Java_NativePoller_watchFile
(JNIEnv* env, jobject nativePoller, jstring fileName, jobject handler) {
(*env)->ExceptionClear(env);
const char* file = (*env)->GetStringUTFChars(env, fileName, NULL);
if (wasEx(env)) return -1;
int fd = open(file, O_RDONLY);
if (fd < 0) {
fprintf(stderr, "Failed to open %s for reading (errno=%d)\n", file, errno);
(*env)->ReleaseStringUTFChars(env, fileName, file);
if (wasEx(env)) return -1;
return fd;
}
(*env)->ReleaseStringUTFChars(env, fileName, file);
if (wasEx(env)) return -1;
struct Poller* poller = malloc(sizeof(struct Poller));
poller->fd.fd = fd;
poller->fd.events = POLLIN;
poller->handler = handler;
jclass objclass = (*env)->GetObjectClass(env, handler);
if (wasEx(env)) return -1;
jmethodID method = (*env)->GetMethodID(env, objclass, "handleEvent", "()V");
if (wasEx(env)) return -1;
printf("Found method %p in class %p\n", method, objclass);
poller->objclass = objclass;
poller->method = method;
printf("Returning poller %p which has descriptor %d\n", poller, poller->fd.fd);
return (int)poller;
}
JNIEXPORT void JNICALL Java_NativePoller_poll
(JNIEnv* env, jobject nativePoller, jint pollerAddress) {
struct Poller* poller = (struct Poller*)pollerAddress;
printf("Polling for %p (%d)\n", poller, poller->fd.fd);
int ret = poll(&poller->fd, 1, 1);
if (ret > 0) {
printf("Got something! Events is %08X", poller->fd.events);
if (poller->fd.events & POLLIN) {
if (poller->method != 0) {
printf("Calling: CallVoidMethod(%p, %p, %p)...\n", env, poller->handler, poller->method);
(*env)->ExceptionClear(env);
(*env)->CallVoidMethod(env, poller->handler, poller->method);
if (wasEx(env)) return;
printf("Called.\n");
}
}
}
}
JNIEXPORT void JNICALL Java_NativePoller_stopWatching
(JNIEnv* env, jobject nativePoller, jint fileDescriptor) {
struct Poller* poller = (struct Poller*)fileDescriptor;
close(poller->fd.fd);
free(poller);
}
(申し訳ありませんが、コードがスーパークリーンではありません - 私はまだやってリファクタリングのビットを持っています。)
ここでそれを呼び出しているコードです:
watchFd = nativePoller.watchFile(ROOT_PATH + pinPath + "value", this::triggerEvent);
//...
private void triggerEvent() {
LOG.info("Event triggered!");
}
すべての私のprintf
出力は次のようになります。ここで、このNPEから来ています
Found method 0x63d03b48 in class 0x63d02860
Returning poller 0x63d036a8 which has descriptor 18
Polling for 0x63d036a8 (18)
Got something! Events is 00000001
Calling: CallVoidMethod(0x63d03d3c, 0x630d0aa8, 0x63d03b48)...
Got an exception!
Exception in thread "Thread-12" java.lang.NullPointerException
at my.package.NativePoller.poll(Native Method)
at my.package.GPIOPinImpl.run(GPIOPinImpl.java:116)
at java.lang.Thread.run(Thread.java:745)
?
ありがとうございます!
_ "次のprintfは" _ "と呼ばれるので、CallVoidMethodを実行する行は機能しているようです。コントロールがJavaに戻るまで例外が保留されるため、ネイティブメソッドが返されるまでNPEが表示されませんでした。コードのエラーチェックはほとんどありません。実際には、NULLを返すはずのないすべてのJNI呼び出しがNULLを返さず、例外が発生する可能性のある各JNI呼び出しの 'ExceptionCheck'も呼び出していることを確認する必要があります。 – Michael
ありがとう@マイケル。私は問題の呼び出しの周りのCで例外チェックを追加するように編集したので、C言語では "処理"されています。私が呼び出す直前に関数を呼び出している正確な引数も表示しています。私は 'CallVoidMethod'に' NULL'を渡していません。 – tyrel
さて、オブジェクトとメソッドはあなたが ''何かを見つけました! ''と' 'Calling:CallVoidMethod" 'で一致しないので、調べなければならないものです。最近多くのコンピュータが64ビットポインタを使用しているので、 'jint'の代わりに' Poller * 'に' jlong'を使うべきでしょう。そして、私は、 'Poller *'なんかに 'fileDescriptor'という名前を付けるのがちょっと奇妙だと分かりました。 – Michael