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
相关产品推荐
相关产品推荐

