Android中获取设备里面的音频文件

前言

本文用来记录如何获取手机设备中存储的音频文件,之后可以通过各种播放器进行播放。


audio介绍

用下面工具类来获取系统提供的ContentProvider中数据库里的音频文件,然后自由选择每条记录的所需的属性并存储在Bean类中,就可以通过实体类随意使用了。

kotlin 复制代码
1.工具类:
//工具类,目前固定获取手机中音频文件中容量、歌曲名、歌手属性,后续考虑修改扩展性更强的工具类
object GetQueryAudioUtils {

    suspend fun getQueryAudio(
        context: Context
    ): List<AudioBean> = withContext(Dispatchers.IO) {
        val resolver = context.contentResolver //获取contentResolver
        val queryResult = mutableListOf<AudioBean>() //存放查询结果

        val projection = arrayOf(
            //查询的属性列表
            MediaStore.Audio.Media._ID, //拼uri 必定存在的列 不会出现找不到的异常
            MediaStore.Audio.Media.SIZE, //容量
            MediaStore.Audio.Media.DISPLAY_NAME, //含扩展名的歌曲名
            MediaStore.Audio.Media.ARTIST, //歌手
        )

        runCatching {
            resolver.query(
                MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
                //获取到路径  容量  歌曲名  歌手
                projection,
                null,
                null,
                null
            )?.use { cursor -> //可以自动关流

                //获取对应属性在表中具体第几列的序列号index
                val idIndex = cursor.getColumnIndexOrThrow(MediaStore.Audio.Media._ID) //防止意外抛出异常
                val sizeIndex = cursor.getColumnIndex(MediaStore.Audio.Media.SIZE)
                val nameIndex = cursor.getColumnIndex(MediaStore.Audio.Media.DISPLAY_NAME)
                val artisIndex = cursor.getColumnIndex(MediaStore.Audio.Media.ARTIST)

                while (cursor.moveToNext()) {
                    //对查询后的结果进行包装实体类
                    val id = cursor.getLong(idIndex)
                    val size = if (sizeIndex != -1) cursor.getLong(sizeIndex) else 0
                    val songName = if (nameIndex != -1) cursor.getString(nameIndex)
                        ?.substringBeforeLast('.') ?: "未知歌曲" else "未知歌曲"
                    val singer = if (artisIndex != -1) cursor.getString(artisIndex)
                        ?: "未知歌手" else "未知歌手"

                    val uri =
                        ContentUris.withAppendedId(
                            MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, id
                        ) //将获取到的id拼接在contentProvider地址的后面

                    queryResult.add(AudioBean(uri, size, songName, singer))
                }
            }
            queryResult
        }.onFailure {
            Log.e("GetQueryAudioUtils", "query audio failed", it)
        }.getOrDefault(emptyList<AudioBean>())

    }
}


2.使用:
  //清单文件中添加权限
    <uses-permission android:name="android.permission.READ_MEDIA_AUDIO" /> <!--api33及以上-->
    <!-- 外部存储读取权限 -->
    <uses-permission
        android:name="android.permission.READ_EXTERNAL_STORAGE"
        android:maxSdkVersion="32" /><!--api29-32-->


//mainActivity
class MainActivity : BaseActivity() {
    private lateinit var text: TextView

    override fun setViewId(): Int = R.layout.activity_main

    override fun initView() {
        text = findViewById(R.id.text)

        //动态获取权限
        PremissionUtils.requestPermissions(this)

        lifecycleScope.launch(Dispatchers.Main) {
            val result: List<AudioBean> = GetQueryAudioUtils.getQueryAudio(this@MainActivity)
            if (result.isNotEmpty())
                text.text = result.toString()
        }
    }
}



//3.动态权限申请
object PremissionUtils {

    var PERMISSIONS_STORAGE: Array<String?> = arrayOf<String?>(
        Manifest.permission.READ_EXTERNAL_STORAGE,
        Manifest.permission.WRITE_EXTERNAL_STORAGE
    ) //Android6.0以后操作系统的动态权限申请


    /**
     * 用于Android6.0以后的操作系统,动态申请存储的读写权限
     * @param context
     */
    fun requestPermissions(context: Activity) {
        //用于Android6.0以后的操作系统,动态申请存储的读写权限
        val permission =
            ActivityCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE)
        if (permission != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(context, PERMISSIONS_STORAGE, 1)
        }
    }

}
相关推荐
石山岭15 小时前
自己动手写了一个 Android 虚拟定位 App:GPSSimulate 技术实
android·前端
杉氧17 小时前
副作用 (Side Effects) 全攻略:如何像大师一样掌控 Composable 的生命周期?
android·架构·android jetpack
Kapaseker21 小时前
Kotlin Toolchain 0.11 发布:主要是把 Amper 干没了
android·kotlin
三少爷的鞋1 天前
Android 现代架构不需要事件总线进阶篇
android
杉氧2 天前
深入理解 Compose 重组机制:快照系统如何驱动 UI 精准刷新?
android·架构·android jetpack
召钱熏2 天前
状态枚举正确≠渲染正确:一个语音按钮的状态机边界修复实录
android·前端
杉氧2 天前
深度解析:Jetpack Compose 核心架构与底层原理 —— 十年安卓老兵的“破茧重生”
android·架构·android jetpack
通玄2 天前
Jetpack Compose 入门系列(七):ViewModel 与界面状态管理
android
落魄Android在线炒饭2 天前
Android Framework 开发技巧:android.jar 生成与系统快速编译验证
android
如此风景2 天前
Kotlin Flow操作符学习
android·kotlin