如何从Sync Adapters通知应用UI线程?(媒体同步场景)
嘿,我来给你梳理下这个场景下实现同步进度展示的思路,毕竟我之前也踩过类似的坑😉
基于Sync Adapter的媒体同步进度展示方案
结合你描述的场景:用Sync Adapter后台上传媒体、SQLite存元数据(无Content Provider)、APP后台/关闭时仍能同步,还要在页面展示「当前同步数/总媒体数」计数器,下面分步骤给你拆解可行的实现方案:
1. 跨进程的进度数据传递
Sync Adapter运行在后台进程,要给UI层实时同步进度,得选靠谱的跨进程通信方式:
- LiveData + 全局ViewModel(推荐Jetpack方案):把同步进度封装成LiveData,放在全局单例或Application级别的ViewModel里。Sync Adapter每完成一个媒体上传,就更新LiveData的值;UI页面通过观察这个LiveData,自动刷新计数器。
- LocalBroadcastManager(兼容旧项目):Android 8.0后虽有一定限制,但同APP进程内通信没问题。每次同步完成一个媒体,发送携带当前进度的广播,UI页面注册广播接收器接收更新。
2. 从SQLite获取同步统计数据
因为你的媒体元数据存在自定义SQLite里,每次同步前后都要计算核心统计值:
- 总媒体数:查询数据库中需要同步的媒体总数(比如
isSynced = 0或你定义的同步条件) - 当前同步数:查询已标记同步完成的媒体数(
isSynced = 1) - 伪代码示例(Kotlin):
// 从SQLite同步统计数据 fun getSyncStats(): Pair<Int, Int> { val db = MyDatabaseHelper.getInstance(context).readableDatabase // 总待同步数 val totalCount = db.query(MEDIA_TABLE, arrayOf("COUNT(*)"), "isSynced = ?", arrayOf("0"), null, null, null).use { it.moveToFirst() it.getInt(0) } // 已完成同步数 val syncedCount = db.query(MEDIA_TABLE, arrayOf("COUNT(*)"), "isSynced = ?", arrayOf("1"), null, null, null).use { it.moveToFirst() it.getInt(0) } return Pair(syncedCount, syncedCount + totalCount) // 返回(已完成, 总数) }
3. Sync Adapter内的进度更新逻辑
在onPerformSync方法里,每完成一个媒体上传,就更新数据库状态并触发进度通知:
@Override public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) { // 获取待同步媒体列表 List<Media> mediaList = getPendingMedia(); int total = mediaList.size(); int completed = 0; for (Media media : mediaList) { // 执行云端上传逻辑 boolean uploadSuccess = uploadMedia(media.getMediaPath()); if (uploadSuccess) { // 更新数据库同步状态 updateMediaSyncStatus(media.getId(), true); completed++; // 发送进度更新(这里用LiveData举例) SyncProgressViewModel.getInstance().updateProgress(completed, total); } } }
4. UI页面的进度展示
在需要展示进度的页面,监听进度数据并更新UI:
- LiveData实现示例:
// 全局ViewModel class SyncProgressViewModel : ViewModel() { private val _syncProgress = MutableLiveData<Pair<Int, Int>>() val syncProgress: LiveData<Pair<Int, Int>> = _syncProgress fun updateProgress(completed: Int, total: Int) { _syncProgress.postValue(Pair(completed, total)) } companion object { private var instance: SyncProgressViewModel? = null fun getInstance() = instance ?: synchronized(this) { instance ?: SyncProgressViewModel().also { instance = it } } } } // 在Activity/Fragment中 val viewModel = SyncProgressViewModel.getInstance() viewModel.syncProgress.observe(this) { (completed, total) -> progressCounter.text = "$completed/$total" }
5. 特殊场景处理
- APP重启/切回前台:进入进度页面时,先调用
getSyncStats()从数据库拉取最新统计值,初始化UI,再监听实时更新,避免显示旧数据。 - 多同步任务并行:给每个同步任务加批次ID,统计时按批次区分,避免不同任务的进度互相干扰。
- 上传失败处理:标记失败媒体的状态(比如
isSynced = 2),统计时排除或单独展示失败数,避免重复上传。
内容的提问来源于stack exchange,提问作者Anurag Singh
相关产品推荐
相关产品推荐

