news 2026/6/9 23:09:56

Moshi 重复类加载问题深度解析:从 ‘duplicate entry: com/squareup/moshi/recordjsonadapter$1.class‘ 错误到解决方案

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Moshi 重复类加载问题深度解析:从 ‘duplicate entry: com/squareup/moshi/recordjsonadapter$1.class‘ 错误到解决方案


1. 错误背景:当 Moshi 撞上“双胞胎”

第一次把项目跑真机时,Gradle 突然甩出一句
cause: duplicate entry: com/squareup/moshi/recordjsonadapter$1.class
打包流程直接中断。字面意思很直白:同一个类被重复写进了 APK。
Moshi 从 1.14 开始给 JDK 16+ 的 Record 做了适配,生成RecordJsonAdapter及其匿名内部类。如果两条依赖路径各自拉进了不同版本(或相同版本但被不同构建缓存),JAR 里就会出现两份.class,DX/D8 在合并时就会炸锅。
典型触发场景:

  • 主工程显式依赖moshi:1.15.0,某个二方库内部又依赖moshi:1.12.0
  • 混用moshimoshi-kotlin,后者自带moshiruntime传递依赖
  • 多模块项目里,A 模块api引入,B 模块implementation引入,版本未对齐

根因一句话:依赖树里同一坐标不同版本并存,且都携带了 Record 适配器代码

2. 技术方案对比:三把手术刀怎么选

方案思想优点缺点适用场景
依赖排除(exclude)把多余的那份直接踢出依赖树配置简单,APK 瘦身需要人工找出冲突源;升级后可能再次引入快速止血,单点冲突
强制版本(force / strict)统一强制解析到指定版本一次配置全局生效;Gradle 自动仲裁若旧库不兼容新 API 会运行时崩溃团队能统一版本管理
Shading(重定位)把类名整体搬家,物理隔离彻底避免冲突;可共存多版本构建耗时增加;调试堆栈变长SDK 厂商、二方库无法改源码时

3. 实现细节:直接能抄的 Gradle 片段

以下均以 Kotlin DSL 为例,Groovy DSL 把括号换成空格即可。

3.1 依赖排除

// build.gradle.kts dependencies { implementation("com.squareup.moshi:moshi:1.15.0") implementation("com.xxx:some-lib:3.2.1") { exclude(group = "com.squareup.moshi", module = "moshi") } }

验证命令:

./gradlew app:dependencies --configuration releaseRuntimeClasspath | grep moshi

确保只剩一条1.15.0

3.2 强制版本

// build.gradle.kts dependencyResolutionManagement { versionCatalogs { create("libs") { version("moshi", "1.15.0") library("moshi", "com.squareup.moshi", "moshi").versionRef("moshi") } } } configurations.all { resolutionStrategy.eachDependency { if (requested.group == "com.squareup.moshi" && requested.name == "moshi") { useVersion(libs.versions.moshi.get()) because("Align moshi to avoid duplicate RecordJsonAdapter") } } }

3.3 Shading(以 Shadow 插件为例)

plugins { id("com.github.johnrengelman.shadow") version "8.1.1" } shadowJar { archiveClassifier.set("shaded") relocate("com.squareup.moshi", "shaded.moshi") // 把 moshi 本身也打进去 from(project.sourceSets.main.get().output) configurations = listOf(project.configurations.runtimeClasspath.get()) } // 发布到本地仓库供其他模块依赖 publishing { publications { create<MavenPublication>("shadow") { artifact(shadowJar) artifactId = "my-moshi-runtime" } } }

主工程里直接依赖my-moshi-runtime即可,与业务代码零感知。

4. 性能考量:构建与运行双面看

  • 构建时间
    exclude/force 只做依赖解析,增量构建几乎无额外耗时;shade 需要重写字节码,全量打包增加 15~30 s(视 CPU 而定)。
  • APK 大小
    exclude/force 仅保留一份,体积最小;shade 会多拷贝一份 relocated 类,增加 300-400 KB。
  • 运行时
    前两种方案对启动速度无影响;shade 因类名变长,首次加载反射略慢(<5 ms),可忽略。
  • 维护成本
    shade 需要单独发布阴影包,CI 流程复杂;exclude/force 只需代码审查阶段保证版本一致即可。

5. 避坑指南:90% 的人都踩过的坑

  1. 只在debug排除,release忘记排除,结果发到线上才崩溃——用configurations.all统一处理。
  2. 用了force但二方库硬编码调用旧 API,运行时NoSuchMethodError——先用./gradlew dependencyInsight确认兼容。
  3. Shading 时把moshi-adapters也 relocate 了,导致 Kotlin 扩展找不到类——只 relocatemoshi核心库即可。
  4. 混用moshi-kotlin-codegenkapt,注解处理器生成的JsonAdapter与反射冲突——保持同一版本且只选一种代码生成方式。
  5. 升级 Android Gradle Plugin 后缓存未清,依旧报 duplicate——./gradlew clean并删除.gradle/caches/transforms-3

6. 进阶建议:把冲突扼杀在摇篮里

  • buildSrc写一份版本清单,所有模块统一引用,禁止硬编码数字。
  • CI 里加一条任务:解析依赖树并输出到 PR 评论,方便 Code Review 一眼看到新增库。
  • 使用 GradlefailOnVersionConflict()在本地提前失败,强制开发者显式解决。
  • 对二方 SDK 要求提供exclude-moshi的 pom 配置,或让厂商直接 shade。
  • 定期跑./gradlew dependencyUpdates,把整条网络保持最新,减少“旧+新”组合概率。

7. 小结与思考

依赖冲突不是编译器故意找茬,而是模块化生态的副作用。
Moshi 的RecordJsonAdapter只是冰山一角,今天遇到 duplicate,明天可能是 Okio、Kotlin Stdlib。
与其每次救火,不如把“版本仲裁策略”写进团队规范:统一入口、自动检测、渐进升级。
当你能一眼从依赖树里揪出那只“双胞胎”,就已经走在高质量交付的路上了。


写完这篇排查笔记,我又顺手把团队里的语音对话 Demo 升级了依赖——没错,就是那个用火山引擎豆包实时语音模型的小玩具。
如果你想亲手搭一个会“听、想、说”的 AI 伙伴,又不想被依赖冲突绊住脚,可以戳这个动手实验:从0打造个人豆包实时通话AI。
我按文档跑了一遍,半小时就能在浏览器里跟虚拟角色聊起来,顺便还能把今天学到的 Moshi 排坑技巧用上,一举两得。


版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/6 22:06:03

AI 辅助开发实战:高效完成本科毕业设计的技术路径与避坑指南

背景痛点&#xff1a;毕设三座大山 大四下学期&#xff0c;时间被实习、考研、面试切成碎片&#xff0c;还要在三个月内交付一份“像样”的本科毕业设计。多数人第一次独立完成完整工程&#xff0c;痛点高度相似&#xff1a; 选题时只有一句话&#xff1a;“做个图书管理系统…

作者头像 李华
网站建设 2026/6/7 1:28:37

CozeStudio进阶指南:多模态与知识库功能深度配置

1. CozeStudio多模态与知识库功能概述 在AI应用开发领域&#xff0c;处理图片、文档等非结构化数据一直是技术难点。CozeStudio作为一站式AI智能体开发平台&#xff0c;通过多模态文件上传与知识库组件&#xff0c;为企业级应用提供了完整的解决方案。我曾在一个电商客服项目中…

作者头像 李华