繁体   English   中英

使用C ++中的NDK更新Android UI

[英]Using NDK from C++ updating Android UI

我的问题与这篇文章直接相关: https : //groups.google.com/forum/#!topic/android-ndk/291sBdkITyI

基本上,我有一个用C ++编写的应用程序,它是使用NDK和基本的Android(活动)编译的。 我有一个textview(在Java中),当c ++方面发生某些事情(例如,状态更改)时,需要对其进行更新。 我想从C ++调用Java并在状态更改时更新textview。

在上面的链接中,他们使用的代码是(可能是伪代码):

public class Example extends Activity{    

    Handler handler = new Handler() {

         @Override
         public void handleMessage(Message msg) {
             this.currentDownloadField.setText(""+ msg.what);
         }

    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        // whatever
    }   

    public static void sendMessage(int  id){ 
         handler.sendEmptyMessage(id); // just the what() is filled with the id
    }
}

从C ++调用将是

void sendMessage(char* buffer, int bufferlen) {
    JNIEnv *env = NULL;
    jmethodID mid = NULL;
    jbyteArray message;

    jint res = (jjvm->AttachCurrentThread(&jjvm, &env, NULL));

    if (res >= 0) {
        message = (*env)->NewByteArray(env, bufferlen);
        (*env)->SetByteArrayRegion(env, message, 0, bufferlen, (const jbyte *) ((BYTE*) buffer));
        mid = (*env)->GetStaticMethodID(env, jcls, "sendMessage", "([B)V");

        // Mid <= 0 ? Not found : OK
        if (mid > 0) {
        (*env)->CallStaticVoidMethod(env, jcls, mid, message);
        }
    }
}

问题是在静态函数的活动中使用“处理程序”不起作用(因为它不是静态的)。 如果它是静态的,那么如何引用“ this.currentDownloadField”?

我也尝试过仅从C ++调用公共函数

public void update(String message) {
    Log.i("logging", "Hit here")
    mMyTextField.setText(message);
}

当C ++调用Java函数“ update”时,命中日志(在logcat中),但是textview不更新。 也许这是线程的问题,但我不知道如何正确更新文本字段。

另一个选择是轮询并让Java调用C ++来读取变量(状态),但是这很乏味且不是良好的编程习惯。

有什么建议可以解决这个问题吗?

提前致谢。

没错,您必须从UI线程调用mMyTextField.setText() Android Java类提供了多种实现方法,例如View.post()Handler.post()Handler.sendMessage()Activity.runOnUiThread() 这些方法都不是静态的 ,它们都需要一个活动的对象才能工作。

有多种方法可以解决此问题。 对代码的最小更改可能如下所示:

public class Example extends Activity {

    private static Handler staticHandler;
    private TextView currentDownloadField;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Handler handler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                this.currentDownloadField.setText(""+ msg.what);
            }
        }
        staticHandler = handler;
        setContentView(R.layout.main_view);
        currentDownloadField = (TextView)findViewById(R.id.download_field);
    }

    @Override
    protected void onDestroy() {
        staticHandler = null;
    }

    public static void sendMessage(int id) {
        staticHandler.sendEmptyMessage(id); // just the what() is filled with the id
    }
}

在上面的代码中,为简洁起见,跳过了错误检查。

或者,您可以将对活动(或处理程序或downloadFiled)的引用传递给C库,而不是调用CallStaticVoidMethod() ,而是使用对象全局引用并使用CallVoidMethod(env, jactivityObj, mid, message)

PS我只是想知道,如果将jbyteArray (在Java中为byte[] )发送给需要int的回调方法,那么您希望系统如何工作?

更新上面的简单代码受Handler Leak约束,因此,如果要在生产中重用它,请在Handler周围包括一个静态内部类包装器

调用JNI C / C ++函数时,您将拥有其方法被调用的对象以及JNIEnv:

JNIEXPORT void JNICALL Java_com_xxx_Yyy_myfunc(JNIEnv *, jobject);

您可以调用该对象的非静态方法。

如果您的C ++代码没有提供在其中传递多余的void *的方法,则只需将内容存储到静态变量中,并确保仅在UI线程上使用它即可。 或者至少总是来自同一线程。

PS,您可以尝试通过线程本地存储等添加线程安全性。但是您不能控制线程的生命周期。 由于不清楚的原因,您可能会得到晦涩的错误。 因此,如果您只需要两个线程,则IMO具有两个(JNIEnv *,jobject)对象对的结构或数组。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM