简体   繁体   English

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

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

My question is related directly to this post: https://groups.google.com/forum/#!topic/android-ndk/291sBdkITyI 我的问题与这篇文章直接相关: https : //groups.google.com/forum/#!topic/android-ndk/291sBdkITyI

Basically, I have an application written in C++ compiled with the NDK with a basic Android (activity). 基本上,我有一个用C ++编写的应用程序,它是使用NDK和基本的Android(活动)编译的。 I have a textview (in Java) that needs to be updated when something happens in the c++ side (say for example a state change). 我有一个textview(在Java中),当c ++方面发生某些事情(例如,状态更改)时,需要对其进行更新。 I would like to call Java from C++ and update the textview when the state changes. 我想从C ++调用Java并在状态更改时更新textview。

In the link above, the code they used was (probably pseudo code): 在上面的链接中,他们使用的代码是(可能是伪代码):

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
    }
}

And calling from C++ would be 从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);
        }
    }
}

The problem is using "handler" in the activity from a static function does not work (because it is not static). 问题是在静态函数的活动中使用“处理程序”不起作用(因为它不是静态的)。 If it was static, then how does "this.currentDownloadField" get referenced? 如果它是静态的,那么如何引用“ this.currentDownloadField”?

I also tried just calling a public function from c++ 我也尝试过仅从C ++调用公共函数

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

When C++ calls the Java function "update", the log hits (in logcat) but the textview does not update. 当C ++调用Java函数“ update”时,命中日志(在logcat中),但是textview不更新。 Maybe it is an issue with threads but I have no idea how to update the textfield correctly. 也许这是线程的问题,但我不知道如何正确更新文本字段。

The other option is to poll and have Java call C++ to read a variable (state) however that is tedious and not good programming practice. 另一个选择是轮询并让Java调用C ++来读取变量(状态),但是这很乏味且不是良好的编程习惯。

Any suggestions to tackle this? 有什么建议可以解决这个问题吗?

Thanks in advance. 提前致谢。

You are right, you must call mMyTextField.setText() from UI thread. 没错,您必须从UI线程调用mMyTextField.setText() Android Java classes provide multiple ways to do it, eg View.post() or Handler.post() or Handler.sendMessage() , or Activity.runOnUiThread() . Android Java类提供了多种实现方法,例如View.post()Handler.post()Handler.sendMessage()Activity.runOnUiThread() None of these methods is static , they all need a live this object to work. 这些方法都不是静态的 ,它们都需要一个活动的对象才能工作。

There are different approaches to resolve this for your purpose. 有多种方法可以解决此问题。 Probably the minimal change to your code will go as follows: 对代码的最小更改可能如下所示:

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
    }
}

In the code above, error checking is skipped for brevity. 在上面的代码中,为简洁起见,跳过了错误检查。

Alternatively, you could pass the reference to your activity (or handler, or downloadFiled) to the C library, and instead of calling CallStaticVoidMethod() you would use the object global reference and use CallVoidMethod(env, jactivityObj, mid, message) . 或者,您可以将对活动(或处理程序或downloadFiled)的引用传递给C库,而不是调用CallStaticVoidMethod() ,而是使用对象全局引用并使用CallVoidMethod(env, jactivityObj, mid, message)

PS I just wonder, how you expect you system to work if you send a jbyteArray (in Java, that's byte[] ) to a callback method that expects int ? PS我只是想知道,如果将jbyteArray (在Java中为byte[] )发送给需要int的回调方法,那么您希望系统如何工作?

Update The simple code above is subject to Handler Leak , thus if you want to reuse it in production, please include a static inner class wrapper around the Handler . 更新上面的简单代码受Handler Leak约束,因此,如果要在生产中重用它,请在Handler周围包括一个静态内部类包装器

When your JNI C/C++ function is called, you have the object whose method is called, and the JNIEnv: 调用JNI C / C ++函数时,您将拥有其方法被调用的对象以及JNIEnv:

JNIEXPORT void JNICALL Java_com_xxx_Yyy_myfunc(JNIEnv *, jobject);

You can call non-static methods of that object. 您可以调用该对象的非静态方法。

If your C++ code does not provide a way to pass an extra void* inside it, just store the stuff into a static variable, and be sure to use it only on the UI thread. 如果您的C ++代码没有提供在其中传递多余的void *的方法,则只需将内容存储到静态变量中,并确保仅在UI线程上使用它即可。 Or at least always from the same thread. 或者至少总是来自同一线程。

PS you can try to add thread-safety via thread-local storage etc. But you do not control the life cycle of threads. PS,您可以尝试通过线程本地存储等添加线程安全性。但是您不能控制线程的生命周期。 You will likely get obscure errors because of unclear reasons. 由于不清楚的原因,您可能会得到晦涩的错误。 So IMO if you need just two threads, have a structure or array of two pairs of that (JNIEnv*, jobject) stuff. 因此,如果您只需要两个线程,则IMO具有两个(JNIEnv *,jobject)对象对的结构或数组。

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

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