You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 07:46:45