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

如何等待MediaScannerConnection.scanFile()完成并获取URI列表

解决MediaScannerConnection.scanFile()异步获取URI列表的问题

你在Flutter Android原生端调用MediaScannerConnection.scanFile()时遇到的问题,根源在于这个方法是异步执行的——扫描操作在后台线程完成,而你直接在主线程打印uris时,回调还没执行,所以得到空列表。你用循环等待的方式不仅会阻塞线程,还可能引发ANR(应用无响应),下面是几种正确的实现方案:

原问题代码回顾

初始异步调用代码

val paths = arrayOf(
    "/storage/emulated/0/Music/Folder/Imperial Black Unit - Now You Can Pray.mp3",
    "/storage/emulated/0/Music/Folder/Lapse of Reason - Lapse of Reason - Go Away.mp3"
)
val uris = ArrayList<String>()

MediaScannerConnection.scanFile(
    applicationContext,
    paths,
    null
) { _, uri ->
    uris.add(uri.toString())
}

println(uris) // 此时回调未执行,输出空列表

不推荐的循环等待临时方案

do {
    var pending = true
    if(uris.size == paths.size) pending = false
} while (pending) // 阻塞线程,易引发ANR

正确解决方案

方案1:使用Kotlin协程(推荐)

Kotlin协程可以将回调式的异步操作转为挂起函数,避免阻塞线程,同时实现"等待异步完成"的逻辑。我们可以结合CountDownLatch和协程来实现:

import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import android.content.Context

// 包装为挂起函数,等待所有文件扫描完成后返回URI列表
suspend fun scanFilesAndGetUris(context: Context, paths: Array<String>): List<String> = withContext(Dispatchers.IO) {
    val uris = mutableListOf<String>()
    val latch = java.util.concurrent.CountDownLatch(paths.size)

    MediaScannerConnection.scanFile(
        context,
        paths,
        null
    ) { _, uri ->
        uri?.let { uris.add(it.toString()) }
        latch.countDown() // 每扫描完成一个文件,计数器减1
    }

    latch.await() // 等待所有扫描完成
    uris
}

// 调用示例(需在协程作用域内,比如ViewModel的viewModelScope或Activity的lifecycleScope)
fun exampleUsage() {
    val paths = arrayOf(/* 你的文件路径 */)
    lifecycleScope.launch {
        val uris = scanFilesAndGetUris(applicationContext, paths)
        println(uris) // 此时已拿到所有URI
        // 后续处理逻辑
    }
}

方案2:使用CountDownLatch(同步等待,适合非协程场景)

如果不想用协程,可以用CountDownLatch实现同步等待,但注意不要在主线程调用await(),否则会阻塞主线程引发ANR:

import android.content.Context
import android.app.Activity

fun scanFilesWithLatch(context: Context, paths: Array<String>) {
    val uris = mutableListOf<String>()
    val latch = java.util.concurrent.CountDownLatch(paths.size)

    MediaScannerConnection.scanFile(
        context,
        paths,
        null
    ) { _, uri ->
        uri?.let { uris.add(it.toString()) }
        latch.countDown()
    }

    // 在子线程等待扫描完成
    Thread {
        try {
            latch.await() // 等待所有扫描任务完成
            println(uris) // 打印完整URI列表
            // 如需更新UI,切换回主线程
            (context as Activity).runOnUiThread {
                // 主线程处理逻辑
            }
        } catch (e: InterruptedException) {
            e.printStackTrace()
        }
    }.start()
}

方案3:使用自定义回调接口

如果更习惯回调式编程,可以定义一个接口,当所有文件扫描完成后触发回调:

import android.content.Context

interface OnScanCompleteListener {
    fun onScanComplete(uris: List<String>)
}

fun scanFilesWithCallback(context: Context, paths: Array<String>, listener: OnScanCompleteListener) {
    val uris = mutableListOf<String>()
    val totalFiles = paths.size

    MediaScannerConnection.scanFile(
        context,
        paths,
        null
    ) { _, uri ->
        uri?.let { uris.add(it.toString()) }
        // 检查是否所有文件都扫描完成
        if (uris.size == totalFiles) {
            listener.onScanComplete(uris)
        }
    }
}

// 调用示例
scanFilesWithCallback(applicationContext, paths, object : OnScanCompleteListener {
    override fun onScanComplete(uris: List<String>) {
        println(uris) // 拿到完整URI列表
        // 后续处理逻辑
    }
})

内容的提问来源于stack exchange,提问作者user3221449

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 09:33:13