Android (NDK) -Implementierung von JNI-Rückrufen, Observer- Subscriber-Muster mit NDK und Rückruf, selbstgeschriebenem EventBus oder Rx
... Ich habe es verstanden "Es gibt keine vom Benutzer zu wartenden Teile im Inneren." Ich möchte sehen, was da ist.
- Russische Nistpuppen bis in die Tiefe. Wirklich, Orozco? Juan sah nicht aus, was eine russische Nistpuppe ist.
"Es ist Müll, Professor Gu." Wer braucht es - um sich damit anzulegen?
"Das Ende der Regenbogen" Vinge Vernor
Es gibt einige Android-Anwendungen, die C ++ - und Java-Code kombinieren. Java implementiert Geschäftslogik und C ++ erledigt die gesamte Computerarbeit, die häufig in der Audioverarbeitung zu finden ist. Der Audiostream wird irgendwo im Inneren verarbeitet, und die Bremse mit Gas und Kupplung wird oben angezeigt, und Daten für alle Arten von lustigen Bildern.
Nun, da ReactiveX bereits bekannt ist, müssen Sie das Observer-Muster regelmäßig in Projekten mit NDK implementieren, um Ihre Hand nicht zu ändern und auf vertraute Weise mit dem JNI-Dungeon zu arbeiten. Nun, gleichzeitig die Verständlichkeit des Codes für Archäologen Diejenigen, die das Pech haben, den Code eines anderen zu verstehen, nehmen zu.
Der beste Weg, etwas zu lernen, ist es, es selbst zu tun.
Nehmen wir an, wir lieben und wissen, wie man unsere Fahrräder schreibt. Und was bekommen wir als Ergebnis:
- so etwas wie ein Postback von C ++ - Code an Unterzeichner;
- Verwaltung der Verarbeitung in nativem Code, dh wir werden möglicherweise nicht zu Abrechnungen gezwungen, wenn keine Abonnenten vorhanden sind und niemand sie senden kann.
- Möglicherweise ist eine Datenübertragung zwischen verschiedenen JVMs erforderlich.
- und um nicht zweimal aufzustehen und gleichzeitig Nachrichten innerhalb des Projektflusses zu senden.
Der vollständige Arbeitscode ist auf GitHub verfügbar. Der Artikel enthält nur Auszüge daraus.
Ein bisschen Theorie und Geschichte
Vor kurzem war ich beim RX-Meeting und war erstaunt über die vielen Fragen zu: Wie schnell ist ReactiveX und wie funktioniert es überhaupt?
Für ReactiveX kann ich nur sagen, dass seine Geschwindigkeit für Java sehr stark davon abhängt, wie vernünftig es verwendet wird. Bei korrekter Verwendung ist seine Geschwindigkeit ausreichend.
Unser Fahrrad ist viel leichter, aber wenn Sie beispielsweise eine Nachrichtenwarteschlange (wie fließfähig) benötigen, müssen Sie diese selbst schreiben. Weil du weißt, dass alle Pannen nur deine sind.
Ein bisschen Theorie: Das Observer- Subscriber-Muster ist ein Mechanismus, mit dem ein Objekt Warnungen über Änderungen des Zustands anderer Objekte erhalten und diese beobachten kann. Dies geschieht, um die Konnektivität und Abhängigkeiten zwischen Softwarekomponenten zu verringern, sodass diese effizienter verwendet und getestet werden können. Ein lebendiger Vertreter, auf dem das Sprachkonzept aufbaut, ist alles - Smalltalk, alles basiert auf der Idee, Nachrichten zu senden. Betroffenes Ziel-C.
Implementierung
Lassen Sie uns in den besten Traditionen des Heimwerken versuchen, sozusagen „die LED zu blinken“. Wenn Sie JNI in der Android NDK-Welt verwenden, können Sie die Java-Methode in jedem Thread asynchron anfordern. Damit bauen wir unseren „Observer“.
Das Demo-Projekt basiert auf der neuen Projektvorlage von Android Studio.
Dies ist eine automatisch generierte Methode. Er kommentierte:
Nun zu dir. Der erste Schritt ist die nsubscribeListener
Methode.
private native void nsubscribeListener(JNIListener JNIListener);
Es ermöglicht C ++ - Code, einen Link zu Java-Code zu erhalten, um einen Rückruf zu einem Objekt zu ermöglichen, das die JNIListener.
Schnittstelle implementiert JNIListener.
public interface JNIListener { void onAcceptMessage(String string); void onAcceptMessageVal(int messVal); }
Werte werden in die Implementierung seiner Methoden übertragen.
Für ein effektives Zwischenspeichern von Links zur virtuellen Maschine speichern wir auch den Link zum Objekt. Wir bekommen einen globalen Link dafür.
Java_ua_zt_mezon_myjnacallbacktest_MainActivity_nsubscribeListener(JNIEnv *env, jobject instance, jobject listener) { env->GetJavaVM(&jvm);
Zählen und speichern Sie sofort Links zu Methoden. Dies bedeutet, dass weniger Vorgänge erforderlich sind, um den Rückruf abzuschließen.
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");
Abonnentendaten werden als ObserverChain
Klassendatensätze gespeichert.
class ObserverChain { public: ObserverChain(jweak pJobject, jmethodID pID, jmethodID pJmethodID); jweak store_Wlistener=NULL; jmethodID store_method = NULL; jmethodID store_methodVAL = NULL; };
Speichern Sie den Abonnenten im dynamischen Array store_Wlistener_vector.
ObserverChain *tmpt = new ObserverChain(store_Wlistener, store_method, store_methodVAL); store_Wlistener_vector.push_back(tmpt);
Nun darüber, wie Nachrichten aus Java-Code übertragen werden.
Eine an die nonNext-Methode gesendete Nachricht wird an alle Unterzeichner gesendet.
private native void nonNext(String message);
Implementierung:
Java_ua_zt_mezon_myjnacallbacktest_MainActivity_nonNext(JNIEnv *env, jobject instance, jstring message_) { txtCallback(env, message_); }
Funktion txtCallback (env, message_); sendet Nachrichten an alle Unterzeichner.
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_); } } }
Um Nachrichten aus C ++ oder C-Code weiterzuleiten, verwenden wir die Funktion test_string_callback_fom_c
void test_string_callback_fom_c(char *val)
Sie prüft von Anfang an, ob es überhaupt Abonnenten gibt.
if (store_Wlistener_vector.empty()) return;
Es ist leicht zu erkennen, dass dieselbe txtCallback-Funktion zum Senden von Nachrichten verwendet wird:
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; }
In MainActivity erstellen wir zwei Textansichten und eine 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" />
In OnCreate binden wir View mit Variablen und beschreiben, dass beim Ändern von Text in EditText eine Nachricht an Abonnenten gesendet wird.
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);
Wir starten und registrieren Abonnenten:
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);
Es sieht ungefähr so aus:

Der vollständige Arbeitscode ist auf GitHub https://github.com/NickZt/MyJNACallbackTest verfügbar
Lassen Sie uns in den Kommentaren wissen, was wir genauer beschreiben sollen.