惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

GbyAI
GbyAI
D
DataBreaches.Net
博客园 - 三生石上(FineUI控件)
H
Hacker News: Front Page
Know Your Adversary
Know Your Adversary
Recorded Future
Recorded Future
The Hacker News
The Hacker News
Help Net Security
Help Net Security
月光博客
月光博客
L
LINUX DO - 热门话题
Hacker News - Newest:
Hacker News - Newest: "LLM"
T
Tor Project blog
Security Archives - TechRepublic
Security Archives - TechRepublic
aimingoo的专栏
aimingoo的专栏
Attack and Defense Labs
Attack and Defense Labs
Project Zero
Project Zero
V
Vulnerabilities – Threatpost
SecWiki News
SecWiki News
S
Security @ Cisco Blogs
Blog — PlanetScale
Blog — PlanetScale
V2EX - 技术
V2EX - 技术
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
A
Arctic Wolf
T
Threat Research - Cisco Blogs
WordPress大学
WordPress大学
H
Heimdal Security Blog
小众软件
小众软件
C
Check Point Blog
T
Tailwind CSS Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
C
Cyber Attacks, Cyber Crime and Cyber Security
Vercel News
Vercel News
云风的 BLOG
云风的 BLOG
Last Week in AI
Last Week in AI
L
LangChain Blog
博客园 - Franky
Martin Fowler
Martin Fowler
MongoDB | Blog
MongoDB | Blog
P
Proofpoint News Feed
T
The Exploit Database - CXSecurity.com
P
Palo Alto Networks Blog
J
Java Code Geeks
Apple Machine Learning Research
Apple Machine Learning Research
C
Cybersecurity and Infrastructure Security Agency CISA
C
CXSECURITY Database RSS Feed - CXSecurity.com
Microsoft Security Blog
Microsoft Security Blog
Google DeepMind News
Google DeepMind News
有赞技术团队
有赞技术团队
MyScale Blog
MyScale Blog
S
Schneier on Security

博客园 - y丶innocence

临时111 RecyclerView 数据多时无法滑动:ConstraintLayout 约束高度修复笔记 Android Kotlin OkHttp3 WebSocket 长连接与 Gson 数据解析系统笔记 Android + Kotlin + OkHttp WebSocket 相关概念与使用流程笔记(TLS/证书 + 鉴权/会话) AI对话导出markdown格式流程 分享文件 面向全球的app的excel导出和kotlin IO原理 安卓导出笔记(未整理) java&kotlin listener 0.7 动画 0.4 View 工作流程 0.3 view 滑动冲突 13. Jetpack 0. 安卓开发艺术探索参考资料 12. Material Design 7. 持久化技术 5. Fragment java 基础 4. UI 开发 3. Activity 2.2 Kotlin 面向对象 2.3 Kotlin高级 2.1 Kotlin基础 1. Android简介 [OpenJudge] 反正切函数的应用 (枚举)(数学) [OpenJudge] 摘花生 (模拟)
代理转发
y丶innocence · 2026-04-28 · via 博客园 - y丶innocence

Android WebSocketListenermProxyListener 代理转发写法理解

这篇笔记用于理解:在 Android OkHttp WebSocket 封装中,为什么常见写法是内部维护一个 wsListener,同时再保存一个外部传入的 mProxyListener

核心结论:

OkHttp -> 内部 wsListener -> mProxyListener -> 外部业务逻辑

wsListener 负责封装层统一处理,mProxyListener 负责把事件继续交给业务层。

1. 场景与角色

OkHttp 创建 WebSocket 时,需要传入一个 WebSocketListener

client.newWebSocket(request, listener)

这个 listener 会在连接打开、收到消息、连接失败、关闭等时机被 OkHttp 回调。

如果你自己封装了一个 WebSocketClient,通常会有两类监听器:

  • wsListener:封装层内部监听器,真正传给 OkHttp。
  • mProxyListener:外部业务层传入的监听器,保存在封装类内部。

也就是说,OkHttp 并不会直接回调业务层的 listener,而是先回调封装层的 wsListener

2. 回调链路

完整链路如下:

OkHttp WebSocket 事件
        |
        v
内部 wsListener
        |
        | 先做封装层统一逻辑
        | 例如日志、状态管理、重连、心跳、线程切换
        v
mProxyListener
        |
        | 再交给外部业务层
        v
业务逻辑

所以 mProxyListener 的本质是一个“外部回调出口”。

3. Kotlin 抽象示例

import android.util.Log
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import okhttp3.WebSocket
import okhttp3.WebSocketListener
import okio.ByteString

class MyWebSocketClient {

    companion object {
        private const val TAG = "MyWebSocketClient"
    }

    // 外部业务层注入的 listener。
    private var mProxyListener: WebSocketListener? = null

    // 内部 listener:真正传给 OkHttp。
    private val wsListener: WebSocketListener = object : WebSocketListener() {

        override fun onOpen(webSocket: WebSocket, response: Response) {
            // 1. 封装层统一逻辑
            Log.d(TAG, "connect open")

            // 2. 转发给外部业务层
            mProxyListener?.onOpen(webSocket, response)
        }

        override fun onMessage(webSocket: WebSocket, text: String) {
            Log.d(TAG, "receive text message")
            mProxyListener?.onMessage(webSocket, text)
        }

        override fun onMessage(webSocket: WebSocket, bytes: ByteString) {
            Log.d(TAG, "receive binary message")
            mProxyListener?.onMessage(webSocket, bytes)
        }

        override fun onClosing(webSocket: WebSocket, code: Int, reason: String) {
            Log.d(TAG, "connect closing: code=$code, reason=$reason")
            mProxyListener?.onClosing(webSocket, code, reason)
        }

        override fun onClosed(webSocket: WebSocket, code: Int, reason: String) {
            Log.d(TAG, "connect closed: code=$code, reason=$reason")
            mProxyListener?.onClosed(webSocket, code, reason)
        }

        override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
            Log.e(TAG, "connect failure", t)
            mProxyListener?.onFailure(webSocket, t, response)
        }
    }

    fun setListener(listener: WebSocketListener?) {
        mProxyListener = listener
    }

    fun connect(client: OkHttpClient, request: Request) {
        // OkHttp 实际回调的是内部 wsListener。
        client.newWebSocket(request, wsListener)
    }
}

4. 外部业务层怎么使用

业务层只需要传入自己的监听器,实现真正关心的逻辑:

val client = MyWebSocketClient()

client.setListener(object : WebSocketListener() {

    override fun onOpen(webSocket: WebSocket, response: Response) {
        // 业务逻辑:例如通知 UI 已连接、发送鉴权消息等。
    }

    override fun onMessage(webSocket: WebSocket, text: String) {
        // 业务逻辑:例如解析消息、分发到业务模块等。
    }

    override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
        // 业务逻辑:例如提示错误、上报异常等。
    }

    override fun onClosed(webSocket: WebSocket, code: Int, reason: String) {
        // 业务逻辑:例如更新连接状态、释放资源等。
    }
})

业务层不需要知道封装层内部如何管理日志、心跳、重连,只需要关心自己的业务事件。

5. 为什么不直接把业务 listener 传给 OkHttp

如果直接把外部业务 listener 传给 OkHttp,封装层就失去了统一拦截点。

使用内部 wsListener 再转发给 mProxyListener,可以在转发前后插入通用能力:

  • 统一日志记录。
  • 统一连接状态管理。
  • 自动重连。
  • 心跳检测。
  • 鉴权流程。
  • 线程切换,例如从 OkHttp 回调线程切到主线程再通知 UI。
  • try-catch 防御,避免业务回调抛异常影响封装层。

因此,这种写法本质上是代理模式或装饰器模式:

外部业务 listener 没有直接交给 OkHttp,
而是被封装层保存起来,
由内部 wsListener 在合适的时候调用它。

6. super.onXxx(...) 通常有没有必要

大多数情况下没有必要。

OkHttp 的 WebSocketListener 默认实现通常是空实现,也就是 no-op:

override fun onOpen(webSocket: WebSocket, response: Response) {
    super.onOpen(webSocket, response) // 通常可以省略
}

所以在 Kotlin 或 Java 中,通常可以直接省略 super.onOpen(...)super.onMessage(...)super.onFailure(...) 等调用。

7. 与 C++ 的对应关系

可以把 Kotlin/Java 的 listener 理解成 C++ 中保存的一组 callback。

Kotlin / Java C++
WebSocketListener 一组回调函数,或一个 listener 接口类
object : WebSocketListener() { ... } lambda、仿函数、匿名实现
mProxyListener 保存用户传入的 callback 成员变量
内部 wsListener 先处理再转发 包装一层 callback,先做内部逻辑,再调用用户 callback

8. C++ std::function 对照示例

#include <functional>
#include <iostream>
#include <string>

struct Callbacks {
  std::function<void()> onOpen;
  std::function<void(const std::string&)> onMessage;
};

class Client {
  Callbacks internal_; // 内部统一入口,相当于 wsListener。
  Callbacks proxy_;    // 用户注入,相当于 mProxyListener。

public:
  Client() {
    internal_.onOpen = [this] {
      std::cout << "connect open\n";      // 内部统一逻辑。

      if (proxy_.onOpen) {
        proxy_.onOpen();                  // 转发给用户。
      }
    };

    internal_.onMessage = [this](const std::string& message) {
      std::cout << "receive message: " << message << '\n';

      if (proxy_.onMessage) {
        proxy_.onMessage(message);
      }
    };
  }

  void setListener(const Callbacks& callbacks) {
    proxy_ = callbacks;
  }
};

这里的关系和 Kotlin 版本一致:

底层事件 -> internal_ -> proxy_ -> 用户逻辑

9. 一句话记忆

mProxyListener 就是业务层 listener 的保存位置。

内部 wsListener 负责拦截 OkHttp 的回调,先做封装层通用逻辑,再调用 mProxyListener,把事件转发给外部业务层。