2014-01-09 2 views
2

Мой вопрос связан непосредственно на этот пост: https://groups.google.com/forum/#!topic/android-ndk/291sBdkITyIИспользование NDK от C++ обновление Android UI

В принципе, у меня есть приложение, написанное на C++, скомпилированного НДК с основным Android (деятельность). У меня есть textview (на Java), который нужно обновлять, когда что-то происходит на стороне C++ (скажем, например, изменение состояния). Я хотел бы вызвать Java из C++ и обновить текстовое представление при изменении состояния.

В приведенной выше ссылке, код, который они использовали (вероятно, псевдо-код):

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

Вы правы, вы должны позвонить 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 
    } 
} 

В приведенном выше коде, проверка ошибок пропущена для краткости.

В качестве альтернативы вы можете передать ссылку на свою деятельность (или обработчик или загрузитьFiled) в библиотеку C, а вместо вызова CallStaticVoidMethod() вы должны использовать глобальную ссылку объекта и использовать CallVoidMethod(env, jactivityObj, mid, message).

PS Я просто удивляюсь, как вы ожидаете, вы система будет работать, если вы отправляете jbyteArray (в Java, это byte[]) к методу обратного вызова, который ожидает int?

Update Простой код выше подлежит Handler Leak, таким образом, если вы хотите использовать его в производстве, пожалуйста, включите 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 * внутри него, просто сохраните материал в статической переменной и обязательно используйте его только в потоке пользовательского интерфейса. Или, по крайней мере, всегда из той же темы.

PS Вы можете попробовать добавить потокобезопасность через локальное хранилище и т. Д. Но вы не контролируете жизненный цикл потоков. Вероятно, вы получите неясные ошибки из-за неясных причин. Таким образом, IMO, если вам нужно всего два потока, имеет структуру или массив из двух пар этого (JNIEnv *, jobject).

Смежные вопросы