我的目标是在C代码中实现无限循环,该循环执行将String传递给Java函数的回调。然后,Java函数使用String更新文本框的内容。问题是回调尝试生成以下错误:

JNI DETECTED ERROR IN APPLICATION: can't call void com.example.helloneon.HelloNeon.updateSign(java.lang.String) on instance of java.lang.Class<com.example.helloneon.HelloNeon>


C代码:

/* return current time in milliseconds */
static double
now_ms(void)
{
    struct timespec res;
    clock_gettime(CLOCK_REALTIME, &res);
    return 1000.0*res.tv_sec + (double)res.tv_nsec/1e6;
}
jstring  Java_com_example_helloneon_HelloNeon_stringFromJNI( JNIEnv* env,
                                                    jobject thiz ) {
    int seconsaperline = 3;
    static double t0 = 0;
    if (t0 == 0) { t0 = now_ms(); }
    //jclass cls = (*env)->FindClass(env, "com/example/helloneon/HelloNeon");
    jclass cls = (*env)->GetObjectClass(env,thiz) ;

    jmethodID timerId2 = (*env)->GetMethodID(env, cls,
                                             "updateSign", "(Ljava/lang/String;)V");
    jstring jstr = (*env)->NewStringUTF(env, "line nnnn");

    while (true) {
        if (((now_ms() - t0) * .001) >= seconsaperline) {
            t0 = now_ms();
        }
        //generates:
        // JNI DETECTED ERROR IN APPLICATION: can't call void com.example.helloneon.HelloNeon.updateSign(java.lang.String) on instance of java.lang.Class<com.example.helloneon.HelloNeon>
        (*env)->CallVoidMethod(env, cls, timerId2, jstr);
    }
    //return jstr;

}


Java代码:

public class HelloNeon extends AppCompatActivity {

    TextView tv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_hello_neon);

        tv = findViewById(R.id.text_view_hello_neon);
        /*
        ((TextView)findViewById(R.id.text_view_hello_neon))
                .setText(stringFromJNI());  */

        new doWork().execute("");
    }


    /*called from c code*/
    @Keep
    public  void updateSign(final String  line) {

        runOnUiThread(new Runnable() {
            @Override
            public void run() {

                HelloNeon.this.tv.setText(line);
            }
        });

    }
    public native String stringFromJNI();



    public class doWork extends AsyncTask<String, Void, String[]> {

        @Override
        protected void onPreExecute() {
        }

        @Override
        protected String[] doInBackground(String... params) {


            while (true) {

                stringFromJNI();

            }


            // return null;

        }

        @Override
        protected void onPostExecute(String[] passedData) {
        }

    }

    static {
        System.loadLibrary("hello-neon");
    }
}


在此精简版本中,我希望文本框继续显示相同的字符串,因为每个string都传递了相同的callback。而是appcrashing。堆栈跟踪给出以下错误

2019-09-05 20:42:10.330 8764-8791/com.example.helloneon A/ample.helloneo: runtime.cc:566] JNI DETECTED ERROR IN APPLICATION: can't call void com.example.helloneon.HelloNeon.updateSign(java.lang.String) on instance of java.lang.Class<com.example.helloneon.HelloNeon>
2019-09-05 20:42:10.330 8764-8791/com.example.helloneon A/ample.helloneo: runtime.cc:566]     in call to CallVoidMethod
2019-09-05 20:42:10.330 8764-8791/com.example.helloneon A/ample.helloneo: runtime.cc:566]     from java.lang.String com.example.helloneon.HelloNeon.stringFromJNI()


编辑:
根据我替换的C代码中的建议

jmethodID timerId2 = (*env)->GetMethodID(env, cls,
                                                 "updateSign", "(Ljava/lang/String;)V");




jmethodID timerId2 = (*env)->GetMethodID(env, thiz,
                                                 "updateSign", "(Ljava/lang/String;)V");


然后用

jmethodID timerId2 = (*env)->GetMethodID(env, (jclass) thiz,
                                             "updateSign", "(Ljava/lang/String;)V");


两种变化都产生以下错误

JNI检测到应用程序错误:jclass类型错误:com.example.helloneon.HelloNeon
2019-09-06 17:31:42.752 9787-9826 / com.example.helloneon A / ample.helloneo:runtime.cc:566]调用GetMethodID
2019-09-06 17:31:42.752 9787-9826 / com.example.helloneon A / ample.helloneo:runtime.cc:566]来自java.lang.String com.example.helloneon.HelloNeon.stringFromJNI()

最佳答案

我找到了可行的解决方案。以下是功能性的C代码

jstring  Java_com_example_helloneon_HelloNeon_stringFromJNI( JNIEnv* env,
                                                             jobject thiz ) {

    int seconsaperline = 3;
    static double t0 = 0;
    if (t0 == 0) { t0 = now_ms(); }

    //originally incorrectly used just this in place of next 2
    jclass clz = (*env)->GetObjectClass(env, thiz);

    //fix added following line, passing jniHelperClz to GetMethodID:
    jclass jniHelperClz = (*env)->NewGlobalRef(env, clz);

    //fix added following line, passing mainActivityObj to CallVoidMethod:
    jclass mainActivityObj = (*env)->NewGlobalRef(env, thiz);

    jmethodID timerId2 = (*env)->GetMethodID(env, jniHelperClz,
                                             "updateSign", "(Ljava/lang/String;)V");


    jstring jstr = (*env)->NewStringUTF(env, "line nnnn");
    while (true) {
        if (((now_ms() - t0) * .001) >= seconsaperline) {
            t0 = now_ms();
        }

         (*env)->CallVoidMethod(env, mainActivityObj, timerId2, jstr);
    }
    //return jstr;

}

09-27 23:20