2014-01-09 42 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)東西的數組。