如何等待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
相关产品推荐
相关产品推荐

