Android 如何使用Kotlin协同程序有效地从任何音乐文件中提取唱片集艺术
在我的应用程序中,我试图从手机存储器中的所有音乐文件中提取唱片集艺术,然后将它们填充到我的Android 如何使用Kotlin协同程序有效地从任何音乐文件中提取唱片集艺术,android,kotlin,kotlin-coroutines,Android,Kotlin,Kotlin Coroutines,在我的应用程序中,我试图从手机存储器中的所有音乐文件中提取唱片集艺术,然后将它们填充到我的RecyclerView中。我成功地获取了所有内容,但所有曲目都需要花费大量时间才能渲染到我的RecyclerView。我想做的就是,只要我的应用程序启动,所有曲目都应该立即在RecyclerView中填入合适的专辑艺术,没有任何延迟。我试图将我的代码放入协同程序中,但仍然需要大量的延迟。任何帮助都将不胜感激 fun getSongList() = GlobalScope.launch(Dispatcher
RecyclerView
中。我成功地获取了所有内容,但所有曲目都需要花费大量时间才能渲染到我的RecyclerView
。我想做的就是,只要我的应用程序启动,所有曲目都应该立即在RecyclerView
中填入合适的专辑艺术,没有任何延迟。我试图将我的代码放入协同程序中,但仍然需要大量的延迟。任何帮助都将不胜感激
fun getSongList() = GlobalScope.launch(Dispatchers.IO)
{
async {
realm = Realm.getDefaultInstance()
val iterator = FileUtils.iterateFiles(
Environment.getExternalStorageDirectory(),
FileFilterUtils.suffixFileFilter("m4a"),
TrueFileFilter.INSTANCE)
while (iterator.hasNext()) {
try {
val fileINeed = iterator.next()
val thisurls = fileINeed.canonicalPath
val mmr = MediaMetadataRetriever()
mmr.setDataSource(thisurls)
var thisTitle = mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_TITLE)
var thisArtist = mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ARTIST)
if (thisTitle.isNullOrEmpty()) {
thisTitle = fileINeed.name
}
if (thisArtist.isNullOrEmpty()) {
thisArtist = "Unknown Artist"
}
val query = realm!!.where(Songdetails::class.java).equalTo("songname",thisTitle).findFirst()
if(query == null)
{
val retriever = MediaMetadataRetriever()
retriever.setDataSource(thisurls)
val art = retriever.embeddedPicture
if (art != null) {
var bitmap = BitmapFactory.decodeByteArray(art, 0, art.size)
ImageSaver(MainActivity.getInstance()!!)
.setFileName(thisTitle)
.setDirectoryName("images")
.save(bitmap)
realm = Realm.getDefaultInstance()
realm!!.executeTransaction(object : Realm.Transaction {
override fun execute(realm: Realm) {
// increment index
var num = realm.where(Songdetails::class.java).max("id")
var nextID: Int
if (num == null) {
nextID = 1
} else {
nextID = num.toInt() + 1
}
var songitem = realm!!.createObject(Songdetails::class.java, nextID)
songitem.songname = thisTitle
songitem.songartist = thisArtist
songitem.songurl = thisurls
songitem.songimage = thisTitle
EventBus.getDefault().post("update")
}
})
}
else{
realm = Realm.getDefaultInstance()
realm!!.executeTransaction(object : Realm.Transaction {
override fun execute(realm: Realm) {
// increment index
var num = realm.where(Songdetails::class.java).max("id")
var nextID: Int
if (num == null) {
nextID = 1
} else {
nextID = num.toInt() + 1
}
var songitem = realm!!.createObject(Songdetails::class.java, nextID)
songitem.songname = thisTitle
songitem.songartist = thisArtist
songitem.songurl = thisurls
songitem.songimage = "custom"
EventBus.getDefault().post("update")
}
})
}
}
}catch (ex:Exception)
{
}
}
realm = Realm.getDefaultInstance()
val mp3iterator = FileUtils.iterateFiles(
Environment.getExternalStorageDirectory(),
FileFilterUtils.suffixFileFilter("mp3"),
TrueFileFilter.INSTANCE)
while (mp3iterator.hasNext()) {
try {
val fileINeed = mp3iterator.next()
val thisurls = fileINeed.canonicalPath
val mmr = MediaMetadataRetriever()
mmr.setDataSource(thisurls)
var thisTitle = mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_TITLE)
var thisArtist = mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ARTIST)
if (!thisTitle.isNullOrEmpty()) {
val query = realm!!.where(Songdetails::class.java).equalTo("songname", thisTitle).findFirst()
if (query == null) {
val retriever = MediaMetadataRetriever()
retriever.setDataSource(thisurls)
val art = retriever.embeddedPicture
if (art != null) {
var bitmap = BitmapFactory.decodeByteArray(art, 0, art.size)
ImageSaver(MainActivity.getInstance()!!)
.setFileName(thisTitle)
.setDirectoryName("images")
.save(bitmap)
realm = Realm.getDefaultInstance()
realm!!.executeTransaction(object : Realm.Transaction {
override fun execute(realm: Realm) {
// increment index
var num = realm.where(Songdetails::class.java).max("id")
var nextID: Int
if (num == null) {
nextID = 1
} else {
nextID = num.toInt() + 1
}
var songitem = realm!!.createObject(Songdetails::class.java, nextID)
songitem.songname = thisTitle
songitem.songartist = thisArtist
songitem.songurl = thisurls
songitem.songimage = thisTitle
EventBus.getDefault().post("update")
}
})
}
else{
realm = Realm.getDefaultInstance()
realm!!.executeTransaction(object : Realm.Transaction {
override fun execute(realm: Realm) {
// increment index
var num = realm.where(Songdetails::class.java).max("id")
var nextID: Int
if (num == null) {
nextID = 1
} else {
nextID = num.toInt() + 1
}
var songitem = realm!!.createObject(Songdetails::class.java, nextID)
songitem.songname = thisTitle
songitem.songartist = thisArtist
songitem.songurl = thisurls
songitem.songimage = "custom"
EventBus.getDefault().post("update")
}
})
}
}
}
}catch (ex:Exception)
{
}
}
realm = Realm.getDefaultInstance()
val waviterator = FileUtils.iterateFiles(
Environment.getExternalStorageDirectory(),
FileFilterUtils.suffixFileFilter("wav"),
TrueFileFilter.INSTANCE)
while (waviterator.hasNext()) {
try {
val fileINeed = waviterator.next()
val thisurls = fileINeed.canonicalPath
val mmr = MediaMetadataRetriever()
mmr.setDataSource(thisurls)
var thisTitle = mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_TITLE)
var thisArtist = mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_ARTIST)
if (thisTitle.isNullOrEmpty()) {
thisTitle = fileINeed.name
}
if (thisArtist.isNullOrEmpty()) {
thisArtist = "Unknown Artist"
}
val query = realm!!.where(Songdetails::class.java).equalTo("songname", thisTitle).findFirst()
if (query == null) {
val retriever = MediaMetadataRetriever()
retriever.setDataSource(thisurls)
val art = retriever.embeddedPicture
if (art != null) {
var bitmap = BitmapFactory.decodeByteArray(art, 0, art.size)
ImageSaver(MainActivity.getInstance()!!)
.setFileName(thisTitle)
.setDirectoryName("images")
.save(bitmap)
realm = Realm.getDefaultInstance()
realm!!.executeTransaction(object : Realm.Transaction {
override fun execute(realm: Realm) {
// increment index
var num = realm.where(Songdetails::class.java).max("id")
var nextID: Int
if (num == null) {
nextID = 1
} else {
nextID = num.toInt() + 1
}
var songitem = realm!!.createObject(Songdetails::class.java, nextID)
songitem.songname = thisTitle
songitem.songartist = thisArtist
songitem.songurl = thisurls
songitem.songimage = thisTitle
EventBus.getDefault().post("update")
}
})
} else {
realm = Realm.getDefaultInstance()
realm!!.executeTransaction(object : Realm.Transaction {
override fun execute(realm: Realm) {
// increment index
var num = realm.where(Songdetails::class.java).max("id")
var nextID: Int
if (num == null) {
nextID = 1
} else {
nextID = num.toInt() + 1
}
var songitem = realm!!.createObject(Songdetails::class.java, nextID)
songitem.songname = thisTitle
songitem.songartist = thisArtist
songitem.songurl = thisurls
songitem.songimage = "custom"
EventBus.getDefault().post("update")
}
})
}
}
} catch (ex: Exception) {
}
}
}
}
我只能猜测这是因为:
难道不可能只获取媒体文件列表,然后在
RecyclerAdapter
中在onBindViewHolder()
中提取位图,而不是在应用程序启动时尝试全部操作,只将位图保留在RAM中,而不必麻烦持久保存到“/图像”中吗?您可以利用Coil或毕加索更好地缓存位图,以备后续发布。由于各种变量(曲目数量、图像曲目大小等),无论使用何种方法,最初的一张专辑都会花费一些时间。为什么不加载系统已生成的相册艺术?它将返回相册艺术的uri,然后您可以使用Glide/Picasso/Coil将其加载到imageview中。这应该比手动操作要快。是的,在我的手机中大约有200到300首音乐,在运行时渲染它们需要花费大量时间!您也可以建议使用其他方法来解决此问题。这不仅仅是获取图像位图的情况,即使获取整个音乐文件列表也不会立即发生。即使我在没有图像的情况下获取它们,也会花费很多时间。在主ui线程上运行任何操作所有文件获取都是在后台线程中使用coroutinesI完成的。我没有完成这是我个人的想法,但我认为可能使用一个线程来获取要“处理”的文件名列表,然后将它们传递到单独处理文件的线程池中。线程池处理完文件后,会将更新发布到ViewModel,回收器会使用DiffUtil更新该ViewModel。这意味着用户可以“看到”正在加载的文件,至少可以开始做一些事情。难道不可能只获取媒体文件列表,然后在RecyclerAdapter中提取onBindViewHolder()中的位图吗我也尝试过使用glide,但在开始时获取没有位图的音乐文件列表仍然需要很多时间!除使用协同程序外,还欢迎使用其他解决方案我们不限于协同程序“我想做的是,一旦我的应用程序启动,所有曲目都应立即在RecyclerView中填充适当的专辑艺术,不得延误”我最关心的是。。。为什么?我的意思是,你正在进行o(N)操作,让你的用户等待N。。。对于一个只需N秒就能打开的应用程序来说,这似乎是一个奇怪的设计/灌木油似乎很有趣,让我看看!是 啊使用coil可以让它发挥作用,但我还是个新手,面临着一些问题,比如在尝试获取图像时如何侦听任何错误,以及在加载图像后我希望它返回位图。我尝试了很多次搜索,但没有找到太多关于它的信息,可能是因为它没有被广泛使用?您可以通过ImageLoaderBuilder查询Mediastore只能使用.mp3,但我也尝试获取扩展名为.m4a和.wma的文件,这在Mediastore查询中不起作用!我尝试在recyclerview列表中使用coil,使过程更快,但是我也想在加载完成后返回位图,如果加载成功,则返回加载的位图,如果失败,则返回给定的错误位图我尝试过这样做,但将代码放入侦听器,并将位图分配给位图变量,但我始终得到一个空位图,所以这在我的case@Mr.Patel像其他评论一样,我也不知道你为什么希望这个过程立即进行。手动生成艺术品需要一些时间,只要您在后台显示通知,任何人都可以完全理解。其次,为什么不使用MediaStore播放mp3,而使用手动播放其他文件?这将有点复杂,但您可以创建自己的db,它从MediaStore复制现有uri,并为其他文件生成和保存albumart uri。除第一次运行外,后续运行应立即加载您想要的图片。playstore中的大多数应用程序都会立即显示所有音乐文件,不会有任何延迟。您可以查看google play music,其他所有应用程序都会立即显示,不会有任何延迟