简介
观察者模式是一种设计模式,用于定义对象之间的一对多依赖关系,使得当一个对象的状态发生变化时,所有依赖它的对象都会得到通知并自动更新。这种模式包括主题(Subject)和观察者(Observer)两个角色,主题负责维护一组观察者,并在状态变化时通知它们。观察者模式有助于对象之间的解耦合,使得它们可以更灵活地交互。在 Android 或者 C++ 开发当中,我们经常会使用到这种设计模式,用于发布订阅消息。下面是一个简单的观察者模式实现:
// 主题接口
interface Subject {
void registerObserver(Observer observer);
void removeObserver(Observer observer);
void notifyObservers();
}
// 观察者接口
interface Observer {
void onReceive(String message);
}
// 具体主题
class ConcreteSubject implements Subject {
private List<Observer> observers = new ArrayList<>();
private String message;
@Override
public void registerObserver(Observer observer) {
observers.add(observer);
}
@Override
public void removeObserver(Observer observer) {
observers.remove(observer);
}
@Override
public void notifyObservers() {
for (Observer observer : observers) {
observer.onReceive(message);
}
}
public void sendMessage(String message) {
this.message = message;
notifyObservers();
}
}
// 具体观察者
class ConcreteObserver implements Observer {
private String name;
public ConcreteObserver(String name) {
this.name = name;
}
@Override
public void update(String message) {
System.out.println(name + " received message: " + message);
}
}
// 测试类
public class ObserverExample {
public static void main(String[] args) {
ConcreteSubject subject = new ConcreteSubject();
ConcreteObserver observer1 = new ConcreteObserver("Observer 1");
ConcreteObserver observer2 = new ConcreteObserver("Observer 2");
subject.registerObserver(observer1);
subject.registerObserver(observer2);
subject.sendMessage("New message!");
}
}
相信上面的代码大家都能够实现,我们通过纯 Java 或者纯 C++ 语言都能够很方便的实现观察者模式。但是如果我们现在要要进行跨语言开发,由 Java 实现观察者,C++ 发布主题通知;或者由 C++ 实现观察者,Java 发布主题通知,这样的实现就会比较复杂了。因为是跨语言开发,所以 Java、C++ 之间没办法直接调用接口来添加观察者、通知消息。在这种情况下,我们就要使用到 JNI 中的引用来实现跨语言观察者。
JNI 观察者模式
Java 作为观察者,C++ 作为主题
这种场景在我们的开发过程中是比较常见的,我们通常会在 c++ 层封装一些通用的逻辑,以达到跨平台的效果。
首先,我们需要实现 java 层的观察者,代码和上面类似,我这里就不放出具体的代码了。唯一不同的一点是,在 Java 层需要额外定义 registerObserver
函数,用于将 Java 观察者注册到 c++ 层当中:
private native void registerObserver(long nativeTestSubject, Observer observer);
在 c++ 层实现这个函数:
void removeObserverNative(JNIEnv *env, jobject clazz, jlong native_test_subject,
jobject observer) {
//创建一个 c++ 的 TestSubject 主题对象,用于发布消息
auto *test_subject = new TestSubject();
test_subject->registerObserver(observer);
//测试,在注册观察者之后,主题就会发送一条消息
test_subject->notifyObservers();
}
然后,我们需要实现 c++ 层的主题,参考上面 Java 代码的实现,我们需要定义一个 Subject
主题接口,并有一个具体主题 TestSubject
实现:
class Subject {
public:
virtual void registerObserver(jobject observer) = 0;
virtual void removeObserver(jobject observer) = 0;
virtual void notifyObservers() = 0;
};
// TestSubject.h
class TestSubject : public Subject {
public:
virtual void registerObserver(jobject observer) override;
virtual void removeObserver(jobject observer) override;
virtual void notifyObservers() override;
};
private:
std::list<jobject> observer_list_;
};
// TestSubject.cpp
void TestSubject::registerObserver(jobject observer) {
observer_list_.push_back(observer);
}
void TestSubject::removeObserver(jobject observer) {
observer_list_.remove(observer);
}
需要注意是,如果是纯 c++ 开发,我们会定义一个 Observer
类对象作为 registerObserver
的入参,但由于我们的观察者对象是 Java 层。所以我们这里的入参需要定义为 jobject
,即 Java 层的观察者。那么,我们该如何实现 notifyObservers
将消息传递给 Java 层呢?
使用 JNI 反射调用即可,下面是 notifyObservers
的实现:
void TestSubject::notifyObservers() {
for (auto observer: observer_list_) {
JNIEnv *env = AttachCurrentThread();
if (env == nullptr) {
return;
}
jclass cls_Observer = env->FindClass("com/netease/android/jnimethoddemo/Observer");
if (cls_Observer == nullptr) {
return;
}
jmethodID mid = env->GetMethodID(cls_Observer, "onReceive", "(Ljava/lang/String;)V");
if (mid == nullptr) {
return;
}
std::string message = "msg from c++";
jstring jmsg = env->NewStringUTF(message.c_str());
env->CallVoidMethod(observer, mid, jmsg);
}
}
我们只需要通过 FindClass
和 GetMethodID
找到 Java 层 Observer
对象接受消息的函数,就可以通过 CallVoidMethod
进行反射调用,将消息通知给 Java 层的观察者对象啦。
看一下测试代码以及测试效果:
TestObserver observer = new TestObserver();
observer.test();
Java 作为主题,C++ 作为观察者
同样的,如果 Java 作为主题,C++ 作为观察者的话,我们也是按照同样的流程来实现。首先实现 c++ 层的观察者,实现如下:
class Observer2 {
public:
virtual void onReceive(const std::string& msg) = 0;
};
//TestObserver2.h
class TestObserver2 : public Observer2 {
public:
TestObserver2();
virtual ~TestObserver2();
virtual void onReceive(const std::string& msg) override;
void test();
private:
jobject test_subject2_;
};
//TestObserver2.cpp
void TestObserver2::onReceive(const std::string &msg) {
LOGD("%s", msg.c_str());
}
void TestObserver2::test() {
JNIEnv *env = AttachCurrentThread();
if (env == nullptr) {
return;
}
jclass cls_TestSubject2 = env->FindClass("com/netease/android/jnimethoddemo/TestSubject2");
if (cls_TestSubject2 == nullptr) {
return;
}
jmethodID mid = env->GetMethodID(cls_TestSubject2, "<init>", "()V");
if (mid == nullptr) {
return;
}
test_subject2_ = env->NewGlobalRef(env->NewObject(cls_TestSubject2, mid));
jmethodID mid_register = env->GetMethodID(cls_TestSubject2, "registerObserver", "(J)V");
if (mid_register == nullptr) {
return;
}
env->CallVoidMethod(test_subject2_, mid_register, reinterpret_cast<jlong>(this));
}
void notifyObserversNative(JNIEnv *env, jobject clazz, jlong native_test_observer,
jstring msg) {
auto *test_observer2 = reinterpret_cast<TestObserver2 *>(native_test_observer);
if (test_observer2 == nullptr) {
return;
}
char *msg_char = JNIUtil::jstringToChar(env, msg);
if (msg_char == nullptr) {
return;
}
test_observer2->onReceive(msg_char);
}
extern "C" {
jint register_android_test_observer2(JNIEnv *env) {
jclass clazz = env->FindClass("com/netease/android/jnimethoddemo/TestSubject2");
if (clazz == nullptr) {
return JNI_ERR;
}
JNINativeMethod methods[] = {
{"notifyObserversNative", "(JLjava/lang/String;)V", (void *) notifyObserversNative},
{"testNative", "()V", (void *) testNative},
};
return env->RegisterNatives(clazz, methods,
sizeof(methods) /
sizeof(methods[0]));
}
}
其中,最关键的代码就是注册 notifyObserversNative
函数, notifyObserversNative
函数的主要作用就是接受来自 Java 层的通知消息,根据入参中的 jlong native_test_observer
,将 native_test_observer
强转为 c++ 的观察者对象,并调用 test_observer2->onReceive(msg_char);
接受消息。
另外,还需要注意一点,举一个实际的例子:例如我们在开发 C++ 的时候,我们希望监听 Java 的网络切换,我们肯定需要创建一个创建一个 Java 层的网络监听者,然后向这个网络监听者当中注册我们的网络观察者,这一步是至关只要的。在上面的测试代码中,可以参考 test()
中的实现,通过反射的方式获取 Java 中的主题类,并通过 init
函数创建一个主题类对象,通过成员变量 test_subject2_
保存下来。后续就可以通过这个 test_subject2_
对象来进行添加、移除观察者的操作的。
Java 中主题类实现就比较简单了:
public class TestSubject2 implements Subject2 {
private List<Long> nativeObservserList = new ArrayList<>();
@Override
public void registerObserver(long observer) {
nativeObservserList.add(observer);
}
@Override
public void removeObserver(long observer) {
Iterator<Long> iterator = nativeObservserList.iterator();
while (iterator.hasNext()) {
Long nativeObserver = iterator.next();
if (nativeObserver == observer) {
iterator.remove();
}
}
}
@Override
public void notifyObservers() {
for (Long observer : nativeObservserList) {
notifyObserversNative(observer, "onReceive: msg from java");
}
}
private native void notifyObserversNative(long nativeObserver, String msg);
}
上述代码提供 registerObserver
和 removeObserver
用于添加、移除观察者,由于我们维护的观察者对象是 C++ 的 long 类型指针,所以我们在移除观察者的时候需要通过对比 long 的值来判断是不是同一个观察者对象。
看一下具体的代码运行效果:
总结
JNI 中的观察者模式实现和纯 Java、纯 C++ 中实现的最大区别主要有以下两点:文章来源:https://www.toymoban.com/news/detail-783644.html
- 需要通过方法注册来实现相互之间的添加、移除观察者,以及消息通知。
- Java 或者 C++ 中都需要维护彼此之间的对象引用,用于函数调用。
在实际开发过程当中,我们可以根据需求来选择使用纯语言或者 JNI 跨语言来实现观察者模式。但有时候出于代码安全性、复用性考虑,我们会将一些重复的代码实现放在 C++ 中,然后打包成 so 等动态库提供给上层使用。在这种情况下,我们只能选择使用 JNI 跨语言来实现观察者模式。文章来源地址https://www.toymoban.com/news/detail-783644.html
到了这里,关于JNI的一些最佳实践(四)观察者模式的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!