Android Wear Tile刷新抛出CancellationException崩溃如何排查
问题根因
这个偶现崩溃的本质是协程取消异常未被拦截导致的:
- 你当前使用自定义的独立
CoroutineScope(serviceScope)执行Tile刷新逻辑,这个作用域和Tile请求生命周期、TileService生命周期完全解绑 - 当系统发起Tile刷新请求后,如果协程执行完成前请求被终止(常见场景:用户滑走Tile离开可视区域、TileService被系统回收、新的刷新请求覆盖旧请求、请求耗时超过系统10秒默认超时窗口),系统会取消对应请求任务
kotlinx-coroutines-guava的future构建器会在协程被取消时,向返回的ListenableFuture传递CancellationException,而Wear Tiles库的TileProviderWrapper在主线程读取Future结果时没有捕获该类取消异常,异常直接抛到主线程触发崩溃,和你提供的堆栈调用链完全匹配。
排查方向
- 场景复现验证:手动连续多次滑动到目标Tile再快速滑走,同时在Logcat过滤
onTileRemove/onDestroy生命周期日志和JobCancellationException日志,确认崩溃是否和Tile离开可视区域、Service销毁的时间点重合 - 协程状态校验:在协程块开始、网络请求前后打印
coroutineContext[Job]?.isActive状态,确认崩溃发生时当前协程Job是否已经被取消,同时排查代码中是否存在手动调用serviceScope.cancel()的逻辑 - 网络耗时排查:抓取网络请求日志,统计
listUsersDevices接口的耗时分布,确认是否存在弱网下请求耗时超过10秒,触发系统超时取消请求的情况 - 刷新逻辑校验:全局搜索
requestUpdate调用点,确认是否存在短时间内多次触发Tile刷新,导致旧请求未完成就被新请求覆盖取消的问题
修复方案
- 替换协程作用域:删除自定义的独立
serviceScope,改用和TileService生命周期绑定的lifecycleScope,确保作用域随Service销毁自动取消,不会出现脱离生命周期的协程任务。注意需要引入Wear版本的lifecycle依赖:androidx.lifecycle:lifecycle-runtime-ktx - 增加异常拦截:在协程块内捕获
CancellationException和其他业务/网络异常,异常发生时返回缓存的旧Tile或者默认占位Tile,避免异常穿透到框架层 - 参考修复代码:
class WeatherTileService : TileService() { // 删除自定义的serviceScope override fun onTileRequest(requestParams: TileRequest) = lifecycleScope.future { Log.d("Tile", "Refreshing weather data") return@future try { val api = AmbientWeatherClient.getInstance() val devices = api.listUsersDevices(APP_KEY, API_KEY) // 原有Tile构建逻辑 Tile.Builder() .setResourcesVersion(RESOURCES_VERSION) .setFreshnessIntervalMillis(5 * 60 * 1000) .setTimeline(/* 原有timeline构建逻辑 */) .build() } catch (e: CancellationException) { // 请求被系统取消是正常场景,直接返回缓存的Tile即可 Log.d("Tile", "Tile request cancelled, use cached tile") getCachedTile() } catch (e: Exception) { // 捕获网络异常、数据解析异常等所有非取消异常 Log.e("Tile", "Refresh tile failed", e) getDefaultErrorTile() } } // 实现本地缓存Tile、默认错误Tile的构建逻辑 private fun getCachedTile(): Tile { /* 实现略 */ } private fun getDefaultErrorTile(): Tile { /* 实现略 */ } }
- 优化网络配置:给OkHttp配置合理的超时时间,将连接、读、写超时都设置在8秒以内,避免请求耗时过长触发系统超时取消
- 增加本地缓存:每次刷新成功后将Tile数据持久化到本地,请求取消或失败时优先返回缓存内容,既避免崩溃,也能提升Tile加载速度
内容的提问来源于stack exchange,提问作者GDefender
相关产品推荐
相关产品推荐

