调用这个方法前记得判断文件读取权限
import android.content.Context
import android.media.MediaMetadataRetriever
import android.media.MediaPlayer
import android.os.Environment
import android.provider.MediaStore
import androidx.core.content.ContextCompat
import me.zempty.common.R
import java.io.File
/**
* @Description: 把对于音频文件的文件获取方式单独当做一个类提取出来
* @Author: dk
* @Date: 2021/7/9 11:04 AM
* @Gmail: dk.guo@tron.network
*/
data class Song(
//歌曲名
var song: String = "",
//歌手
var singer: String = "",
//歌曲所占空间大小 单位 byte
var size: Long = 0,
//歌曲时间长度 单位 ms
var duration: Int = 0,
//歌曲地址 /storage/emulated/0/package/name.mp3
var path: String = ""
)
private val songSuffix = arrayListOf("MP3", "AAC", "M4A", "MP4", "WAV", "3GP")
/**
* 时长低于10s的不再加入列表
*/
private val MIN_AUDIO_DURATION = 10 * 1000
//调用这个方法前记得判断文件读取权限
//记得在子线程调用
fun Context.getMusics(): MutableList<Song> {
val list = ArrayList<Song>()
//优先调用系统的音频路径
val cursor = contentResolver.query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null, null, null, MediaStore.Audio.AudioColumns.IS_MUSIC)
if (cursor != null) {
while (cursor.moveToNext()) {
val song = Song()
song.song = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DISPLAY_NAME))
song.singer = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ARTIST))
song.path = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DATA))
song.duration = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DURATION))
song.size = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.SIZE))
if (song.song.contains("-")) {
val str: Array<String> = song.song.split("-").toTypedArray()
if (str.size > 1) {
song.singer = str[0].trim()
val songName = str[1].trim()
song.song = if (songName.contains('.')) {
songName.dropLast(songName.length - songName.lastIndexOf('.'))
} else {
songName
}
} else {
song.song = str[0]
song.singer = getString(R.string.unknown_singer)
}
}
logD("MusicFileExt->" + song.toJson())
if (song.duration > MIN_AUDIO_DURATION) {
list.add(song)
}
}
cursor?.close()
} else {
//作为cursor无法正常创建的备选方案 该方案获取文件速度很慢 主要原因是音频时长的获取方式在主线程执行
//在部分机型或者高版本上下面的方法可能无法调用,优先使用上面的方法
val hasSDCard = Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)
val firstPath = if (hasSDCard) Environment.getExternalStorageDirectory() else filesDir.absoluteFile
val secondPath = getExternalFilesDir(null)?.absoluteFile
var songFileList = findSong(firstPath)
if (songFileList.isEmpty() && secondPath != null) {
songFileList = findSong(secondPath)
}
list.addAll(getSongInfoWithFile(songFileList, this))
}
return list
}
private fun findSong(file: File): ArrayList<File> {
val arrayList = ArrayList<File>()
file.listFiles()?.forEach { singleFile ->
val suffix = singleFile.name.split(".").lastOrNull()
if (singleFile.isDirectory && !singleFile.isHidden) {
arrayList.addAll(findSong(singleFile))
} else if (songSuffix.find { it.equals(suffix, true) } != null) {
arrayList.add(singleFile)
}
}
return arrayList
}
private fun getSongInfoWithFile(files: MutableList<File>, context: Context): MutableList<Song> {
val mediaPlayer = MediaPlayer()
val mmr = MediaMetadataRetriever()
val songs = ArrayList<Song>()
files.forEach { file ->
val song = Song()
song.path = file.absolutePath
song.size = file.length()
if (file.name.contains("-")) {
val split = file.name.split("-")
if (split.size > 1) {
song.singer = split[0].trim()
val songName = split[1].trim()
song.song = if (songName.contains('.')) {
songName.dropLast(songName.length - songName.lastIndexOf('.'))
} else {
songName
}
} else {
song.song = file.name
song.singer = context.getString(R.string.unknown_singer)
}
} else {
song.song = file.name
song.singer = context.getString(R.string.unknown_singer)
}
//这个获取时长的方法会造成卡顿,如果不是强制需要时长,尽量不要调用
try {
mmr.setDataSource(file.absolutePath)
song.duration = mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION).number()
if (song.duration == 0) {
song.duration = readDurationWithMediaPlayer(mediaPlayer, file)
}
} catch (e: Exception) {
song.duration = readDurationWithMediaPlayer(mediaPlayer, file)
}
if (song.duration > MIN_AUDIO_DURATION) {
songs.add(song)
}
}
if (mediaPlayer != null) {
mediaPlayer.stop()
mediaPlayer.reset()
mediaPlayer.release()
}
return songs
}
private fun readDurationWithMediaPlayer(mediaPlayer: MediaPlayer, file: File): Int {
var duration = 0
try {
mediaPlayer.setDataSource(file.absolutePath)
mediaPlayer.prepare()
duration = mediaPlayer.getDuration()
} catch (e: Exception) {
logE("file->${file.absolutePath} read media duration failed, message-> ${e.message}")
}
return duration
}