观察者模式本身不自动更新UI,只负责通知变化;真正触发UI更新的是回调中调用setState、setText等刷新接口,漏掉则UI静止。

直接说结论:观察者模式本身不自动更新 UI,它只负责「通知变化」;真正触发 UI 更新的,是你的回调逻辑里是否调用了 setState、invalidate()、notifyDataSetChanged() 或类似刷新接口。漏掉这一步,数据变了,UI 依然静止。
为什么改了数据,UI 还没刷新?
常见错误现象是:你调用了 temp_sensor.set(25),控制台也打印了 “温度已更新”,但屏幕上的数字卡在 24 不动。
根本原因不是观察者没收到通知,而是观察者回调里没做 UI 操作:
- Android 中忘了调用
textView.setText()或adapter.notifyDataSetChanged() - 前端用了
Proxy劫持数据,但没在set的 handler 里触发render() - Unity 的
Bindable值变了,但没用.BindText(text)建立绑定
观察者模式只解决「谁来通知」和「谁来响应」的连接问题,不代劳「怎么更新」的具体实现。
Android LiveData + ViewModel 是最省心的组合
它把「数据变化 → 通知 → UI 更新」这条链封装好了,你只需要写一次订阅逻辑,后续数据变,UI 就自动跟上。
关键点在于:
-
LiveData是被观察对象,observe()是注册观察者 -
ViewModel持有LiveData,保证配置变更(如横竖屏)后不丢失数据 - 必须在主线程调用
postValue()或在后台线程用setValue()(否则抛IllegalStateException)
示例片段:
// ViewModel 中
private MutableLiveData<string> userName = new MutableLiveData();
public LiveData<string> getUserName() {
return userName;
}
// Activity 中
viewModel.getUserName().observe(this, name -> textView.setText(name));
</string></string>
注意:this 是 LifecycleOwner,确保自动解注册;如果传 getActivity() 或 getApplicationContext(),可能引发内存泄漏或空指针。
手写简易观察者时,别让 observer 数组越界或空指针
嵌入式或轻量级场景常自己实现观察者列表,最容易踩的坑是数组容量硬编码且不检查边界。
比如:
struct sensor { int value; void (*observers[8])(int); int count; };
如果 count 超过 8,observers[i] 就会访问非法内存。更稳妥的做法是:
- 用
malloc动态分配 observer 列表,或用Vector/ArrayList管理 - 每次调用
notify前加if (observers[i] != NULL)判空 - 提供
detach()接口,并在模块销毁前显式调用,避免野指针回调
尤其在资源受限的 MCU 上,一个未清理的 observer 回调可能让整个 LCD 刷新死循环。
前端 Proxy + WeakMap 是防内存泄漏的关键
JavaScript 中用 Object.defineProperty 或 Proxy 实现响应式,若观察者(如组件实例)被销毁但没取消订阅,就会造成内存泄漏。
正确做法是结合 WeakMap 自动关联目标对象与依赖列表:
const depsMap = new WeakMap();
function observe(obj, callback) {
if (!depsMap.has(obj)) depsMap.set(obj, new Set());
depsMap.get(obj).add(callback);
}
这样当 obj 被 GC 回收时,对应的 Set 也会被自动清理。而手动维护数组列表,极易忘记 unobserve()。
实际项目中,漏掉这层弱引用管理,跑几天就发现页面越来越卡——不是数据量大,是残留的 observer 越积越多。










