onRenderProcessGone was not overridden, so when the WebView sandboxed renderer process crashed (e.g. during file upload of docx), Android terminated the main app process as well (default return false behavior). Now: return true to keep the host app alive, report the renderer crash via BugCollect (reflection, no compile-time dependency), and reload the page so the user is not stuck on a blank screen. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> |
||
|---|---|---|
| docs | ||
| gradle | ||
| sample-app | ||
| scripts | ||
| sdk-bugcollect | ||
| sdk-bugcollect-plugin | ||
| sdk-core | ||
| sdk-im | ||
| sdk-license | ||
| sdk-push | ||
| sdk-update | ||
| sdk-webview | ||
| .gitignore | ||
| .java-version | ||
| build.gradle.kts | ||
| CLAUDE.md | ||
| gradle.properties | ||
| gradlew | ||
| gradlew.bat | ||
| Jenkinsfile | ||
| README.md | ||
| settings.gradle.kts | ||
XuqmGroup Android SDK 文档
Kotlin 2.3.10 · AGP 9.1.0 · minSdk 24 · compileSdk 36
模块结构
XuqmGroup-AndroidSDK/
├── sdk-core/ # 核心:初始化、HTTP、Token 存储、文件上传、通用工具/组件
├── sdk-bugcollect/ # 日志监控:错误上报、Crash 捕获、漏斗分析(含 Gradle Plugin)
├── sdk-im/ # IM:WebSocket 实时通信
├── sdk-push/ # 推送:设备 Token 注册
├── sdk-update/ # 版本管理:检查更新、下载安装
├── sdk-webview/ # WebView:嵌入式组件 / 独立页面
└── sample-app/ # 示例 App(Jetpack Compose)
集成
Gradle(Maven 私有仓库)
在项目根 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 ?: ""
}
}
}
}
在 gradle.properties 或环境变量中配置:
NEXUS_USER=your_username
NEXUS_PASSWORD=your_password
引入依赖:
dependencies {
implementation("com.xuqm:sdk-core:0.4.2")
implementation("com.xuqm:sdk-bugcollect:0.4.2") // 日志监控(推荐)
implementation("com.xuqm:sdk-im:0.4.2") // 可选
implementation("com.xuqm:sdk-push:0.4.2") // 可选
implementation("com.xuqm:sdk-update:0.4.2") // 可选
implementation("com.xuqm:sdk-webview:0.4.2") // 可选
}
快速开始
1. 初始化
方式 A(推荐): 将加密配置文件放置到 src/main/assets/xuqm/config.xuqm,ContentProvider 自动完成初始化,无需代码。
方式 B(手动): 在 Application.onCreate() 中调用:
XuqmSDK.initialize(
context = this,
appKey = "ak_your_app_key",
logLevel = if (BuildConfig.DEBUG) LogLevel.DEBUG else LogLevel.WARN
)
2. 用户登录后初始化 IM
// 调用业务登录接口,拿到 userSig 后只需要登录一次 SDK
val userSig = api.getUserSig(userId)
XuqmSDK.login(
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()
3. 切换联调环境
默认使用外网域名。若要本地联调,可在 Application.onCreate() 里切换:
XuqmSDK.useLocalServiceEndpoints("192.168.113.37")
// 物理设备可改成你的电脑局域网 IP
如果是 sample-app,也可以直接调用:
SampleEnvironmentConfig.useLocalhost("192.168.113.37")
切换后,HTTP API 会立即走新端点;如果 IM 已登录,SDK 会自动重新连接。
sample-app 里也提供了一个“环境设置”页面,可以直接在外网和本地联调之间切换。
sdk-core
SDKConfig
| 参数 | 类型 | 说明 |
|---|---|---|
appKey |
String | 应用标识(租户平台获取) |
logLevel |
LogLevel | 日志等级 |
TokenStore
基于 EncryptedSharedPreferences 持久化存储。
// 存储
XuqmSDK.tokenStore.saveToken("eyJ...")
// 读取(协程)
val token = XuqmSDK.tokenStore.getToken()
// 清除(登出)
XuqmSDK.tokenStore.clear()
ApiClient
基于 OkHttp 5 + Retrofit 3,自动附加 Bearer Token,统一解析 ApiResponse<T>。
// 通过 RetrofitFactory 获取接口实例
val service = RetrofitFactory.create(MyApiService::class.java)
FileSDK
文件上传、下载、打开的统一入口,位于 com.xuqm.sdk.file。
上传
// 从 Uri(文件选择器返回值)上传,自动解析文件名和 MIME 类型
val result: FileUploadResult = FileSDK.upload(
context = context,
uri = uri,
onProgress = { progress -> /* 0–100 */ },
)
// 直接上传字节数组(如相机拍照后的 ByteArray)
val result = FileSDK.uploadBytes(
fileName = "photo.jpg",
mimeType = "image/jpeg",
bytes = byteArray,
onProgress = { progress -> },
)
// 上传 File 对象
val result = FileSDK.upload(file = file)
FileUploadResult 字段:url、thumbnailUrl、hash、size、originalName、mimeType、ext
下载
// 下载存储目标
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)
FileSDK.openFile(context, file)
需在 AndroidManifest.xml 中配置 FileProvider:
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
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.login() 成功后,SDK 会自动完成当前系统推送 token 的注册与上传,不再要求业务侧手工传入 token。logout() 时会自动注销当前设备绑定。
还可以按用户设置接收开关:
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_paths(external-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 自动拦截以下两种场景,下载完成后调用 FileSDK.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 中维护。