一句话说透Android里面的观察者模式

234 阅读3分钟

一句话总结:
观察者模式就像「班级群通知」—— 老师(被观察者)发消息,学生(观察者)自动接收,Android 里的 LiveData、点击事件、广播都用这套机制实现消息传递!


一、观察者模式在 Android 的经典应用

1. LiveData 的数据驱动更新

源码实现

// frameworks/base/core/java/androidx/lifecycle/LiveData.java  
public abstract class LiveData<T> {  
    // 存储观察者的安全集合  
    private SafeIterableMap<Observer<? super T>, ObserverWrapper> mObservers = new SafeIterableMap<>();  

    // 注册观察者(自动绑定生命周期)  
    public void observe(LifecycleOwner owner, Observer<? super T> observer) {  
        LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);  
        mObservers.put(observer, wrapper);  
        owner.getLifecycle().addObserver(wrapper);  
    }  

    // 数据变更时通知观察者  
    protected void setValue(T value) {  
        mData = value;  
        dispatchingValue(null);  
    }  

    private void dispatchingValue(ObserverWrapper initiator) {  
        for (Map.Entry<Observer<? super T>, ObserverWrapper> entry : mObservers) {  
            considerNotify(entry.getValue()); // 逐个通知观察者  
        }  
    }  
}  

设计亮点

  • 生命周期感知:当 Activity/Fragment 销毁时自动移除观察者,避免内存泄漏
  • 版本控制:每个数据更新增加版本号,避免重复通知
  • 线程安全SafeIterableMap 支持并发遍历与修改

二、底层原理深入解析

1. 观察者注册与通知流程

用户调用 liveData.observe()  
→ 创建 LifecycleBoundObserver 包装类  
→ 将观察者存入 SafeIterableMap  
→ 当 LiveData 数据变化(setValue())  
→ 遍历所有观察者  
→ 检查生命周期状态(活跃才通知)  
→ 调用 observer.onChanged()  

2. 生命周期绑定实现(LifecycleBoundObserver)

// 内部类实现生命周期监听  
class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {  
    @Override  
    public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) {  
        if (owner.getLifecycle().getCurrentState() == DESTROYED) {  
            removeObserver(observer); // 自动移除销毁的观察者  
        }  
        activeStateChanged(shouldBeActive());  
    }  
}  

3. 线程切换机制(postValue)

// 在后台线程更新数据  
public void postValue(T value) {  
    // 将值存入队列  
    ArchTaskExecutor.getInstance().postToMainThread(() -> setValue(value));  
}  
  • 原理:通过 ArchTaskExecutor 切换到主线程执行 setValue()

三、其他典型应用场景

1. View 的点击事件监听

// 观察者接口  
public interface OnClickListener {  
    void onClick(View v);  
}  

// 被观察者 View 的实现  
public class View implements OnClickListener {  
    private OnClickListener mOnClickListener;  

    public void setOnClickListener(OnClickListener l) {  
        mOnClickListener = l; // 注册观察者  
    }  

    // 触发通知  
    public boolean performClick() {  
        if (mOnClickListener != null) {  
            mOnClickListener.onClick(this);  
            return true;  
        }  
        return false;  
    }  
}  

2. BroadcastReceiver 的广播系统

// 注册观察者  
IntentFilter filter = new IntentFilter("MY_ACTION");  
registerReceiver(new BroadcastReceiver() {  
    @Override  
    public void onReceive(Context context, Intent intent) {  
        // 处理广播  
    }  
}, filter);  

// 系统发送广播(被观察者通知)  
Intent broadcast = new Intent("MY_ACTION");  
sendBroadcast(broadcast);  

底层机制:通过 ActivityManagerService 管理广播队列,跨进程分发


四、观察者模式的优势与挑战

优势

  1. 解耦:发送方和接收方无需直接引用
  2. 动态订阅:运行时灵活添加/移除观察者
  3. 广播通信:一对多消息传递效率高

挑战

  1. 内存泄漏:忘记反注册观察者(如未取消广播注册)
  2. 性能问题:观察者过多或处理耗时操作导致卡顿
  3. 线程安全:跨线程通知需同步机制(如 LiveData 的 postValue

五、最佳实践与避坑指南

该做的

  1. 使用 LiveData 替代普通观察者:自动处理生命周期
  2. 在 onDestroy 中反注册:如 unregisterReceiver()
  3. 耗时操作切到子线程:避免在主线程的观察者中处理复杂逻辑

不该做的

  1. 在 Activity 中直接持有观察者引用:用弱引用或自动解绑
  2. 频繁通知无意义更新:如数据未变化也应避免触发 onChanged
  3. 在观察者中更新 UI 不加判断:先检查 isAdded() 或 isResumed()

六、源码中的设计模式变种

1. 事件总线(EventBus)的粘性事件

// 存储粘性事件  
private final Map<Class<?>, Object> stickyEvents = new ConcurrentHashMap<>();  

// 新观察者注册时收到历史事件  
public void register(Object subscriber) {  
    // 检查并发送粘性事件  
    for (Map.Entry<Class<?>, Object> entry : stickyEvents.entrySet()) {  
        postToSubscription(subscriber, entry.getValue(), true);  
    }  
}  

2. WorkManager 的任务状态观察

WorkManager.getInstance(context)  
    .getWorkInfoByIdLiveData(uploadWork.id)  
    .observe(this, workInfo -> {  
        if (workInfo.getState() == State.SUCCEEDED) {  
            // 处理任务完成  
        }  
    });  

七、总结口诀

观察模式像群聊,LiveData 是代表
注册监听等通知,生命周期自动管
点击广播都用它,忘记反注会泄漏

核心要点

  1. 使用 LiveData + ViewModel 实现数据驱动 UI
  2. 避免在观察者中直接操作视图(用数据绑定或 ViewModel 中转)
  3. 跨线程通知必须用 postValue 或 Handler

Android 观察者三剑客

场景被观察者观察者
数据变化LiveDataObserver
用户交互ViewOnClickListener
系统事件BroadcastBroadcastReceiver