2014-01-09 45 views
2

我的问题,直接关系到这个帖子:https://groups.google.com/forum/#!topic/android-ndk/291sBdkITyI使用由C++ NDK的Android更新UI

基本上,我有写在C编译NDK的一个基本的Android(活动)的应用程序++。我有一个文本视图(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函数“更新”,日志命中(在logcat中),但是TextView的不更新公共功能。也许这是线程的问题,但我不知道如何正确更新文本字段。

另一种选择是轮询和具有Java调用C++读取变量(状态),但是这是乏味的,而不是良好的编程习惯。

任何建议来解决这个问题?

在此先感谢。

回答

3

你是对的,你必须调用来自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回调方法?

更新上面的简单的代码是受处理程序泄漏,因此,如果你想重新使用它在生产中,请包括static inner class wrapper around the Handler

+0

这就是当我从该页面复制并粘贴时发生的情况:P – user654628

+0

对于对处理程序被泄露警告感到好奇的人,请执行以下操作:http://stackoverflow.com/questions/11278875/handlers-and-内存泄漏功能于安卓 – user654628

1

当你的JNI C/C++函数被调用,你有它的方法被调用的对象,和JNIEnv的:

JNIEXPORT void JNICALL Java_com_xxx_Yyy_myfunc(JNIEnv *, jobject); 

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

如果你的C++代码不提供一种方式来传递一个额外的void *的里面,只是东西存储到一个静态变量,并且一定要使用它仅在UI线程。或者至少总是来自同一个线程。

PS,你可以尝试通过线程本地存储等,以增加线程安全的,但你没有控制线程的生命周期。由于不清楚的原因,您可能会得到模糊的错误。所以IMO如果你只需要两个线程,就有一个结构或两个那对(JNIEnv *,jobject)东西的数组。