在Android(NDK)中使用JNI回调实现“观察者-订阅者”模式

JNI回调的Android(NDK)实现,带有NDK和回调的Observer- Subscriber模式,自写的EventBus或Rx


... 我知道“里面没有用户可维修的零件”。 我想看看那里是什么。
-俄罗斯嵌套娃娃到最深处。 真的,奥罗斯科吗? 胡安没有看俄罗斯嵌套娃娃是什么。
“这是垃圾,顾教授。” 谁需要它-搞砸了吗?
“彩虹的尽头” Vinge Vernor

有很多Android应用程序将C ++和Java代码结合在一起。 Java实现业务逻辑,而C ++完成通常在音频处理中发现的所有计算工作。 音频流在内部某处进行处理,楼上显示带有油门和离合器的制动器,以及各种有趣图片的数据。
好吧,由于ReactiveX已经很熟悉了,为了不改手,并以熟悉的方式使用JNI地牢,您通常需要在带有NDK的项目中实现Observer模式。 好吧,与此同时,代码的可理解性 考古学家 那些不幸“理解别人的代码”的人增加了。
因此,最好的学习方法是自己做。
假设我们热爱并知道如何写自行车。 结果是:


  • 像是从C ++代码回发给签名者一样;
  • 以本机代码进行处理的管理,也就是说,在没有订阅者且没有人发送的情况下,我们可能不会被迫解决。
  • 不同的JVM之间可能需要数据传输;
  • 为了避免起床两次,同时在项目流中发送消息。

完整的工作代码可在GitHub上获得 。 本文仅提供摘录。


一点理论和历史


最近,我在RX会议上,对以下问题感到惊讶:ReactiveX有多快以及它如何工作。
对于ReactiveX,我只能说对于Java,它的速度在很大程度上取决于它的使用方式;如果使用正确,它的速度就足够了。
我们的自行车重量更轻,但是例如,如果您需要一个消息队列(如可流动的),则需要自己编写。 因为您知道所有故障都是您的。
理论上的一点: 观察者-订阅者模式是一种机制,它允许对象接收有关其他对象状态变化的警报,从而观察它们。 这样做是为了减少软件组件之间的连接性和依赖性,从而可以更有效地使用和测试它们。 语言概念所基于的生动代表就是一切-Smalltalk,全部基于发送消息的思想。 受影响的Objective-C。


实作


让我们尝试DIY的最佳传统,可以说是“闪烁LED”。 如果使用JNI,则在Android NDK世界中,可以在任何线程中异步请求Java方法。 这就是我们用来构建“观察者”的东西。


该演示项目基于Android Studio中的新项目模板。


这是一种自动生成的方法。 他评论说:


// Used to load the 'native-lib' library on application startup. static { System.loadLibrary("native-lib"); } 

现在为您自己。 第一步是nsubscribeListener方法。


 private native void nsubscribeListener(JNIListener JNIListener); 

它允许C ++代码获取到Java代码的链接,以实现对实现JNIListener.接口的对象的回调JNIListener.


 public interface JNIListener { void onAcceptMessage(String string); void onAcceptMessageVal(int messVal); } 

值将被转移到其方法的执行中。


为了有效地缓存到虚拟机的链接,我们还将链接保存到对象。 我们为此获得了全球链接。


 Java_ua_zt_mezon_myjnacallbacktest_MainActivity_nsubscribeListener(JNIEnv *env, jobject instance, jobject listener) { env->GetJavaVM(&jvm); //store jvm reference for later call store_env = env; jweak store_Wlistener = env->NewWeakGlobalRef(listener); 

立即计数并保存指向方法的链接。 这意味着完成回调所需的操作较少。


 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"); 

订户数据存储为ObserverChain类记录。


 class ObserverChain { public: ObserverChain(jweak pJobject, jmethodID pID, jmethodID pJmethodID); jweak store_Wlistener=NULL; jmethodID store_method = NULL; jmethodID store_methodVAL = NULL; }; 

将订户保存到动态数组store_Wlistener_vector。


 ObserverChain *tmpt = new ObserverChain(store_Wlistener, store_method, store_methodVAL); store_Wlistener_vector.push_back(tmpt); 

现在介绍如何传输来自Java代码的消息。


发送到nonNext方法的消息将发送给所有签名者。


 private native void nonNext(String message); 

实现方式:


 Java_ua_zt_mezon_myjnacallbacktest_MainActivity_nonNext(JNIEnv *env, jobject instance, jstring message_) { txtCallback(env, message_); } 

函数txtCallback(env,message_); 发送消息给所有签署者。


 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_); } } } 

要转发来自C ++或C代码的消息,我们使用test_string_callback_fom_c函数


 void test_string_callback_fom_c(char *val) 

她从一开始就检查是否有订户。


 if (store_Wlistener_vector.empty()) return; 

很容易看出,使用相同的txtCallback函数发送消息:


 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; } // double check it's all ok JavaVMAttachArgs args; args.version = JNI_VERSION_1_6; // set your JNI version args.name = NULL; // you might want to give the java thread a name args.group = NULL; // you might want to assign the java thread to a ThreadGroup int getEnvStat = jvm->GetEnv((void **) &g_env, JNI_VERSION_1_6); if (getEnvStat == JNI_EDETACHED) { __android_log_print(ANDROID_LOG_ERROR, "GetEnv:", " not attached\n"); if (jvm->AttachCurrentThread(&g_env, &args) != 0) { __android_log_print(ANDROID_LOG_ERROR, "GetEnv:", " Failed to attach\n"); } } else if (getEnvStat == JNI_OK) { __android_log_print(ANDROID_LOG_VERBOSE, "GetEnv:", " JNI_OK\n"); } else if (getEnvStat == JNI_EVERSION) { __android_log_print(ANDROID_LOG_ERROR, "GetEnv:", " version not supported\n"); } jstring message = g_env->NewStringUTF(val);// txtCallback(g_env, message); if (g_env->ExceptionCheck()) { g_env->ExceptionDescribe(); } if (getEnvStat == JNI_EDETACHED) { jvm->DetachCurrentThread(); } } 

在MainActivity中,我们创建两个文本视图和一个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" /> 

在OnCreate中,我们将View与变量绑定,并描述在EditText中更改文本时,将向订阅者发送一条消息。


 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); 

我们开始并注册订户:


 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); 

看起来像这样:



完整的工作代码位于GitHub https://github.com/NickZt/MyJNACallbackTest
在评论中让我们知道要详细描述的内容。

Source: https://habr.com/ru/post/zh-CN420389/


All Articles