Implementação Android (NDK) de retornos de chamada JNI, padrão Observer- Subscriber com NDK e retorno de chamada, EventBus ou Rx proprietário
... Entendi "não há peças que possam ser reparadas pelo usuário". Eu quero ver o que está lá.
- Bonecas russas até as profundezas. Sério, Orozco? Juan não parecia o que é uma boneca russa.
"É lixo, professor Gu." Quem precisa disso - para mexer com isso?
"O fim dos arco-íris" Vinge Vernor
Existem alguns aplicativos Android que combinam código C ++ e Java. Java implementa lógica de negócios e C ++ faz todo o trabalho de computação, geralmente encontrado no processamento de áudio. O fluxo de áudio é processado em algum lugar dentro, e o freio com gasolina e embreagem é exibido no andar de cima, com dados para todos os tipos de fotos engraçadas.
Bem, desde o ReactiveX, ele já é familiar, para não mudar de mão e trabalhar com a masmorra JNI de maneiras familiares, você precisa regularmente implementar o padrão Observer em projetos com NDK. Bem, ao mesmo tempo, a compreensibilidade do código para arqueólogos aqueles que têm azar de "entender o código de outra pessoa" aumentam.
Portanto, a melhor maneira de aprender algo é fazer você mesmo.
Digamos que amamos e sabemos como escrever nossas motos. E o que obteremos como resultado:
- algo como uma postagem do código C ++ para os signatários;
- gerenciamento do processamento em código nativo, ou seja, não podemos ser forçados a fazer acordos quando não houver assinantes e não houver ninguém para enviá-los;
- transferência de dados entre diferentes JVMs pode ser necessária;
- e para não se levantar duas vezes, ao mesmo tempo enviando mensagens dentro dos fluxos do projeto.
O código completo de trabalho está disponível no GitHub . O artigo fornece apenas trechos dele.
Um pouco de teoria e história
Recentemente, eu estava na reunião do RX e fiquei surpreso com o número de perguntas sobre: quão rápido é o ReactiveX e como ele funciona.
Para o ReactiveX, posso apenas dizer que, para Java, sua velocidade depende muito de quão razoavelmente é usada; se usada corretamente, sua velocidade é suficiente.
Nossa bicicleta é muito mais leve, mas se você precisar, por exemplo, de uma fila de mensagens (como fluida), precisará escrever por conta própria. Porque você sabe que todas as falhas são apenas suas.
Um pouco de teoria: o padrão Observer- Subscriber é um mecanismo que permite que um objeto receba alertas sobre mudanças no estado de outros objetos e, portanto, os observe. Isso é feito para reduzir a conectividade e as dependências entre os componentes de software, o que permite que eles sejam usados e testados com mais eficiência. Um representante vívido no qual o conceito de linguagem é construído sobre isso é tudo - Smalltalk, tudo baseado na idéia de enviar mensagens. Objetivo-C afetado.
Implementação
Vamos tentar as melhores tradições de bricolage, por assim dizer, "pisque o LED". Se você usar JNI, no mundo NDK do Android, poderá solicitar o método Java de forma assíncrona, em qualquer encadeamento. É isso que usamos para construir nosso "Observador".
O projeto de demonstração é baseado no novo modelo de projeto do Android Studio.
Este é um método gerado automaticamente. Ele comentou:
Agora para você. A primeira etapa é o método nsubscribeListener
.
private native void nsubscribeListener(JNIListener JNIListener);
Ele permite que o código C ++ obtenha um link para o código java para ativar um retorno de chamada para um objeto que implementa a interface JNIListener.
public interface JNIListener { void onAcceptMessage(String string); void onAcceptMessageVal(int messVal); }
Os valores serão transferidos para a implementação de seus métodos.
Para um cache eficaz de links para a máquina virtual, também salvamos o link no objeto. Nós temos um link global para isso.
Java_ua_zt_mezon_myjnacallbacktest_MainActivity_nsubscribeListener(JNIEnv *env, jobject instance, jobject listener) { env->GetJavaVM(&jvm);
Contar e salvar imediatamente os links para os métodos. Isso significa que menos operações são necessárias para concluir o retorno de chamada.
jclass clazz = env->GetObjectClass(store_Wlistener); jmethodID store_method = env->GetMethodID(clazz, "onAcceptMessage", "(Ljava/lang/String;)V"); jmethodID store_methodVAL = env->GetMethodID(clazz, "onAcceptMessageVal", "(I)V");
Os dados do assinante são armazenados como registros da classe ObserverChain
.
class ObserverChain { public: ObserverChain(jweak pJobject, jmethodID pID, jmethodID pJmethodID); jweak store_Wlistener=NULL; jmethodID store_method = NULL; jmethodID store_methodVAL = NULL; };
Salve o assinante no array dinâmico store_Wlistener_vector.
ObserverChain *tmpt = new ObserverChain(store_Wlistener, store_method, store_methodVAL); store_Wlistener_vector.push_back(tmpt);
Agora, sobre como as mensagens do código Java serão transmitidas.
Uma mensagem enviada para o método nonNext será enviada a todos os signatários.
private native void nonNext(String message);
Implementação:
Java_ua_zt_mezon_myjnacallbacktest_MainActivity_nonNext(JNIEnv *env, jobject instance, jstring message_) { txtCallback(env, message_); }
Função txtCallback (env, message_); envia mensagens para todos os signatários.
void txtCallback(JNIEnv *env, const _jstring *message_) { if (!store_Wlistener_vector.empty()) { for (int i = 0; i < store_Wlistener_vector.size(); i++) { env->CallVoidMethod(store_Wlistener_vector[i]->store_Wlistener, store_Wlistener_vector[i]->store_method, message_); } } }
Para encaminhar mensagens do código C ++ ou C, usamos a função test_string_callback_fom_c
void test_string_callback_fom_c(char *val)
Ela verifica desde o início se há algum assinante.
if (store_Wlistener_vector.empty()) return;
É fácil ver que a mesma função txtCallback é usada para enviar mensagens:
void test_string_callback_fom_c(char *val) { if (store_Wlistener_vector.empty()) return; __android_log_print(ANDROID_LOG_VERBOSE, "GetEnv:", " start Callback to JNL [%d] \n", val); JNIEnv *g_env; if (NULL == jvm) { __android_log_print(ANDROID_LOG_ERROR, "GetEnv:", " No VM \n"); return; }
No MainActivity, criamos duas visualizações de texto e uma EditView.
<TextView android:id="@+id/sample_text_from_Presenter" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:padding="25dp" android:text="Hello World!from_Presenter" app:layout_constraintBottom_toTopOf="parent" app:layout_constraintEnd_toStartOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <TextView android:id="@+id/sample_text_from_act" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" android:text="Hello World from_ac!" app:layout_constraintBottom_toTopOf="parent" app:layout_constraintStart_toStartOf="@+id/sample_text_from_Presenter" app:layout_constraintTop_toTopOf="parent" /> <EditText android:id="@+id/edit_text" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello World!" app:layout_constraintBottom_toTopOf="parent" app:layout_constraintStart_toStartOf="@+id/sample_text_from_act" app:layout_constraintTop_toTopOf="parent" />
No OnCreate, vinculamos o View às variáveis e descrevemos que quando o texto é alterado no EditText, uma mensagem será enviada aos assinantes.
mEditText = findViewById(R.id.edit_text); mEditText.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) { } @Override public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) { nonNext(charSequence.toString()); } @Override public void afterTextChanged(Editable editable) { } }); tvPresenter = (TextView) findViewById(R.id.sample_text_from_Presenter); tvAct = (TextView) findViewById(R.id.sample_text_from_act);
Iniciamos e registramos assinantes:
mPresenter = new MainActivityPresenterImpl(this); nsubscribeListener((MainActivityPresenterImpl) mPresenter); nlistener = new JNIListener() { @Override public void onAcceptMessage(String string) { printTextfrActObj(string); } @Override public void onAcceptMessageVal(int messVal) { } }; nsubscribeListener(nlistener);
Parece algo como isto:

O código completo de trabalho está disponível no GitHub https://github.com/NickZt/MyJNACallbackTest
Nos deixe saber nos comentários o que descrever com mais detalhes.