LiveEventBusCore 跨 Activity 通讯源码解析

源码版本:JeremyLiao / LiveEventBus(master 分支) 核心文件:

  • com.jeremyliao.liveeventbus.core.LiveEventBusCore
  • com.jeremyliao.liveeventbus.core.Observable
  • android.arch.lifecycle.ExternalLiveData
  • com.jeremyliao.liveeventbus.core.Config

1. 从一个典型场景说起

Activity A 登录成功后需要通知 Activity B 刷新用户头像,常规做法是:

  • startActivityForResult / onActivityResult 层层回调;
  • 或者在 Application 里挂一个全局监听器,手动管理注册与反注册。

LiveEventBus 的思路是:用一把"钥匙"(字符串 key)把发送端和接收端串起来 ,底层交给 LiveData 做生命周期感知的消息分发。于是上面的需求可以写成:

csharp 复制代码
// Activity A 登录成功后发送
LiveEventBus.get("user_login")
        .post(new UserInfo("张三"));
​
// Activity B 订阅
LiveEventBus.get("user_login", UserInfo.class)
        .observe(this, userInfo -> {
            // 刷新头像
        });

整个 App 里所有 Activity 只要持有同一个 key,就能像订阅同一个电台一样收发消息,而且不用手动解绑------这是 LiveData 自带的能力。

2. 核心角色一览

角色 职责 关键源码位置
LiveEventBusCore 单例总线,持有所有 LiveEvent core/LiveEventBusCore.java
LiveEvent 对应一个 key 的消息通道,内部封装 LifecycleLiveData LiveEventBusCore 的非静态内部类
LifecycleLiveData 继承自 ExternalLiveData,决定观察者在哪个生命周期状态算"激活" LiveEventBusCore 的内部类
ExternalLiveData 对官方 LiveData 的扩展,开放 mVersion、自定义 observerActiveLevel android/arch/lifecycle/ExternalLiveData.java
ObserverWrapper 装饰器,控制"非粘性消息"第一次回调是否被吞掉 LiveEventBusCore 的内部类
Observable 对外接口,定义 post / observe / observeSticky 等方法 core/Observable.java
Config 全局/单 key 配置入口 core/Config.java

一句话总结:LiveEventBusCore 是车站,LiveEvent 是某条线路的调度室,LifecycleLiveData 是这条线路上的广播喇叭。

3. 单例与 key 路由

vbnet 复制代码
public final class LiveEventBusCore {
​
    private static class SingletonHolder {
        private static final LiveEventBusCore DEFAULT_BUS = new LiveEventBusCore();
    }
​
    public static LiveEventBusCore get() {
        return SingletonHolder.DEFAULT_BUS;
    }
​
    private final Map<String, LiveEvent<Object>> bus;
​
    private LiveEventBusCore() {
        bus = new HashMap<>();
        // ...
    }
​
    public synchronized <T> Observable<T> with(String key, Class<T> type) {
        if (!bus.containsKey(key)) {
            bus.put(key, new LiveEvent<>(key));
        }
        return (Observable<T>) bus.get(key);
    }
}
  • 采用静态内部类 实现懒加载单例,线程安全且无需 synchronized 锁整个方法。
  • with(key, type) 保证同一个 key 全局只有一份 LiveEvent,这是跨 Activity 能收到同一条消息的根本。
  • 泛型 Class<T> 主要给调用端做类型提示,底层 LiveEvent 实际以 Object 存取。

4. 发送一条消息到底发生了什么

LiveEvent 里的 post 方法:

typescript 复制代码
@Override
public void post(T value) {
    if (ThreadUtils.isMainThread()) {
        postInternal(value);
    } else {
        mainHandler.post(new PostValueTask(value));
    }
}
​
@MainThread
private void postInternal(T value) {
    logger.log(Level.INFO, "post: " + value + " with key: " + key);
    liveData.setValue(value);
}

流程很简单:

  1. 无论在子线程还是主线程,最终都会切到主线程执行 setValue。
  2. liveData.setValue(value) 会触发 LiveData 自身的分发逻辑:遍历 mObservers,把消息派给当前处于激活状态的观察者。

4.1 为什么最终一定要回到主线程

官方 LiveData.setValue 会检查线程:

typescript 复制代码
// LiveData.java
@MainThread
protected void setValue(T value) {
    assertMainThread("setValue");
    mVersion++;
    mData = value;
    dispatchingValue(null);
}

所以 LiveEventBus 在框架层先把消息 post 到 mainHandler,避免调用方自己处理线程切换。

5. 不同 Activity 为什么能收到同一条消息

关键在于 liveData 是进程内单例的。当 Activity B 调用:

kotlin 复制代码
LiveEventBus.get("user_login", UserInfo.class).observe(this, ...);

实际上拿到的是 LiveEventBusCore.bus.get("user_login") 里的同一个 LiveEvent 对象,同一个 LifecycleLiveData。

因此 Activity A 调用 post 修改了这个 LiveData 的值后,所有绑定到该 key 且处于激活状态的 Activity/Fragment 都会收到回调。

5.1 生命周期是怎么被"感知"的

核心类 ExternalLiveData:

scala 复制代码
public class ExternalLiveData<T> extends MutableLiveData<T> {
​
    public static final int START_VERSION = LiveData.START_VERSION;
​
    @Override
    public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer) {
        if (owner.getLifecycle().getCurrentState() == DESTROYED) {
            return;
        }
        try {
            // 用自定义的 ExternalLifecycleBoundObserver 替代官方的 LifecycleBoundObserver
            LifecycleBoundObserver wrapper = new ExternalLifecycleBoundObserver(owner, observer);
            LifecycleBoundObserver existing =
                    (LifecycleBoundObserver) callMethodPutIfAbsent(observer, wrapper);
            if (existing != null && !existing.isAttachedTo(owner)) {
                throw new IllegalArgumentException(
                        "Cannot add the same observer with different lifecycles");
            }
            if (existing != null) {
                return;
            }
            owner.getLifecycle().addObserver(wrapper);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
​
    protected Lifecycle.State observerActiveLevel() {
        return CREATED;
    }
​
    class ExternalLifecycleBoundObserver extends LifecycleBoundObserver {
        ExternalLifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<T> observer) {
            super(owner, observer);
        }
​
        @Override
        boolean shouldBeActive() {
            return mOwner.getLifecycle().getCurrentState().isAtLeast(observerActiveLevel());
        }
    }
}

官方 LiveData 判断观察者是否激活,用的是 Lifecycle.State.STARTED,即 Activity 必须处于 onStart 之后才会收到消息。

LiveEventBus 通过反射把观察者包装成自己的 ExternalLifecycleBoundObserver,并重写 shouldBeActive():

scss 复制代码
return mOwner.getLifecycle().getCurrentState().isAtLeast(observerActiveLevel());

默认 observerActiveLevel() 返回 CREATED,也就是说只要 Activity 没有被销毁,就能收到消息。

5.2 可配置:让后台 Activity 收不到实时消息

在 Application.onCreate 中:

scss 复制代码
LiveEventBus.get()
        .config()
        .lifecycleObserverAlwaysActive(false);

此时 LifecycleLiveData 的 observerActiveLevel() 返回 STARTED:

typescript 复制代码
@Override
protected Lifecycle.State observerActiveLevel() {
    return lifecycleObserverAlwaysActive() ? Lifecycle.State.CREATED : Lifecycle.State.STARTED;
}

这意味着:

  • true(默认):Activity 在 onCreate ~ onDestroy 全周期实时收消息,即使当前在后台。
  • false:只有 onStart ~ onStop 之间实时收消息;后台时暂存,回到前台再补发最近一条。

6. 非粘性消息是怎么实现的

LiveData 自带"粘性":新注册的观察者会立即收到当前已存在的值。LiveEventBus 的 observe 需要避免这种行为,于是引入了 ObserverWrapper:

less 复制代码
private class ObserverWrapper<T> implements Observer<T> {

    @NonNull
    private final Observer<T> observer;
    private boolean preventNextEvent = false;

    ObserverWrapper(@NonNull Observer<T> observer) {
        this.observer = observer;
    }

    @Override
    public void onChanged(@Nullable T t) {
        if (preventNextEvent) {
            preventNextEvent = false;
            return;
        }
        observer.onChanged(t);
    }
}

注册非粘性观察者时:

less 复制代码
@MainThread
private void observeInternal(@NonNull LifecycleOwner owner, @NonNull Observer<T> observer) {
    ObserverWrapper<T> observerWrapper = new ObserverWrapper<>(observer);
    observerWrapper.preventNextEvent = liveData.getVersion() > ExternalLiveData.START_VERSION;
    liveData.observe(owner, observerWrapper);
}
  • START_VERSION 是 LiveData 初始版本号,通常为 -1。
  • 如果 liveData.getVersion() > START_VERSION,说明这条 LiveData 之前已经 setValue 过。
  • 此时把 preventNextEvent 置为 true,观察者注册后第一次 onChanged 会被拦截,从而避免收到"历史消息"。
  • 后续再有新消息,preventNextEvent 已经重置为 false,正常回调。

6.1 粘性订阅

less 复制代码
@Override
public void observeSticky(@NonNull final LifecycleOwner owner,
                          @NonNull final Observer<T> observer) {
    // ... 切到主线程
    observeStickyInternal(owner, observer);
}

@MainThread
private void observeStickyInternal(@NonNull LifecycleOwner owner,
                                    @NonNull Observer<T> observer) {
    ObserverWrapper<T> observerWrapper = new ObserverWrapper<>(observer);
    liveData.observe(owner, observerWrapper);
}

observeSticky 没有设置 preventNextEvent,因此会保留 LiveData 的默认粘性行为:

  • 如果注册时已经有值,立即回调一次;
  • 之后有新值,继续回调。

7. 完整流程串讲

假设当前 App 有两个 Activity:A 和 B。

csharp 复制代码
1. App 启动,LiveEventBusCore 单例初始化,bus = {}。

2. Activity B 在 onCreate 中注册:
   LiveEventBus.get("login_ok", UserInfo.class).observe(this, observer);
   → LiveEventBusCore.with 创建 key="login_ok" 的 LiveEvent
   → LifecycleLiveData 诞生
   → observer 被包成 ObserverWrapper,并设置 preventNextEvent
   → ExternalLiveData.observe 把 wrapper 注册到 Activity B 的 Lifecycle

3. Activity A 登录成功后调用:
   LiveEventBus.get("login_ok").post(userInfo);
   → 切到主线程
   → liveData.setValue(userInfo)
   → LiveData 遍历 mObservers,发现 Activity B 的 wrapper 处于激活状态
   → wrapper.onChanged(userInfo)
   → 由于 preventNextEvent 已重置,调用业务 observer.onChanged(userInfo)

4. Activity B 被销毁,Lifecycle 自动把 wrapper 移除,无需手动 unregister。

8. 跨进程 / 跨 App 通讯和 Activity 通讯的区别

LiveEventBus 还提供:

  • postAcrossProcess:App 内跨进程;
  • postAcrossApp:跨 App;
  • broadcast:底层走 sendBroadcast + LebIpcReceiver。

但在同一个进程里的不同 Activity 之间通讯时,走的还是普通的 post → setValue,不需要广播。广播方案是为了解决多进程/多 App 场景:

typescript 复制代码
@Override
public void postAcrossProcess(T value) {
    broadcast(value, false, true);   // onlyInApp = true
}

@Override
public void postAcrossApp(T value) {
    broadcast(value, false, false);  // 全局
}

如果项目不是多进程架构,不需要为了跨 Activity 通讯而调用 postAcrossProcess,否则既浪费 IPC 开销,又增加序列化限制。

9. 容易被忽略的细节

9.1 autoClear:没有观察者时是否清掉通道

less 复制代码
@Override
public void removeObserver(@NonNull Observer<T> observer) {
    super.removeObserver(observer);
    if (autoClear() && !liveData.hasObservers()) {
        LiveEventBusCore.get().bus.remove(key);
    }
}

默认 autoClear = false。开启后,当某个 key 下没有任何观察者时,会从 bus 中移除,避免空 LiveEvent 长期驻留。

9.2 子线程发送

post 内部已经做了线程切换,业务方可以安全地在子线程调用。如果想保证消息顺序,可用 postOrderly,它统一 post 到主线程 Handler 队列,按入队顺序执行。

9.3 同一个 observer 重复注册

ExternalLiveData.observe 沿用了官方 LiveData 的去重逻辑:同一个 observer 对象重复 observe 会被忽略,但绑定到不同 LifecycleOwner 会抛异常。

9.4 类型安全

kotlin 复制代码
LiveEventBus.get("login_ok", UserInfo.class)
        .observe(this, userInfo -> { ... });

with(key, type) 的 type 并不改变底层存储类型,主要用于 IDE 推断和运行期 ObserverWrapper 的异常捕获。传错类型可能导致 ClassCastException,框架里已经 catch 并打印日志:

erlang 复制代码
catch (ClassCastException e) {
    logger.log(Level.WARNING, "class cast error on message received: " + t, e);
}

10. 总结

LiveEventBus 跨 Activity 通讯的精髓非常紧凑:

  1. 单例 LiveEventBusCore + HashMap<String, LiveEvent> :全局共享消息通道。
  2. LiveEvent 内部持有一个 LifecycleLiveData :所有订阅同一 key 的 Activity/Fragment 共享同一个 LiveData。
  3. ExternalLiveData 重写 shouldBeActive :把激活阈值从默认的 STARTED 降到 CREATED,让 Activity 从 onCreate 起就能收到消息,且生命周期销毁后自动解绑。
  4. ObserverWrapper.preventNextEvent :在保留 LiveData 粘性的基础上,给普通 observe 增加"非粘性"语义。

理解了这四点,就能明白为什么 Activity A 的一条 post 可以无缝地到达 Activity B,同时还能保证生命周期安全和内存安全。

相关推荐
维克兜率天1 小时前
【维克】统计套利建模:从因子到信号的完整流程
android·开发语言·python·算法·kotlin
hunterandroid2 小时前
Room 数据库迁移与多表关联查询实战
android
吐了啊取名字太难2 小时前
Android 相机 USB 连接别写在 Activity 里:锁屏、切页和后台传输怎么设计?
android·数码相机
hunterandroid2 小时前
Android ViewModel + LiveData 工程实践:配置变更、一次性事件与状态恢复
android
2501_915921433 小时前
iOS应用从开发到上架App Store的完整步骤详解
android·ios·小程序·https·uni-app·iphone·webview
HackTwoHub4 小时前
免 Root 安卓渗透工具 Stryker|把 Kali 装进手机!随身无线渗透工作站,内置 Nmap/Nuclei/Metasploit 全套武器库
android·linux·运维·人工智能·web安全·macos·智能手机
朱涛的自习室14 小时前
70 美金的 Beyond Compare,我让 Gemini 4 和 DeepSeek 造个开源免费版(Day 2)
android·前端·人工智能
.Bryce.18 小时前
在智能体中使用A2A工具:让大模型自己决定“该找谁帮忙“
android·java·服务器·redis·后端·agent·a2a