ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Android工具类库选型指南:从utilcode到AndroidX的Gradle依赖实践

2026/10/2 20:26:29 拓冰建站 浏览量
Android工具类库选型指南:从utilcode到AndroidX的Gradle依赖实践 1. 从 utilcode 到 AndroidX一次真实的依赖选型踩坑记录如果你正在维护一个 2019 年前后启动的 Android 项目大概率会在build.gradle里看到com.blankj:utilcode这个依赖。它把 Activity 栈管理、SP 封装、屏幕适配、正则校验、文件读写这些零碎活儿全塞进一个库当年确实省事。但项目一旦升级到 AndroidX或者引入 Compose、Kotlin 协程、新版 AGP问题就来了utilcode依赖的是 support 库和 AndroidX 的android.useAndroidXtrue直接冲突编译期报Duplicate class android.support.v4.app.INotificationSideChannel是家常便饭。这篇内容聚焦一个很具体的场景在 Gradle 构建中筛选并集成 Android 工具类库对比 utilcode 与 AndroidX 生态的依赖配置差异。我会给出可直接复制的build.gradle片段、版本对齐检查清单以及依赖冲突的排查验证步骤。适合谁看适合那些项目里还挂着老工具库、想迁移又怕崩、或者新项目想选一套稳定工具库组合的 Android 开发者。核心检索词就是 Android 工具类库、Gradle 依赖、utilcode、AndroidX 这几个全文围绕它们展开。先说结论utilcode本身没有死作者提供了 AndroidX 版本utilcodex包名从com.blankj.utilcode变成com.blankj.utilcodexAPI 基本一致。但迁移不是改一行依赖那么简单Gradle 的依赖解析、传递依赖、版本对齐才是真正花时间的地方。我试过在一个 30 多个 module 的项目里做迁移光排查Duplicate class就花了大半天。下面把过程拆开讲。2. TaoToken 前置为什么工具库选型也要聊模型接入看到这个小标题你可能愣一下工具类库选型和模型接入有什么关系关系在于现在很多 Android 项目已经不只是纯客户端而是要在 App 里集成 AI 能力——比如智能客服、代码辅助、内容摘要。这些能力背后需要调用大模型 API而调用 API 就涉及 Base URL、API Key、Model ID 三件套的配置。工具库负责的是本地能力模型接入负责的是云端能力两者在同一个build.gradle和同一个settings.gradle里共存配置风格最好统一。我目前用的是 TaoToken 做模型接入层官网是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 入口是 https://taotoken.net/api 。它的好处是兼容 OpenAI 风格的接口Android 端用 OkHttp 或 Retrofit 直接发请求就行不需要额外 SDK。对于工具库选型来说这意味着你可以在utilcodex的LogUtils里打印模型请求日志用SPUtils存 API Key用NetworkUtils判断网络状态再决定是否发起请求——工具库和模型接入是能串起来的。具体到配置TaoToken 的接入需要三个东西Base URL、API Key、Model ID。Base URL 用https://taotoken.net/apiAPI Key 在控制台生成Model ID 根据你用的模型填。这三个值建议放在local.properties或gradle.properties里不要硬编码进代码。比如在gradle.properties里写TAOTOKEN_BASE_URLhttps://taotoken.net/api TAOTOKEN_API_KEYsk-你的key TAOTOKEN_MODEL_ID你的模型ID然后在build.gradle里通过buildConfigField注入到BuildConfig代码里用BuildConfig.TAOTOKEN_BASE_URL读取。这样工具库和模型配置都在 Gradle 体系内管理版本对齐和依赖排查的思路是一致的。如果你只是想先验证模型能不能通可以直接用模型对话页面 https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewrite 发一条测试消息确认 Base URL 和 Key 没问题再回到 Android 项目里集成。长期做编码和 Agent 的话Coding Plan 页面 https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite 有更详细的套餐说明。API Key 管理在 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite 接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 。这些链接先放着后面配置环节会用到。回到工具库本身。为什么要在选型阶段就考虑模型接入因为工具库的NetworkUtils、LogUtils、SPUtils会直接参与模型请求的调试和存储。如果工具库选得不好比如utilcode和 AndroidX 冲突导致编译不过那模型接入代码根本跑不起来。所以前置工作是把工具库依赖理顺再谈模型调用。3. 可复制配置utilcode 与 utilcodex 的 build.gradle 对照这一节是全文最核心的部分直接给可复制的配置片段。先看老项目的build.gradleModule 级别// 老项目support 库时代 dependencies { implementation com.blankj:utilcode:1.29.0 implementation com.android.support:appcompat-v7:28.0.0 implementation com.android.support:recyclerview-v7:28.0.0 }这个配置在android.useAndroidXfalse的时代没问题。但一旦在gradle.properties里打开android.useAndroidXtrueutilcode就会和 AndroidX 的类冲突。解决办法是换成utilcodex// 新项目AndroidX 时代 dependencies { implementation com.blankj:utilcodex:1.31.1 implementation androidx.appcompat:appcompat:1.6.1 implementation androidx.recyclerview:recyclerview:1.3.2 }注意版本号utilcodex最新稳定版是1.31.1比utilcode的1.29.0高。包名从com.blankj.utilcode变成com.blankj.utilcodex但类名和 API 基本没变。比如原来写com.blankj.utilcode.util.LogUtils现在写com.blankj.utilcodex.util.LogUtils。如果你不想改 import可以在build.gradle里加一行namespace映射但更推荐直接全局替换 import。gradle.properties里必须确认这两行android.useAndroidXtrue android.enableJetifiertrueandroid.enableJetifiertrue的作用是自动把第三方库里的 support 库引用转成 AndroidX。但注意utilcode本身没有提供 Jetifier 兼容的元数据所以即使开了 Jetifierutilcode还是可能冲突。这就是为什么必须换成utilcodex而不是靠 Jetifier 硬转。版本对齐检查清单我整理成表格检查项老配置新配置说明工具库坐标com.blankj:utilcodecom.blankj:utilcodex包名不同工具库版本1.29.01.31.1新版修复了 AndroidX 兼容问题support 库com.android.support:*移除全部换成 androidx.*useAndroidXfalsetruegradle.propertiesenableJetifierfalsetrue辅助转换但不能替代换库compileSdk2834建议对齐 AGP 版本AGP 版本3.x8.x新版 AGP 要求 namespace如果你项目里还有Cline MCP或Codex auth.json相关的配置注意它们和工具库不在一个层面但 Gradle 版本对齐的逻辑是一样的Base URL、Key、Model ID 三件套要写全。比如在local.properties里TAOTOKEN_BASE_URLhttps://taotoken.net/api TAOTOKEN_API_KEYsk-xxx TAOTOKEN_MODEL_IDxxx然后在build.gradle里读取def taotokenBaseUrl project.findProperty(TAOTOKEN_BASE_URL) ?: def taotokenApiKey project.findProperty(TAOTOKEN_API_KEY) ?: def taotokenModelId project.findProperty(TAOTOKEN_MODEL_ID) ?: android { buildTypes { debug { buildConfigField String, TAOTOKEN_BASE_URL, \${taotokenBaseUrl}\ buildConfigField String, TAOTOKEN_API_KEY, \${taotokenApiKey}\ buildConfigField String, TAOTOKEN_MODEL_ID, \${taotokenModelId}\ } } }这样配置的好处是工具库依赖和模型配置都在 Gradle 里版本对齐时一起检查不会漏。另外utilcodex的SPUtils可以用来存 API Key 的临时缓存LogUtils可以打印请求日志NetworkUtils可以判断网络状态。这些工具类在模型接入调试时很实用。还有一个细节utilcodex的AdaptScreenUtils在 AndroidX 下需要配合androidx.appcompat使用如果项目用了 Compose屏幕适配逻辑要单独处理。这不是依赖冲突但属于选型时要考虑的点。4. 验证请求编译通过 模型调用成功配置改完后第一步是验证 Gradle 能不能编译通过。在项目根目录执行./gradlew :app:assembleDebug --stacktrace如果报Duplicate class说明还有 support 库残留。用这个命令查依赖树./gradlew :app:dependencies --configuration debugCompileClasspath输出里搜com.android.support如果还有说明某个第三方库还在传递依赖 support 库。解决办法是在build.gradle里排除implementation(com.blankj:utilcodex:1.31.1) { exclude group: com.android.support }或者用resolutionStrategy强制替换configurations.all { resolutionStrategy.eachDependency { details - if (details.requested.group com.android.support) { details.useTarget androidx.${details.requested.name}:${details.requested.name}:1.0.0 } } }编译通过后第二步是验证模型调用。在 Android 代码里用 OkHttp 发一个请求到 TaoTokenval client OkHttpClient() val json { model: ${BuildConfig.TAOTOKEN_MODEL_ID}, messages: [{role: user, content: 你好}] } .trimIndent() val request Request.Builder() .url(${BuildConfig.TAOTOKEN_BASE_URL}/v1/chat/completions) .addHeader(Authorization, Bearer ${BuildConfig.TAOTOKEN_API_KEY}) .addHeader(Content-Type, application/json) .post(json.toRequestBody(application/json.toMediaType())) .build() client.newCall(request).enqueue(object : Callback { override fun onFailure(call: Call, e: IOException) { LogUtils.e(请求失败: ${e.message}) } override fun onResponse(call: Call, response: Response) { LogUtils.d(响应: ${response.body?.string()}) } })如果返回choices字段说明模型调用成功。如果报401检查 API Key 是否正确如果报local proxy failed检查网络配置如果报reading choices相关错误说明响应格式不对检查 Model ID 是否填错。验证成功后你可以在LogUtils里看到完整的请求和响应日志。utilcodex的LogUtils支持 JSON 格式化打印模型返回的 JSON 很清晰。这一步也顺便验证了工具库和模型接入的协同工作。5. 本篇常见错排查401、Duplicate class、reading choices这一节对照真实报错给出排查步骤。第一个高频错误是401 Unauthorized。报错信息通常是HTTP 401 Unauthorized {error: {message: Invalid API key, type: invalid_request_error}}原因API Key 没填、填错、或者过期。排查步骤打开 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite 确认 Key 状态检查gradle.properties里的TAOTOKEN_API_KEY有没有被local.properties覆盖检查BuildConfig里读到的值是不是空字符串。注意Key 不要提交到 Git放在local.properties里并加入.gitignore。第二个错误是Duplicate class。报错信息Duplicate class android.support.v4.app.INotificationSideChannel found in modules core-1.9.0-runtime (androidx.core:core:1.9.0) and support-compat-28.0.0-runtime (com.android.support:support-compat:28.0.0)原因support 库和 AndroidX 库同时存在。排查步骤执行./gradlew :app:dependencies查依赖树搜com.android.support找到传递依赖的库用exclude排除或者用resolutionStrategy强制替换。注意utilcode必须换成utilcodex光靠 Jetifier 不够。第三个错误是reading choices相关。报错信息com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_ARRAY but was BEGIN_OBJECT at line 1 column 2 path $.choices原因模型返回的 JSON 结构和解析代码不匹配。排查步骤先用LogUtils.json(response)打印原始响应确认choices是数组还是对象检查 Model ID 是否填错有些模型返回格式不同检查请求体里的messages格式是否正确。如果用的是utilcodex的GsonUtils注意fromJson的 Type 要匹配。第四个错误是local proxy failed。报错信息java.net.ConnectException: Failed to connect to /127.0.0.1:7890原因代码里配置了本地代理但代理没启动。排查步骤检查 OkHttp 是否设置了proxy检查gradle.properties里有没有systemProp.http.proxyHost如果不需要代理移除相关配置。注意Android 模拟器和真机的网络环境不同模拟器用10.0.2.2访问宿主机真机用局域网 IP。第五个错误是OAuth相关。报错信息OAuth token expired原因如果用的是 OAuth 方式接入token 过期了。排查步骤重新生成 token检查 token 的有效期如果是 Claude Code 或 Anthropic 相关接入确认 Base URL 和 Key 的配置方式。Claude Code 接入文档在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 里面有详细的配置说明。排查完这些错误后建议把utilcodex的CrashUtils初始化一下这样线上崩溃能捕获到模型请求相关的异常。初始化代码CrashUtils.init(object : CrashUtils.OnCrashListener { override fun onCrash(crashInfo: String?) { LogUtils.e(崩溃: $crashInfo) } })这样工具库和模型接入的异常都能统一收集。6. 语义一致 CTA工具库选型后的模型接入入口工具库选型理顺后下一步就是把模型接入跑通。如果你在排查依赖冲突或接入报错建议先看 API Keys 页面 https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi_keysutm_campaignrewrite 确认 Key 状态再看接入文档 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 对照配置。如果只是想验证模型能不能通直接用模型对话 https://taotoken.net/models?utm_sourcetaotoken_aicg_blog_endutm_contentmodel_chatutm_campaignrewrite 发一条消息最快。长期做编码和 Agent 的话Coding Plan https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding_planutm_campaignrewrite 有更完整的方案。控制台入口在 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite Claude Code 相关配置在 https://taotoken.net/claude-code?utm_sourcetaotoken_aicg_blog_endutm_contentclaude_codeutm_campaignrewrite 。最后说一个实用技巧utilcodex的SPUtils可以存模型请求的缓存结果CacheDoubleUtils可以做二级缓存减少重复请求。比如把模型返回的摘要缓存到磁盘下次直接读缓存。这样工具库和模型接入就真正串起来了不是两张皮。