Android-OkHttp中Cookie.name()触发NullPointerException问题排查
问题场景
生产环境中应用触发NullPointerException,异常点为OkHttp内部BridgeInterceptor.cookieHeader()方法调用Cookie.name()时遇到null对象。代码采用Kotlin非空类型,自定义CookieJar仅保存OkHttp传入的有效Cookie,且OkHttp的Cookie.parseAll()会过滤null实例,但仍出现该异常,且无法稳定复现。
异常栈追踪
java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String okhttp3.Cookie.name()' on a null object reference at okhttp3.internal.http.BridgeInterceptor.cookieHeader(SourceFile:39)
关联代码片段
BridgeInterceptor中的cookieHeader方法实现:
private fun cookieHeader(cookies: List<Cookie>): String = buildString { cookies.forEachIndexed { index, cookie -> if (index > 0) append("; ") append(cookie.name).append('=').append(cookie.value) // 异常触发处 } }
注:该方法仅在BridgeInterceptor.intercept()中调用,Cookie来源为自定义CookieJar.loadForRequest()的返回值。
可能根源分析
自定义CookieJar线程安全问题
若保存Cookie的集合为非线程安全的可变容器(如普通MutableList),多线程并发调用saveFromResponse和loadForRequest时,可能出现集合结构被并发修改,导致loadForRequest返回的List中混入null元素。即使Kotlin的List是只读类型,若底层引用的是未同步的可变集合,仍可能出现此类问题。自定义CookieJar逻辑漏洞
若在saveFromResponse中对Cookie做自定义处理(如修改属性、转换格式),可能因逻辑疏漏生成null实例并存入集合;或未对OkHttp传入的Cookie列表做防御性过滤(尽管官方文档说明传入的Cookie均有效,但极端场景下可能存在边界case)。OkHttp 4.10.0版本潜在bug
尽管官方声称Cookie.parseAll()会过滤null,但特定边界场景(如异常格式的Set-Cookie头、特殊字符处理)下可能存在null泄漏,导致Cookie被传入CookieJar。
排查与修复建议
强化CookieJar线程安全
使用线程安全容器存储Cookie(如ConcurrentHashMap存储域名与Cookie列表的映射),在loadForRequest返回前将列表转换为不可变集合(如调用toList()),避免并发修改带来的异常。添加防御性过滤逻辑
在saveFromResponse中强制过滤null元素:override fun saveFromResponse(url: HttpUrl, cookies: List<Cookie>) { val validCookies = cookies.filterNotNull() // 后续存储逻辑 }在
loadForRequest返回前也添加过滤:override fun loadForRequest(url: HttpUrl): List<Cookie> { val storedCookies = // 从存储获取的Cookie列表 return storedCookies.filterNotNull() }生产环境现场日志采集
在loadForRequest方法中添加日志,记录当前返回的Cookie列表大小、是否包含null元素及请求URL等信息,便于异常发生时定位触发场景。升级OkHttp版本
若确认是版本bug,升级至OkHttp最新稳定版,此类底层NPE问题通常会被官方快速修复。
内容的提问来源于stack exchange,提问作者Ricardo Costeira

