你说的"空"不是空,你说的白是什么白?
前排广告位,欢迎访问我的个人网站: hixiaohezi.com
最近在开发一个混合 App 项目时,遇到了一个让我和安卓同事都摸不着头脑的问题。简单说就是:我在 H5 页面调用 JSBridge 方法时,明明传了参数,但安卓端却接收不到。
整个场景是这样的:在 App 的 WebView 中打开一个 H5 页面,页面上有个"关闭"按钮,点击后通过 JSBridge 调用原生的 closeH5 方法。由于业务需求,iOS 和 Android 需要不同的参数:
- iOS : 传空字符串
'' - Android : 传 JSON 字符串
'{"showAdPage": true}'(序列化后)
问题出现
代码写得很清晰,H5 端的逻辑大概是这样:
javascript
function handleClose() {
const isAndroid = /Android/i.test(navigator.userAgent);
const param = isAndroid
? JSON.stringify({ showAdPage: true })
: '';
console.log('调用 closeH5,参数:', param);
window.JSBridge.closeH5(param);
}
自测时,我在 Chrome DevTools 里打开了 WebView 调试,清清楚楚看到控制台打印:
json
调用 closeH5,参数: {"showAdPage":true}
一切正常。但是!安卓同事说他那边接收到的参数是空的。
我第一反应:怎么可能?
第一轮排查:你是不是没刷新?
这种"我这没问题,你那有问题"的情况,很容易陷入甩锅状态。为了避免无谓的争论,我先自查了一遍:
- 参数确实传了:控制台打印证据确凿
- JSBridge 调用正常:没有报错,方法确实被调用了
- Android 判断没错 :
isAndroid返回true,进了正确的分支
好,H5 端没问题。那是不是安卓那边的锅?
沟通的陷阱:什么叫"为空"?
这里有个小插曲值得单独说一下。最开始安卓同事跟我说"参数为空"的时候,我脑子里想的是空字符串 '',因为从前端视角看,"空"通常就是指空字符串或者null或者 undefined,但我传的是字符串所以我以为他说的"为空"指的就是空字符串。
但其实他说的"为空"指的是 null。
这个语义差异直接导致了我们沟通了好几个回合都没找到重点。我以为他接收到了空字符串,只是业务逻辑没处理好;他以为我没传参数,导致原生解析失败。
教训:跨端协作时,别用口头描述变量的值,直接发截图或者共享屏幕看代码。 "为空"、"没值"、"undefined" 这些词,在不同语言、不同上下文里含义都不一样,很容易造成信息不同步,反而浪费时间。
第二轮排查:现场办公
既然远程说不清楚,我直接走到安卓同事工位旁边,准备一起现场排查。
"来来来,你打印一下参数看看。"
他在原生代码里加了打印:
kotlin
fun closeH5(param: Any?) {
Log.d("JSBridge", "接收到的参数: $param")
// ...后续逻辑
}
我在手机上点击关闭按钮,H5 控制台:
json
调用 closeH5,参数: {"showAdPage":true}
Android Studio 的 Logcat:
csharp
接收到的参数: null
null???
这下我愣住了。
关键发现:null ≠ 空字符串
我盯着那个 null 看了两秒,突然意识到不对劲:
"等等,你这打印出来的是 null,不是空字符串 '' 吧?"
安卓同事:"对啊,是 null 啊。"
我:"那就不对了!如果我 H5 传的参数有问题,比如传了 undefined 或者啥都没传,你那边应该接收到空字符串 才对,怎么会是 null?Android 会自动把空字符串转成 null 吗?"
他愣了一下:"这......应该不会吧?"
这个发现很关键:如果是前端传参问题,原生接收到的应该是空字符串或其他值,而不是 null 。null 通常意味着变量压根没被赋值,或者类型不匹配导致解析失败。
找到真凶:类型声明的锅
我让他把接收参数的类型改一下试试:
kotlin
// 之前
fun closeH5(param: Any?) {
Log.d("JSBridge", "接收到的参数: $param")
}
// 改为
fun closeH5(param: String) {
Log.d("JSBridge", "接收到的参数: $param")
}
再跑一次,Logcat:
json
接收到的参数: {"showAdPage":true}
成功了!
复盘:为什么 Any? 会导致参数丢失?
后来我们分析了一下,可能的原因是:
- JSBridge 的参数传递机制:H5 传给原生的参数,本质上是通过 JSON 序列化后传递的字符串
- Kotlin 的
Any?类型过于宽泛 :当声明为Any?时,Kotlin 可能会尝试将接收到的 JSON 字符串解析为其他类型(对象、数组等),如果解析失败,就会返回null String类型明确了意图 :直接声明为String,告诉编译器"我就要字符串",就不会有歧义了
当然,这只是我们的推测,具体实现可能因 JSBridge 库而异。但教训是明确的:跨端通信时,类型声明要尽可能明确,避免使用过于宽泛的类型。
总结
这次 Bug 排查的收获:
- 看到预期外的值,先质疑假设 :
null和空字符串不一样,这个差异往往是关键线索 - 跨端问题,要看两端的代码:不能只看 H5 或只看原生,有时候问题出在"握手"环节
- 类型声明很重要:尤其在跨语言通信时,明确的类型能避免很多隐蔽的问题
- 现场排查比远程高效:有条件的话,坐一起看代码比发截图快多了
最后,提醒一下做混合开发的同学:当 H5 和原生的打印结果不一致时,别急着甩锅,先看看数据在两端的"形态"是否一致。 很多时候,问题出在中间的"翻译"环节。
一个看似简单的参数传递,背后藏着类型系统的细节。技术债往往就是这样一点点积累起来的。
欢迎访问我的个人网站: hixiaohezi.com
