You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Kotlin AAR回调中传递复杂数据结构的问题排查

AAR跨模块传递复杂数据结构异常:回调结果显示失败且字节数组为null

我用Kotlin编写了一个包含writeToDevicesList函数的AAR,该函数接收BLE设备列表、消息映射,执行消息发送后通过回调返回MutableMap<String, ArrayList<Pair<MyResultClass, ByteArray?>>>结构的结果集合。本地测试项目调用时回调返回数据完全正常,但打包为AAR在其他项目调用时,回调返回的Map键正确,不过列表中的结果均显示为失败且字节数组为null。AAR内部日志显示结果数据已正确填充,怀疑是复杂数据结构跨AAR传递的问题,想了解是否需要用序列化解决,寻求相关建议。

函数代码如下:

fun writeToDevicesList(context : Context,
                           bleDevicesList: ArrayList<BleDevice>,
                           sendingMap: MutableMap<String, ArrayList<Pair<String, ByteArray>>>,
                           writingCallback: (MutableMap<String, ArrayList<Pair<MyResultClass, ByteArray?>>>) -> Unit) {
    var resultsMap : MutableMap<String, ArrayList<Pair<MyResultClass, ByteArray?>>> = LinkedHashMap()
    // do a lot of stuff filling up the results map
    writingCallback?.let {
        it(resultsMap)
    }
}

问题原因分析

这种跨AAR模块的数据异常,核心原因大概率是类加载器隔离或数据结构的序列化/反序列化不一致:

  • 当AAR作为独立模块被其他项目引用时,MyResultClass可能在主项目和AAR中存在两份不同的类实例(即使代码完全一致),导致跨模块传递时对象无法正确识别,出现默认值(失败、null)。
  • 复杂嵌套的集合类型(MutableMap+ArrayList+Pair)在跨模块传递时,若没有明确的序列化约定,会依赖JVM的默认对象传递机制,一旦类加载器上下文不同,就会出现数据丢失或类型错误。

解决建议

1. 为自定义类实现序列化接口

针对MyResultClass这类自定义类,必须实现序列化接口,确保跨模块传递时对象能正确被解析:

  • Android环境优先选择Parcelable(性能更优):
@Parcelize
data class MyResultClass(/* 你的字段定义 */) : Parcelable
  • 也可以实现Java的Serializable接口(兼容性好但效率较低):
data class MyResultClass(/* 你的字段定义 */) : Serializable

2. 简化跨模块传递的数据结构

嵌套层级过深的集合会增加序列化复杂度,建议简化结构:

  • 用自定义数据类替代Pair,比如创建WriteResult类封装结果和字节数组:
@Parcelize
data class WriteResult(val result: MyResultClass, val data: ByteArray?) : Parcelable {
    override fun equals(other: Any?): Boolean {
        if (this === other) return true
        if (javaClass != other?.javaClass) return false
        other as WriteResult
        if (result != other.result) return false
        if (!data.contentEquals(other.data)) return false
        return true
    }

    override fun hashCode(): Int {
        var result1 = result.hashCode()
        result1 = 31 * result1 + data.contentHashCode()
        return result1
    }
}
  • 将回调的结果结构改为MutableMap<String, ArrayList<WriteResult>>,减少嵌套层级,提升序列化可靠性。

3. 确保共享类的全局可见性

  • 确保MyResultClass、BleDevice等自定义类的访问修饰符为public(Kotlin默认即为public,避免在内部包中定义)。
  • 建议将这些共享类单独抽成独立的Java/Kotlin Library模块,让AAR和主项目都依赖该公共模块,从根源避免类加载器隔离问题。

4. 验证数据传递的完整性

在AAR内部回调之前,对resultsMap做一次深拷贝(通过序列化后反序列化实现),确保传递出去的数据是独立的、可被外部模块正确解析的实例:

// 示例:基于Parcelable实现深拷贝
fun <T : Parcelable> deepCopy(obj: T): T {
    val parcel = Parcel.obtain()
    obj.writeToParcel(parcel, 0)
    parcel.setDataPosition(0)
    val copy = obj.javaClass.getDeclaredConstructor(Parcel::class.java).newInstance(parcel)
    parcel.recycle()
    return copy
}

// 在回调前处理结果集合
val copiedResults = resultsMap.mapValues { entry ->
    entry.value.map { deepCopy(it) } as ArrayList<WriteResult>
} as MutableMap<String, ArrayList<WriteResult>>
writingCallback(copiedResults)

内容的提问来源于stack exchange,提问作者Kibi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 04:18:15