调用C函数时Toast不显示

时间:2016-06-17 17:16:44

标签: java android c java-native-interface toast

我有一个服务,既可以使用启动器启动,也可以在启动时运行。问题是当我用JNI调用C函数时,不再显示所有Toast通知,而如果我只调用java方法,则Toast通知显示正常。是什么导致这种情况以及如何解决?

注意:的 如果C函数执行简单的操作(如回调到java并终止),则会显示Toast通知。但是,我的函数有一个用于处理中断的无限循环。

ServiceLauncher

public class ServiceLauncher extends AppCompatActivity  {
    @Override
    public void onCreate(Bundle savedInstanceState)  {
        Toast.makeText(getBaseContext(), "onCreate", Toast.LENGTH_LONG).show();
        super.onCreate(savedInstanceState);
        startService(new Intent(this, MyService.class));
        finish();
    }
}

MyService

public class MyService extends Service  {
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
    public void onDestroy() {
        Toast.makeText(this, "Terminated", Toast.LENGTH_LONG).show();
    }

    @Override
    public int onStartCommand(Intent intent,int flags, int startid)  {
        Toast.makeText(this, "Running", Toast.LENGTH_LONG).show();
        javaMethod(); /*toast displays if this is called*/
        cMethodFromJNI(); /*toast does not display if this is called*/
        return START_STICKY;
    }
}

C代码结构

JNIEXPORT jint JNICALL
Java_className_cMethodFromJNI(JNIEnv *env, jclass type) {


    jmethodID mid = (*env)->GetStaticMethodID(env, type, "javaMethod", "(I)V");

   // open and read some fd (not shown)

    for (;;) {
        // wait on change in fd and act on it (not shown)
        callJavaAgain(env,type,mid)
        }
    }
   //close fd (not shown)
    exit(0);
}

编辑:我按照以下答案更改了代码。似乎情况与以前完全一样,也许我错过了一些东西。

修订C代码结构

JNIEXPORT jint JNICALL

Java_className_cMethodFromJNI(JNIEnv * env, jclass type) {

    /*cache JVM*/
    int status = ( * env) - > GetJavaVM(env, & jvm);
    if (status != 0) {
        LOGD("failed to retrieve *env");
        exit(1);
    }
    attachedThread();
}

void attachedThread() {
        /* get a new environment and attach a new thread */
        JNIEnv * newEnv;
        JavaVMAttachArgs args;
        args.version = JNI_VERSION_1_6; // choose your JNI version
        args.name = NULL; // if you want to give the java thread a name
        args.group = NULL; // you can assign the java thread to a ThreadGroup
        ( * jvm) - > AttachCurrentThread(jvm, & newEnv, & args);
        jclass cls = ( * newEnv) - > FindClass(newEnv, "fully/qualified/class/name");
        jmethodID mid = ( * newEnv) - > GetStaticMethodID(newEnv, cls, "callJavaAgain", "(V)V");
        // open and read some fd (not shown)

        for (;;) {
            // wait on change in fd and act on it (not shown)
            intermediaryFunction(newEnv, cls, mid);
        }
    }
        //close fd (not shown)
    exit(0);
}

void intermediaryFunction(JNIEnv * newEnv, jclass cls, jmethodID mid) {
    //do some stuff (not shown)
    ( * newEnv) - > CallStaticVoidMethod(newEnv, cls, mid);
}

1 个答案:

答案 0 :(得分:1)

好的,所以这个话题需要一些注意。 Android docs讨论JavaVM和JNIEnv以及如何与它们进行交互。您需要做的是存储可从C代码中的所有线程访问的JavaVM的全局引用。 JVM允许线程访问JNIEnv,这可以让您访问类的方法,而不是。下一个关键问题是你需要存储一个工作指针'你从C代码(你可能已经知道)回调的android对象。确保在获取JNIEnv引用的线程上调用JNIEnv-> DetachThread

再次:

  • 获取JavaVM引用并将其存储在c代码中
  • 有java调用c代码来存储回调的jobject引用
  • 启动一个线程进行循环,获取对该线程上的JNIEnv的引用以调用java
  • 在终止线程执行之前调用DetachThread

另一个麻烦的问题是从java代码访问c ++对象,这里有一个巧妙的技巧,你可以使用c ++代码创建 java对象,并分配给private long _cppPointer成员在创建的Java类上,然后将此对象添加到已经指向的java对象上的某个集合(如活动或其他内容)

祝你好运。

编辑:我终于回忆起有关JNI工作的another great source信息