【问题标题】:How to call a Java function via JNI from a signal handler function in Android如何通过 JNI 从 Android 中的信号处理函数调用 Java 函数
【发布时间】:2013-05-13 14:09:48
【问题描述】:

我的目标是从内核向运行在用户空间中的 Android 服务发送信号。收到此信号后,服务应向内核进行 IOCTL 回调。通过这个 ioctl 调用从内核获取数据后,它必须显示给用户。为此,我从我的 Java 服务中调用了一个本地方法,该方法注册了 sigaction 结构,其中包括该信号的处理函数。此处理函数将进行 IOCTL 调用并调用 Java 函数以将字符串传递给 Java 服务。

这里是signal.java类

public class signal {
static{
    System.loadLibrary("signal");
}
public native String hello();

public String messageMe(String s)
{
    if(null != MainActivity.mUiHandler)
    {
        Message msgToActivity = new Message();
        msgToActivity.what = 0;

            msgToActivity.obj  = s; // you can put extra message here

        MainActivity.mUiHandler.sendMessage(msgToActivity);
    }

    System.out.println(s);
        return s;
}

}

我需要从信号处理程序中调用这个“messageMe”函数。 这是注册 sigaction 结构的本机 hello() 函数。

JNIEXPORT jstring JNICALL Java_com_example_service_signal_hello
(JNIEnv * env, jobject obj) {

 int configfd;
char buf[10];
/* setup the signal handler for SIG_TEST
 * SA_SIGINFO -> we want the signal handler function with 3 arguments
 */
struct sigaction sig;
sig.sa_sigaction = receiveData;
sig.sa_flags = SA_SIGINFO;
sigaction(SIG_TEST, &sig,NULL);
}

receiveData 是处理函数。就这样吧。

void receiveData(int n, siginfo_t *info,void* unused) {
    char buf[200];
    char *msg = "hello world";
    int fd = -1;
    if ((fd = open("/dev/my_device", O_RDWR)) < 0) {
            perror("on");
            return;
    }
    if(ioctl(fd, READ_IOCTL, buf) < 0)
            perror("second ioctl");

     jstring jstr = (*env)->NewStringUTF(env, buf);
     jclass *clazz = (*env)->GetObjectClass(env, obj);
     jmethodID messageMe = (*env)->GetMethodID(env,clazz, "messageMe", "(Ljava/lang/String;)Ljava/lang/String;");
     jobject result = (*env)->CallObjectMethod(env,obj, messageMe, jstr);

    printf("message: %s\n", buf);

} 这个处理函数应该调用 Java 函数“messageMe”并将它从内核读取的字符串作为参数传递。但它没有环境变量 JNIEnv * 和 jobject obj。当我在 hello() 函数中维护指向这些变量的全局指针并在这里使用它们时,它给出了 NoClassDefFoundError。 现在如何将字符串从这个信号处理程序发送到 Java 函数?

【问题讨论】:

  • 虽然您的问题不是重复,但stackoverflow.com/questions/1083154/… 的最佳答案中引用了一个示例实现
  • 我尝试缓存 JavaVM 指针并在处理程序中获取 JNIEnv 变量。但是 getMethodID 函数给出了一个挂起的异常 NoClassDefFoundError 并且 VM 正在中止。当我在原始本机函数中执行此操作时,不会引发异常。这是为什么呢?

标签: java android android-ndk java-native-interface


【解决方案1】:

我有以下全局变量。我缓存了这些对象以备将来使用。

JavaVM* vm;
static jclass cl;
static jobject ob;

在 hello 函数中,我对上述变量进行了全局引用。

JNIEXPORT jstring JNICALL Java_com_example_service_signal_hello
(JNIEnv * env, jobject obj) {
(*env)->GetJavaVM(env,&vm);
ob = (*env)->NewGlobalRef(env,obj);
jclass clazz = (*env)->FindClass(env,"com/example/service/signal");
cl = (*env)->NewGlobalRef(env,clazz);
}

现在我在处理函数中使用了这些

void receiveData(int n, siginfo_t *info,void* unused) {
//other code
JNIEnv* env = NULL;
    if(vm==NULL)
        return;
    (*vm)->AttachCurrentThread(vm,&env,0);
 jmethodID messageMe = (*env)->GetStaticMethodID(env,cl, "messageMe", "(Ljava/lang/String;)Ljava/lang/String;");
jobject result = (*env)->CallStaticObjectMethod(env,cl, messageMe, jstr);
}

我做的一个改变是使用静态方法而不是普通方法。所以我不必使用该类的特定实例。如果方法不是静态的,可以在CallObjectMethod中使用全局变量“ob”。

【讨论】:

  • 在信号处理程序中尽可能少做通常是个好主意,因为您不知道信号到达时当前线程处于什么状态。 Dalvik VM 通过使用sigprocmask 阻塞某些信号并在sigwait 中拥有一个专用的信号捕获线程来处理它想要捕获的信号。 (请参阅android.googlesource.com/platform/dalvik/+/master/vm/. 中的 Init.cpp 和 SignalCatcher.cpp)如果您可以使用信号定位特定线程,则无需在所有其他线程上阻塞它。
【解决方案2】:

据我所知,GetObjectClass 方法返回 jclass,而不是 jclass *

在你的receiveData 函数中,你应该替换

jclass *clazz = (*env)->GetObjectClass(env, obj);

通过

jclass clazz = (*env)->GetObjectClass(env, obj);

【讨论】:

  • 是的,我后来意识到了。但这并没有解决问题。我终于弄清楚了问题所在。我会发布一个答案。
猜你喜欢
  • 2022-01-24
  • 1970-01-01
  • 1970-01-01
  • 2012-05-10
  • 1970-01-01
  • 1970-01-01
  • 2023-04-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多