转到文件
2026-07-27 10:19:31 +08:00
docs refactor(sdk): unify configuration and extension lifecycle 2026-07-27 09:46:13 +08:00
gradle refactor(sdk): unify configuration and extension lifecycle 2026-07-27 09:46:13 +08:00
sample-app refactor(sdk): unify configuration and extension lifecycle 2026-07-27 09:46:13 +08:00
scripts docs(test): 更新测试报告和文档 2026-05-05 16:06:32 +08:00
sdk-bugcollect refactor(sdk): unify configuration and extension lifecycle 2026-07-27 09:46:13 +08:00
sdk-bugcollect-plugin refactor(sdk): unify configuration and extension lifecycle 2026-07-27 09:46:13 +08:00
sdk-common-plugin refactor(sdk): unify configuration and extension lifecycle 2026-07-27 09:46:13 +08:00
sdk-core test(core): tamper decoded ciphertext deterministically 2026-07-27 10:19:31 +08:00
sdk-file refactor(sdk): unify configuration and extension lifecycle 2026-07-27 09:46:13 +08:00
sdk-im refactor(sdk): unify configuration and extension lifecycle 2026-07-27 09:46:13 +08:00
sdk-push refactor(sdk): unify configuration and extension lifecycle 2026-07-27 09:46:13 +08:00
sdk-update refactor(sdk): unify configuration and extension lifecycle 2026-07-27 09:46:13 +08:00
sdk-webview refactor(sdk): unify configuration and extension lifecycle 2026-07-27 09:46:13 +08:00
.gitignore refactor(sdk): unify configuration and extension lifecycle 2026-07-27 09:46:13 +08:00
.java-version feat(im): 添加平台事件通知功能支持应用审核状态实时更新 2026-05-08 18:32:46 +08:00
AGENTS.md fix(core): align okhttp for React Native hosts 2026-07-18 08:28:33 +08:00
build.gradle.kts refactor(sdk): unify configuration and extension lifecycle 2026-07-27 09:46:13 +08:00
CLAUDE.md refactor(sdk): unify configuration and extension lifecycle 2026-07-27 09:46:13 +08:00
gradle.properties refactor(sdk): unify configuration and extension lifecycle 2026-07-27 09:46:13 +08:00
gradlew feat(sample): 集成 Sentry 异常监控功能 2026-04-24 16:46:38 +08:00
gradlew.bat feat(sample): 集成 Sentry 异常监控功能 2026-04-24 16:46:38 +08:00
Jenkinsfile refactor(sdk): unify configuration and extension lifecycle 2026-07-27 09:46:13 +08:00
README.md refactor(sdk): unify configuration and extension lifecycle 2026-07-27 09:46:13 +08:00
settings.gradle.kts refactor(sdk): unify configuration and extension lifecycle 2026-07-27 09:46:13 +08:00

XuqmGroup Android SDK 文档

Kotlin 2.3.10 · AGP 9.1.0 · minSdk 24 · compileSdk 36

模块结构

XuqmGroup-AndroidSDK/
├── sdk-core/        # 核心:初始化、基础 HTTP、本地文件、时间、安全存储
├── sdk-file/        # 平台文件上传与 FileProvider 打开能力
├── sdk-bugcollect/  # 日志监控错误上报、Crash 捕获、漏斗分析(含 Gradle Plugin
├── sdk-im/          # IMWebSocket 实时通信
├── sdk-push/        # 推送:设备 Token 注册
├── sdk-update/      # 版本管理:检查更新、下载安装
├── sdk-webview/     # WebView嵌入式组件 / 独立页面
└── sample-app/      # 示例 AppJetpack Compose

集成

GradleMaven 私有仓库)

在项目根 settings.gradle.kts 添加仓库:

dependencyResolutionManagement {
    repositories {
        maven {
            url = uri("https://nexus.xuqinmin.com/repository/android-hosted/")
            credentials {
                username = providers.gradleProperty("NEXUS_USER").orNull ?: ""
                password = providers.gradleProperty("NEXUS_PASSWORD").orNull ?: ""
            }
        }
    }
}

所有宿主变体必须应用 common 构建插件。它会扫描宿主 src/**,只接受唯一固定路径 src/main/assets/config/config.xuqmconfig;任何改名、嵌套或其它 sourceSet 中的 .xuqmconfig 文件都会直接失败。 Debug 执行本地 V2 验签,Release 额外执行租户平台在线有效性校验,并负责不可变 buildId 和 BugCollect 构建门禁,并生成扩展自动初始化所需的唯一 Manifest 标记;BugCollect 插件 仅负责 R8 mapping 上传。Debug 自动采集固定关闭,但非应急禁用的可调试宿主可通过内部 开发入口显式发送一次测试事件。

plugins {
    id("com.xuqm.common") version "VERSION"
    // 使用 BugCollect 且 Release 开启 R8 时再添加:
    id("com.xuqm.bugcollect") version "VERSION"
}

gradle.properties 或环境变量中配置:

NEXUS_USER=your_username
NEXUS_PASSWORD=your_password

引入依赖:

dependencies {
    implementation("com.xuqm:sdk-core:VERSION")
    implementation("com.xuqm:sdk-file:VERSION")       // 平台文件能力可选
    implementation("com.xuqm:sdk-bugcollect:VERSION") // 可选
    implementation("com.xuqm:sdk-im:VERSION")         // 可选
    implementation("com.xuqm:sdk-push:VERSION")       // 可选
    implementation("com.xuqm:sdk-update:VERSION")     // 可选
    implementation("com.xuqm:sdk-webview:VERSION")    // 可选
}

快速开始

1. 初始化

从租户平台下载签名的 config.xuqmconfig,原样放入 app/src/main/assets/config/config.xuqmconfig。ContentProvider 会自动初始化,宿主无需 编写初始化代码,也不得修改、复制、自行生成或在 flavor/buildType 下放置第二份文件。 配置中的 serverUrl 是唯一平台地址且必填,运行时没有默认地址或跨平台回退。

仅依赖 sdk-core 时不会合并初始化 Provider、通知权限或 FileProvider,也不要求配置 文件、初始化或登录。引入任一需要平台能力的扩展后,各 AAR 中同名声明会合并为唯一 XuqmMergedProvider;Provider 只有检测到 common 构建插件生成的标记后才会自动读取 上述配置,避免未应用插件的宿主发生隐式初始化。

2. 用户登录后初始化 IM

// 调用业务登录接口,拿到 userSig 后只需要登录一次 SDK
val userSig = api.getUserSig(userId)
XuqmSDK.setUserInfo(XuqmUserInfo(userId = userId, userSig = userSig))

// 如果工程里集成了 sdk-im / sdk-push / sdk-update,
// SDK 会自动完成对应模块的登录与初始化

3. WebView 独立模块

sdk-webview 不需要额外初始化,直接依赖 sdk-core 即可使用。

import com.xuqm.sdk.webview.XWebViewConfig
import com.xuqm.sdk.webview.XWebViewView
import com.xuqm.sdk.webview.XWebViewScreen
import com.xuqm.sdk.webview.openXWebView

// 嵌入式组件:直接放到页面中,不包含导航栏 / 状态栏
XWebViewView(
    config = XWebViewConfig(
        url = "https://example.com",
        title = "嵌入式网页",
    ),
)

// 独立页面:先设置配置,再跳转到页面
openXWebView(
    XWebViewConfig(
        url = "https://example.com",
        title = "独立页面",
    )
)
// navigate("xwebview") 后使用 XWebViewScreen()

sdk-core

SDKConfig

参数 类型 说明
appKey String 应用标识(租户平台获取)
logLevel LogLevel 日志等级

TokenStore

基于统一的 SecureStore 持久化:密钥由 Android Keystore 生成且不可导出, 值使用 AES-256-GCM 加密并通过 namespace/key 关联数据认证。

Sample Release 签名

示例应用的 Release 签名只从用户级 ~/.gradle/gradle.properties 读取,仓库不保存 密钥库路径或凭据。需要配置的属性名为:

  • YIWANGXIN_STORE_FILE
  • YIWANGXIN_STORE_PASSWORD
  • YIWANGXIN_ALIAS
  • YIWANGXIN_KEY_PASSWORD

缺少属性或密钥库文件不可读时,Release 构建会在签名前失败;Debug 使用 Android 默认调试签名,不读取生产签名材料。

Sample 还要求把租户平台签发且包名匹配的文件放到 sample-app/src/main/assets/config/config.xuqmconfig。该文件被 Git 忽略;Debug 与 Release 构建都会在文件缺失时给出明确错误,禁止提交、伪造或手工修改配置。

运行 Sample 仪器集成测试时,测试密码通过 -e XUQM_TEST_PASSWORD <value> 注入,禁止写入源码或 Gradle 配置。

// 存储
XuqmSDK.tokenStore.saveToken("eyJ...")

// 读取(协程)
val token = XuqmSDK.tokenStore.getToken()

// 清除(登出)
XuqmSDK.tokenStore.clear()

网络边界

sdk-coreCommonHttpClient 与下载/摘要能力无需 SDK 初始化;依赖租户平台认证、 服务地址和 FileProvider 的上传、打开能力统一由 sdk-filePlatformFileSDK 提供。

FileSDK

纯本地能力位于 com.xuqm.sdk.file.FileSDK;平台上传与 FileProvider 打开位于 com.xuqm.sdk.file.platform.PlatformFileSDK

上传

// 从 Uri文件选择器返回值上传,自动解析文件名和 MIME 类型
val result: FileUploadResult = PlatformFileSDK.upload(
    context = context,
    uri = uri,
    onProgress = { progress -> /* 0–100 */ },
)

// 直接上传字节数组(如相机拍照后的 ByteArray
val result = PlatformFileSDK.uploadBytes(
    fileName = "photo.jpg",
    mimeType = "image/jpeg",
    bytes = byteArray,
    onProgress = { progress -> },
)

// 上传 File 对象
val result = PlatformFileSDK.upload(file = file)

FileUploadResult 字段:urlthumbnailUrlhashsizeoriginalNamemimeTypeext 。上传入口会自动等待平台初始化;服务端业务失败或响应结构错误统一抛出保留 code/status/messagePlatformFileException,不会暴露原始响应体。

下载

// 下载存储目标
sealed class FileDownloadDestination {
    data object Sandbox : FileDownloadDestination()        // 应用私有目录(无需权限)
    data object PublicDownloads : FileDownloadDestination() // 系统 Downloads 文件夹
}

// 下载到指定目标,支持通知栏进度
val file: File = FileSDK.download(
    context = context,
    downloadUrl = "https://example.com/report.pdf",
    fileName = "report.pdf",                         // 可选,默认从 URL 推断
    destination = FileDownloadDestination.PublicDownloads,
    notificationTitle = "正在下载",                   // 非 null 时显示通知栏进度条
    onProgress = { progress -> /* 0–100,同步进度到 H5 或 UI */ },
)

通知栏进度:设置 notificationTitle 后,下载过程中通知栏会显示带进度条的持续通知,完成后自动切换为完成图标。需在 AndroidManifest.xml 中声明 POST_NOTIFICATIONS 权限Android 13+)。

打开文件

// 用系统应用打开本地文件(通过 FileProvider + ACTION_VIEW
PlatformFileSDK.openFile(context, file)

openFile 是纯本地操作,不要求 SDK 初始化。

sdk-file 是该 FileProvider 的唯一所有者;sdk-updatesdk-imsdk-webview 均复用它。只集成 sdk-core 不会向宿主注入 Provider,也不存在 core 内的上传或打开 转发 API。


sdk-im

Android sample 已具备

  • 会话列表先读本地缓存,再刷新网络
  • 联系人列表先读本地缓存,再刷新网络
  • 聊天历史分页加载
  • 当前会话本地搜索
  • 输入草稿自动保存
  • 群设置支持编辑群名和群公告
  • 群组扩展 API 已补齐:管理员设置、禁言、解散群
  • 公开群支持搜索、创建和加群审批
  • 会话支持本地删除
  • 显示总未读数
  • 消息状态直接展示
  • 会话置顶/免打扰/已读/草稿/删除同步服务端
  • IM 连接状态提示
  • SDK 登录态恢复后自动重连
  • 重复调用登录会覆盖当前 IM 会话并自动重连,SDK 侧不做生命周期检测或维护
  • 群聊支持 @userId 提及,并写入 mentionedUserIds
  • 关系链支持好友申请、接受/拒绝、黑名单
  • 支持图片 / 视频 / 音频 / 文件消息,文件通过独立文件服务上传后再发 IM
  • 图片支持拍照、摄像、图库选择,文件支持文件管理器选择
  • 语音支持长按录音、抬手发送
  • 支持引用回复、群聊已读人数展示
  • 支持 LOCATION / CUSTOM / RICH_TEXT / FORWARD / QUOTE / MERGE / CALL_AUDIO / CALL_VIDEO 等通用消息类型发送
  • 单聊支持已读回执,服务端会把 READ 状态推回发送者
  • sdk-webview 作为独立模块提供嵌入式组件和独立页面两种形态,可与 IM / Push / Update 任意组合

ImClient

val imClient = ImClient()

// 注册事件监听
imClient.listener = object : ImEventListener {
    override fun onConnected() { /* WebSocket 连接成功 */ }
    override fun onDisconnected(code: Int, reason: String) { /* 断开 */ }
    override fun onMessage(msg: ImMessage) { /* 收到新消息 */ }
    override fun onRevoke(msgId: String, operatorId: String) { /* 消息被撤回 */ }
    override fun onError(t: Throwable) { /* 连接错误 */ }
}

// 连接
imClient.connect()

// 发送消息
imClient.send(SendMessageParams(
    toId = "user_002",
    chatType = ChatType.SINGLE,
    msgType = MsgType.TEXT,
    content = "Hello!"
))

// 群聊提及
imClient.send(SendMessageParams(
    toId = "group_001",
    chatType = ChatType.GROUP,
    msgType = MsgType.TEXT,
    content = "@user_002 你好",
    mentionedUserIds = "user_002",
))

// 撤回消息
imClient.revoke(msgId = "uuid")

// 断开连接Activity/Fragment 销毁时调用)
imClient.disconnect()

消息类型MsgType

TEXT / IMAGE / VIDEO / AUDIO / FILE / CUSTOM / LOCATION / NOTIFY / RICH_TEXT / CALL_AUDIO / CALL_VIDEO / FORWARD / QUOTE / MERGE

ImMessage 结构

data class ImMessage(
    val id: String,
    val fromId: String,
    val toId: String,
    val chatType: ChatType,     // SINGLE / GROUP
    val msgType: MsgType,
    val content: String,
    val extra: String?,
    val revoked: Boolean,
    val createdAt: String
)

自动重连

断线后指数退避重连,初始间隔 3 秒,最大间隔 30 秒。调用 disconnect() 后停止重连。


sdk-push

推送接入

宿主调用 XuqmSDK.setUserInfo(XuqmUserInfo(...)) 后,SDK 会自动完成当前系统推送 token 的注册与上传,不再要求业务侧手工传入 token;传入 null 时自动注销当前设备绑定。

还可以按用户设置接收开关:

PushSDK.setReceivePush(context, enabled = false)
PushSDK.setReceivePush(context, enabled = true)

如果项目接入了 Firebase Messaging,sdk-push 会通过 FirebaseMessagingService.onNewToken() 自动接收并上报 FCM token。对应服务已经随库注册,只要应用工程提供 Firebase 配置即可生效。

与 IM 联动

当 IM 和推送服务均已开启时,IM 服务在目标用户离线时会自动调用推送服务发送离线推送通知。
业务方只需分别注册推送 Token 和 IM 登录,无需额外配置。


sdk-update

检查原生 App 更新

val result: UpdateResult = UpdateSDK.checkUpdate(
    appKey = "ak_xxx",
    platform = Platform.ANDROID
)

if (result.needsUpdate) {
    // result.versionName, result.downloadUrl, result.forceUpdate
    UpdateSDK.downloadAndInstall(context, result.downloadUrl)
}

downloadAndInstall 会将 APK 下载到 getExternalFilesDir(null),通过 FileProvider 触发系统安装。
AndroidManifest 中已配置 @xml/file_pathsexternal-files-path)。


sdk-webview

XWebViewView 是基于 android.webkit.WebView 封装的 Jetpack Compose 组件,内置文件选择、拍照、下载拦截和 JS 通信能力。

XWebViewConfig 完整参数

参数 类型 默认值 说明
url String "" 初始加载地址
title String "" 页面标题(独立页面模式使用)
hideToolbar Boolean false 隐藏独立页面顶栏
hideStatusBar Boolean false 隐藏状态栏
userAgent String? null 自定义 User-Agent
injectedJavaScript String? null 页面加载后注入的额外 JS
jsBridgeName String "XWebViewBridge" JS 桥接对象名
debugEnabled Boolean false 开启 WebView 远程调试
downloadDestination FileDownloadDestination Sandbox 下载文件存储目标
downloadNotificationTitle String? null 非 null 时通知栏显示下载进度
onMessage (String) -> Unit? null H5 发送消息的回调

文件选择与拍照

WebView 内 <input type="file"><input type="file" capture> 均已内置处理:

  • accept="image/*" + capture → 调起系统相机,自动申请 CAMERA 权限
  • accept=".docx,.xlsx" 等扩展名格式 → 自动映射为正确的 MIME 类型后调起文件选择器
  • getUserMedia() WebRTC 摄像头 → 自动请求 CAMERA 权限后授权

下载拦截

注入的 JS 自动拦截以下两种场景,下载完成后调用 PlatformFileSDK.openFile() 打开文件:

  • download 属性的 <a> 标签,或链接以可下载扩展名(.pdf.zip.docx 等)结尾
  • Blob URL自动转 base64 后传给 native 处理)
XWebViewView(
    config = XWebViewConfig(
        url = "https://example.com",
        downloadDestination = FileDownloadDestination.PublicDownloads, // 存入系统 Downloads
        downloadNotificationTitle = "正在下载",                         // 通知栏进度
    )
)

H5 监听下载进度

H5 页面可通过 window.addEventListener 接收下载进度和完成事件:

// 下载进度0–100
window.addEventListener('__xwvDownloadProgress', (e) => {
    console.log(e.detail.url, e.detail.progress)
})

// 下载完成
window.addEventListener('__xwvDownloadDone', (e) => {
    if (e.detail.success) {
        console.log('下载成功', e.detail.url)
    } else {
        console.error('下载失败', e.detail.error)
    }
})

H5 ↔ Native 消息通信

// H5 发消息给 Native触发 onMessage 回调)
window.XWebViewBridge.postMessage(JSON.stringify({ type: 'login', token: '...' }))
XWebViewConfig(
    onMessage = { raw ->
        val json = JSONObject(raw)
        when (json.optString("type")) {
            "login" -> { /* 处理登录 */ }
        }
    }
)
// Native 发消息给 H5
val controller = getXWebViewController()
controller?.postMessageToWeb("window.dispatchEvent(new CustomEvent('nativeMsg', { detail: { key: 'value' } }))")

发版

# 在 gradle.properties 中配置 NEXUS_USER / NEXUS_PASSWORD
./gradlew :sdk-core:publish
./gradlew :sdk-im:publish
./gradlew :sdk-push:publish
./gradlew :sdk-update:publish

发布至 https://nexus.xuqinmin.com/repository/android-hosted/,groupId com.xuqm,版本号在各模块 build.gradle.kts 中维护。