织梦网站源码下载代理招商网免费加盟
织梦网站源码下载,代理招商网免费加盟,网站建设的平台分析,医院网站建设情况汇报1. 错误背景#xff1a;当 Moshi 撞上“双胞胎”
第一次把项目跑真机时#xff0c;Gradle 突然甩出一句 cause: duplicate entry: com/squareup/moshi/recordjsonadapter$1.class 打包流程直接中断。字面意思很直白#xff1a;同一个类被重复写进了 APK。 Moshi 从 1.14 开…1. 错误背景当 Moshi 撞上“双胞胎”第一次把项目跑真机时Gradle 突然甩出一句cause: duplicate entry: com/squareup/moshi/recordjsonadapter$1.class打包流程直接中断。字面意思很直白同一个类被重复写进了 APK。Moshi 从 1.14 开始给 JDK 16 的 Record 做了适配生成RecordJsonAdapter及其匿名内部类。如果两条依赖路径各自拉进了不同版本或相同版本但被不同构建缓存JAR 里就会出现两份.classDX/D8 在合并时就会炸锅。典型触发场景主工程显式依赖moshi:1.15.0某个二方库内部又依赖moshi:1.12.0混用moshi与moshi-kotlin后者自带moshi的runtime传递依赖多模块项目里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 { createMavenPublication(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% 的人都踩过的坑只在debug排除release忘记排除结果发到线上才崩溃——用configurations.all统一处理。用了force但二方库硬编码调用旧 API运行时NoSuchMethodError——先用./gradlew dependencyInsight确认兼容。Shading 时把moshi-adapters也 relocate 了导致 Kotlin 扩展找不到类——只 relocatemoshi核心库即可。混用moshi-kotlin-codegen与kapt注解处理器生成的JsonAdapter与反射冲突——保持同一版本且只选一种代码生成方式。升级 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 排坑技巧用上一举两得。