java中用接口+实现类模拟回调实现异步通知,需防内存泄漏、生命周期错乱和线程安全问题,而非直接用completablefuture。

Java 里怎么用回调函数实现异步通知
Java 没有原生的“回调函数”语法,但用接口 + 实现类就能干净地模拟。核心不是写个 Runnable 就完事,而是让调用方能传入自己的逻辑,在异步任务完成时被触发。
常见错误是直接在异步线程里 new 一个匿名内部类去更新 UI 或修改共享变量,结果遇到空指针、并发修改或生命周期错乱——比如 Activity 已销毁,回调还在试图更新 View。
- 定义一个只含一个方法的接口,比如
OnResultListener,比用Consumer更明确语义 - 异步方法(如网络请求)接收该接口实例作为参数,不持有强引用,必要时用
WeakReference包一层防内存泄漏 - 回调执行前务必检查接收方是否还有效(例如 Android 中
isFinishing()或getActivity() != null) - 不要在回调里直接操作非线程安全的对象;UI 更新必须切回主线程,用
Handler、runOnUiThread()或LifecycleOwner.getLifecycle().addObserver()
为什么不能直接用 CompletableFuture 的 thenAccept
CompletableFuture 确实能链式处理结果,但它默认在 ForkJoinPool 中执行回调,不是你期望的 UI 线程,也不自带生命周期感知能力。
更麻烦的是:如果上游抛异常,thenAccept 不会捕获,整个链就静默失败;而自定义接口回调可以统一加 try-catch,把错误转成 onError(Throwable) 方法暴露出来。
立即学习“Java免费学习笔记(深入)”;
-
thenAccept只响应成功路径,异常需额外配exceptionally()或handle(),代码分散 - 无法自然绑定 Android Fragment / ViewModel 的生命周期,容易导致回调执行时目标已不可用
- 泛型擦除后类型信息丢失,不如接口方法签名清晰(比如
onSuccess(User user)比thenAccept(Object o)安全得多)
接口定义里要不要加 default 方法
加,但只加空实现,不加逻辑。比如定义 onProgress(int progress) 时给个 default 空体,调用方就不用强制实现所有方法。
别在 default 方法里写日志、Toast 或网络请求——那会让所有实现类被动继承副作用,违反“调用方控制行为”的原则。
- default 方法仅用于可选回调,避免子类编译报错
- 所有实际业务逻辑必须由使用者显式提供,接口只负责调度时机
- 如果未来要加新回调方法,用 default 填空比改所有实现类更轻量
Android 场景下最容易漏掉的三件事
不是 Java 语法问题,而是 Android 运行时特性带来的隐性约束。漏掉任何一条,都会在低概率下复现崩溃,且难以定位。
- 异步任务发起后,Activity/Fragment 可能已被系统回收重建,回调里的
this引用已失效,必须用WeakReference或通过ViewModel中转 - 配置变更(横竖屏)触发重建时,旧的回调对象没取消,新界面又发一次请求,导致重复回调或状态错乱
- 回调执行时 Context 已 detach(比如弹 Toast 报
android.view.WindowManager$BadTokenException),应优先用 Application Context 做非 UI 操作,UI 操作前先判空
真正难的从来不是“怎么写回调”,而是“什么时候不该执行它”。这个判断点,比接口定义本身重要得多。








