2026-07-17 13:35:27 +08:00
|
|
|
|
# Android SDK 重构实施接管文档
|
|
|
|
|
|
|
2026-07-27 09:46:13 +08:00
|
|
|
|
> 更新时间:2026-07-26
|
2026-07-17 13:35:27 +08:00
|
|
|
|
> 分支:`main`
|
|
|
|
|
|
> 发布约束:Maven 制品只允许通过 `https://jenkins.xuqinmin.com/` 发布。
|
|
|
|
|
|
|
|
|
|
|
|
## 当前目标
|
|
|
|
|
|
|
|
|
|
|
|
- `sdk-core` 是配置、会话、网络、文件、时间等公共能力的唯一实现。
|
|
|
|
|
|
- `sdk-update` 只提供更新领域 API;宿主与 RN 桥接不复制下载、校验和安装代码。
|
|
|
|
|
|
- APK 更新必须支持 SHA-256、原子临时文件、进度、取消、有限重试、FileProvider、未知来源安装权限及确定错误码。
|
|
|
|
|
|
|
2026-07-27 11:44:24 +08:00
|
|
|
|
## 2026-07-27 sdk-file 与宿主 FileProvider 隔离
|
|
|
|
|
|
|
|
|
|
|
|
- `sdk-file` 不再直接把 `androidx.core.content.FileProvider` 注册进宿主 Manifest,
|
|
|
|
|
|
改为注册唯一的 `XuqmFileProvider` 子类。
|
|
|
|
|
|
- Android Manifest 可能按 Provider 类名合并节点;宿主已有 `FileProvider` 时,
|
|
|
|
|
|
不同 authority 会在合并阶段冲突。
|
|
|
|
|
|
- authority 和公开文件 API 保持不变,宿主不需要 `tools:replace` 或新增 Manifest
|
|
|
|
|
|
配置。
|
|
|
|
|
|
|
2026-07-17 13:35:27 +08:00
|
|
|
|
## 2026-07-17 已完成
|
|
|
|
|
|
|
|
|
|
|
|
- `FileSDK` 新增详细下载进度、SHA-256 与哈希匹配公共 API。
|
|
|
|
|
|
- 下载改为 `.part` 临时文件完成后替换目标文件;HTTP 非 2xx 失败,协程取消会中断连接并清理临时文件。
|
2026-07-27 09:46:13 +08:00
|
|
|
|
- 当时的 core FileProvider 归属已被后续终态替代:当前仅 `sdk-file` 声明,core 零 Manifest。
|
2026-07-17 13:35:27 +08:00
|
|
|
|
- `sdk-update` 删除无版本 APK 兼容路径和弃用下载入口。
|
|
|
|
|
|
- `sdk-update` 2.0.0-SNAPSHOT 强制要求合法 SHA-256,并提供下载重试、取消、缓存文件复验和确定错误码。
|
|
|
|
|
|
- 增加 Java/RN 可直接使用的 `startDownloadAndInstall`、`UpdateInstallTask`、进度/结果回调和安装权限设置 Intent。
|
|
|
|
|
|
- `sdk-core`、`sdk-update`、`sample-app` 编译通过。
|
2026-07-27 09:46:13 +08:00
|
|
|
|
- 收敛 update/push/im/sample 的空 package/resource 目录;源码树禁止保留空目录或
|
|
|
|
|
|
`.gitkeep` 占位。
|
2026-07-18 08:28:33 +08:00
|
|
|
|
- 清理后 `:sdk-core:testDebugUnitTest :sdk-update:testDebugUnitTest` 重新执行并通过。
|
2026-07-17 13:35:27 +08:00
|
|
|
|
- `sdk-core` 1 个单元测试、`sdk-update` 2 个单元测试通过。
|
|
|
|
|
|
- 修复通知权限检查、MediaStore API 等级和 `java.time` minSdk 24 反向移植,core/update Release Lint 均通过。
|
|
|
|
|
|
|
2026-07-18 08:28:33 +08:00
|
|
|
|
## 2026-07-18 RN 宿主网络兼容修复
|
|
|
|
|
|
|
|
|
|
|
|
- App4 真机链路发现 `sdk-core:1.1.6-SNAPSHOT` 将 OkHttp 提升到 5.3.2,
|
|
|
|
|
|
与 React Native 0.86 的 `okhttp-urlconnection:4.9.2` 混装后会在首次请求时
|
|
|
|
|
|
因 `okhttp3.internal.Util` 缺失导致原生进程崩溃。
|
|
|
|
|
|
- `sdk-core` 统一使用 OkHttp BOM 4.12.0;这是 RN 0.86 当前可用的最新 4.x
|
|
|
|
|
|
稳定兼容线,不允许下游单独提升某个 OkHttp 组件到 5.x。
|
|
|
|
|
|
- `check` 增加 `verifyNetworkDependencyAlignment`,发布前强制确认所有
|
|
|
|
|
|
`com.squareup.okhttp3` 组件版本一致。
|
|
|
|
|
|
- `sdk-update` 的 Gson 改为复用版本目录定义,删除模块内的重复版本事实来源。
|
|
|
|
|
|
|
2026-07-27 09:46:13 +08:00
|
|
|
|
## 后续外部验收
|
2026-07-17 13:35:27 +08:00
|
|
|
|
|
2026-07-27 09:46:13 +08:00
|
|
|
|
1. 从租户平台下载与 Sample 包名匹配的真实 `config.xuqmconfig`,再执行 Sample
|
|
|
|
|
|
Debug/Release 整包和仪器测试。
|
|
|
|
|
|
2. 在目标宿主中验证下载取消、错误码、权限跳转和安装器唤起。
|
|
|
|
|
|
3. 发布只允许通过 Jenkins 执行;当前开发机未发布任何 Maven 制品。
|
2026-07-17 13:35:27 +08:00
|
|
|
|
|
|
|
|
|
|
## 已验证命令
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
./gradlew :sdk-core:compileReleaseKotlin :sdk-update:compileReleaseKotlin :sample-app:compileDebugKotlin
|
|
|
|
|
|
./gradlew :sdk-core:testDebugUnitTest :sdk-update:testDebugUnitTest :sample-app:compileDebugKotlin
|
|
|
|
|
|
./gradlew :sdk-core:lintRelease :sdk-update:lintRelease :sdk-update:assembleRelease
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
不得在开发机执行 Maven 发布。
|
2026-07-27 09:46:13 +08:00
|
|
|
|
|
|
|
|
|
|
## 2026-07-26 初始化、Update 与 BugCollect 收敛
|
|
|
|
|
|
|
|
|
|
|
|
- `sdk-core` 新增唯一初始化状态机:`IDLE / INITIALIZING / READY / DEGRADED / FAILED`,
|
|
|
|
|
|
对外失败使用 `XuqmSdkException + XuqmErrorCode`。
|
|
|
|
|
|
- 平台配置以 `appKey + packageName + platformUrl` 隔离,使用加密 LKG 保存七天;
|
|
|
|
|
|
远端配置最多执行三次指数退避重试,有有效 LKG 时降级运行,否则只停用扩展能力。
|
|
|
|
|
|
- 初始化文件唯一位置为 `assets/config/config.xuqmconfig`。运行时只接受
|
|
|
|
|
|
`XUQM-CONFIG-V2`:Ed25519 签名覆盖前五段 ASCII,先验签、后 AES 解密,
|
|
|
|
|
|
canonical JSON、schema、UUID、revision 和 RFC3339 时间均强校验;V1 与未知 keyId 拒绝。
|
2026-07-27 10:42:55 +08:00
|
|
|
|
- 已同时内置开发签发公钥 `xuqm-config-dev-2026-07` 与生产签发公钥
|
|
|
|
|
|
`xuqm-config-prod-2026-07`;私钥只保存在 Jenkins 凭据库和服务端运行环境,
|
|
|
|
|
|
不属于 SDK 仓库或发布制品。
|
2026-07-27 09:46:13 +08:00
|
|
|
|
- `BugCollect` 必须同时满足平台启用、隐私已同意、上传地址有效。关闭时恢复宿主原
|
|
|
|
|
|
UncaughtExceptionHandler 并删除队列;服务端 `BUGCOLLECT_DISABLED` 会形成持久停用锁,
|
|
|
|
|
|
仅新的成功平台配置可解除。
|
|
|
|
|
|
- error/fatal 加密持久化,最多 500 条且保留七天;普通事件仅内存。后台上传最多
|
|
|
|
|
|
重试三次并加入抖动,不向宿主线程抛错;统一脱敏入口在入队前清理敏感键、手机号、
|
|
|
|
|
|
身份证号和 Bearer Token。
|
|
|
|
|
|
- Update 使用平台动态 `updateRequiresLogin`:未登录正常跳过;登录后同一会话自动补检
|
|
|
|
|
|
一次;退出或切换用户会取消旧任务并清除灰度检查缓存,回调统一切回主线程。
|
|
|
|
|
|
- BugCollect Gradle 插件拒绝 V1,Release 读取同一 V2 文件;支持
|
|
|
|
|
|
`-Pxuqm.bugcollect=disabled` 应急构建,此时保留本地 mapping 产物但不访问上传服务。
|
|
|
|
|
|
- Android R8 `mapping.txt` 通过 `POST /bugcollect/v1/artifacts/upload` 以
|
|
|
|
|
|
`artifactType=R8_MAPPING` 上传,并携带 appKey、platform、appVersion、buildId 与文件
|
|
|
|
|
|
SHA-256。它不是 RN Source Map,不改名为 `.map`,也不伪造 module/bundle 身份。
|
|
|
|
|
|
上传使用构建机 `XUQM_API_TOKEN` Bearer 认证;令牌不进入配置、任务输入快照或日志。
|
|
|
|
|
|
平台启用 BugCollect 但缺少令牌时 Release 明确失败;应急 disabled 构建不要求令牌。
|
|
|
|
|
|
- 所有 Release(无论是否开启混淆)都会在打包前调用
|
|
|
|
|
|
`POST /api/sdk/build/config/validate`,提交原始签名配置和最终 applicationId。服务不可达、
|
|
|
|
|
|
配置撤销、过期或包名不匹配都会阻止打包;应急关闭 BugCollect 也不能绕过配置校验。
|
|
|
|
|
|
- 删除不再参与 Manifest 的 `BugCollectInitProvider`,避免扩展模块平行初始化。
|
|
|
|
|
|
- 删除公开手动初始化与旧 Provider 兼容类。`sdk-core` 单独集成不注册 Provider、
|
|
|
|
|
|
FileProvider 或通知权限;需要平台能力的扩展统一合并同一个 `XuqmMergedProvider`。
|
|
|
|
|
|
FileProvider 归唯一 `sdk-file`,通知权限归 `sdk-push`。
|
|
|
|
|
|
- Sample 删除硬编码 appKey,只接受被 Git 忽略的租户平台签发配置;缺少文件时 Debug/
|
|
|
|
|
|
Release 构建门禁给出明确错误。Release 签名只从用户级 Gradle 属性读取。
|
|
|
|
|
|
- 敏感存储收敛为唯一 `SecureStore`:Android Keystore 不可导出 AES-256-GCM 密钥,
|
|
|
|
|
|
namespace/key 作为 AAD;删除废弃 security-crypto 依赖。
|
|
|
|
|
|
- `sdk-webview/XWebViewView.kt` 的既有未提交修改始终作为用户基线保留;本轮只在该基线
|
|
|
|
|
|
上将打开文件能力切换到唯一 `PlatformFileSDK`,未覆盖其它改动。
|
|
|
|
|
|
|
|
|
|
|
|
本轮最终已验证:
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
./gradlew --no-daemon -Dkotlin.compiler.execution.strategy=in-process \
|
|
|
|
|
|
:sdk-core:testDebugUnitTest \
|
|
|
|
|
|
:sdk-bugcollect:testDebugUnitTest \
|
|
|
|
|
|
:sdk-update:testDebugUnitTest \
|
|
|
|
|
|
:sdk-bugcollect-plugin:test \
|
|
|
|
|
|
:sdk-core:lintRelease \
|
|
|
|
|
|
:sdk-bugcollect:lintRelease \
|
|
|
|
|
|
:sdk-update:lintRelease
|
|
|
|
|
|
|
|
|
|
|
|
./gradlew --no-daemon \
|
|
|
|
|
|
:sdk-core:assembleRelease \
|
|
|
|
|
|
:sdk-bugcollect:assembleRelease \
|
|
|
|
|
|
:sdk-update:assembleRelease \
|
|
|
|
|
|
:sdk-bugcollect-plugin:assemble \
|
|
|
|
|
|
verifySdkManifestOwnership
|
|
|
|
|
|
|
|
|
|
|
|
./gradlew :sample-app:compileDebugKotlin \
|
|
|
|
|
|
-x :sample-app:validateSampleSdkConfig
|
|
|
|
|
|
|
|
|
|
|
|
./gradlew :sample-app:compileDebugAndroidTestKotlin \
|
|
|
|
|
|
-x :sample-app:validateSampleSdkConfig
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Sample 源码编译时只为静态验证跳过真实签发文件的存在性门禁;正常执行
|
|
|
|
|
|
`:sample-app:validateSampleSdkConfig` 已确认会以明确错误阻止缺配置构建。合并 Manifest
|
|
|
|
|
|
静态结果确认多个扩展最终只有一个初始化 Provider。
|
|
|
|
|
|
|
|
|
|
|
|
尚未执行 Jenkins、Maven 发布、真机仪器测试或带真实租户配置的 Sample 整包。在线校验的不可达、HTTP 失败、
|
|
|
|
|
|
`CONFIG_REVOKED`、`CONFIG_EXPIRED`、`PACKAGE_MISMATCH` 和成功响应已有单元测试覆盖;
|
|
|
|
|
|
服务端真实生成的 V2 固定向量已在 Android 端完成验签、解密和 canonical JSON 精确
|
|
|
|
|
|
比对。R8 multipart 上传字段、Bearer Header、SHA-256、`.txt` 文件名与令牌不进入请求
|
|
|
|
|
|
正文均有本地 HTTP 契约测试。
|
|
|
|
|
|
|
|
|
|
|
|
## 2026-07-26 审计后终态修复
|
|
|
|
|
|
|
|
|
|
|
|
- 新增唯一 `com.xuqm.common` 构建插件:扫描宿主 `src/**`,所有变体只接受
|
|
|
|
|
|
`src/main/assets/config/config.xuqmconfig`;改名、嵌套或其它 sourceSet 中出现任何
|
|
|
|
|
|
`.xuqmconfig` 都会失败。Debug
|
|
|
|
|
|
执行本地 V2 验签,Release 额外在线校验。buildId 与运行时
|
|
|
|
|
|
BugCollect 构建门禁均归此插件;可选 BugCollect 插件只上传 R8 mapping。
|
|
|
|
|
|
- 解密后的 `serverUrl` 是唯一且必填的平台地址;已删除旧业务默认地址和运行时回退,
|
|
|
|
|
|
配置缺失或地址为空时初始化明确失败。
|
|
|
|
|
|
- 删除 `configureServiceEndpoints`、`useExternalServiceEndpoints`、
|
|
|
|
|
|
`useLocalServiceEndpoints` 及 Sample 的环境设置页面、路由和持久化。服务端点仅可由
|
|
|
|
|
|
签发配置的 `serverUrl` 与该平台远程配置产生;远程字段缺失时只回退到同一
|
|
|
|
|
|
`serverUrl`,不会切换平台。
|
|
|
|
|
|
- Release buildId 优先读取 Jenkins/CLI 的 `XUQM_BUILD_ID`;本地缺失时使用 UUID。
|
|
|
|
|
|
为防止 Gradle configuration cache 复用随机身份,启用配置缓存的 Release 强制要求显式 buildId。
|
|
|
|
|
|
- `-Pxuqm.bugcollect=disabled` 是不可覆盖的构建禁用,同时关闭运行时采集/上传与
|
|
|
|
|
|
mapping 上传;mapping 仍保留为本地产物。Debug 只关闭自动采集,可调试宿主在构建未
|
|
|
|
|
|
禁用、隐私同意、平台启用时可通过隐藏开发入口显式发送一次测试事件。
|
|
|
|
|
|
- common 插件按变体生成唯一 Manifest 标记、buildId 与 BugCollect 构建开关;扩展 AAR
|
|
|
|
|
|
的合并 Provider 未检测到该标记时只退出,不执行初始化,core-only 仍保持零 Manifest。
|
|
|
|
|
|
- Update 先服从平台 `features.update`,相同用户重复同步不会再次补检;下载与安装授权
|
|
|
|
|
|
绑定当前 generation 和 userId,切换或登出会取消活动下载、清除授权并阻止旧灰度结果安装。
|
|
|
|
|
|
- BugCollect 队列 flush 使用 Mutex 单飞,定时与满批触发不会重复上传同一批数据。
|
|
|
|
|
|
- 内部 BugCollect 状态桥接改为私有反射 SPI,不进入 Java/Kotlin 公开 API。
|
|
|
|
|
|
- `sdk-core` 保持零 Manifest,只保留无需初始化的纯本地文件能力。新增唯一 `sdk-file`
|
|
|
|
|
|
扩展承载租户平台上传与 FileProvider 打开;IM、Update、WebView 复用该扩展。
|
|
|
|
|
|
- `sdk-file` 的 Uri、File、ByteArray 三个上传入口统一先等待平台初始化,再创建请求体
|
|
|
|
|
|
和读取服务端点;平台失败统一抛出保留 `code/status/message` 且不暴露响应体的
|
|
|
|
|
|
`PlatformFileException`。`openFile` 仍为不依赖初始化的纯本地能力。
|
|
|
|
|
|
- 全仓日志、标准输出和异常文案不再输出 token、userId、groupId、消息目标、H5
|
|
|
|
|
|
payload、请求/响应正文、文件 Uri/绝对路径等实际值。根任务
|
|
|
|
|
|
`verifyNoSensitiveLogging` 在各模块 `testDebugUnitTest`、`lintRelease` 和 `check`
|
|
|
|
|
|
前扫描 Kotlin/Java/Gradle 源码,阻止凭据或用户标识重新进入输出边界。
|
|
|
|
|
|
- 删除文档中不存在的 `RetrofitFactory` 示例;公开文档同步 common/file 的真实边界。
|
|
|
|
|
|
|
|
|
|
|
|
### 本批最终验证
|
|
|
|
|
|
|
|
|
|
|
|
- core/file/bugcollect/update/im/push 与 BugCollect Gradle 插件单元测试通过;
|
|
|
|
|
|
`sdk-common-plugin` 独立构建测试通过,包含严格 `data.valid` 防伪响应和单配置路径测试。
|
|
|
|
|
|
- core/file/bugcollect/update/im/push/webview 的 `lintRelease` 与 `assembleRelease` 全部通过。
|
|
|
|
|
|
- Sample 的 Debug Kotlin、AndroidTest 编译以及合并 Manifest 校验通过;因仓库不提交被忽略的
|
|
|
|
|
|
`config.xuqmconfig`,验证编译时仅显式跳过 `xuqmValidateConfigDebug`。
|
|
|
|
|
|
- 单独执行 `xuqmValidateConfigDebug` 已按预期失败,并明确提示唯一缺失路径
|
|
|
|
|
|
`src/main/assets/config/config.xuqmconfig`;未伪造配置、未绕过在线 Release 校验。
|
|
|
|
|
|
- 普通 Release 生成结果为 `buildEnabled=true/automaticEnabled=true`;应急参数生成结果为
|
|
|
|
|
|
`false/false`;Debug 为 `true/false`,显式开发测试仍受隐私和平台开关约束。
|
|
|
|
|
|
- Release 未提供 `XUQM_BUILD_ID` 且启用 configuration cache 时按预期失败;提供显式
|
|
|
|
|
|
buildId 后配置缓存可保存、复用,生成 Manifest 中的值保持不变。
|
2026-07-27 12:34:11 +08:00
|
|
|
|
# 2026-07-27 Jenkins Git 认证收口
|
|
|
|
|
|
|
|
|
|
|
|
- `sdk-android-publish` 的 Jenkins SCM 已改为 Gitea SSH URL 并绑定
|
|
|
|
|
|
`jenkins-ssh-key`;正式版本回写 `gradle.properties` 时也通过 `sshagent` 使用同一
|
|
|
|
|
|
凭据,不依赖 checkout 临时环境或 URL 内嵌令牌。
|
|
|
|
|
|
- Jenkins 作业配置二次读取确认无内嵌凭据。该 Jenkinsfile 修改仍需在下一次实际
|
|
|
|
|
|
发布中验证版本回写阶段。
|