Android 在后台的Recyclerview中显示Coverart(来自id3)

Android 在后台的Recyclerview中显示Coverart(来自id3),android,android-recyclerview,background,id3,Android,Android Recyclerview,Background,Id3,我正在开发一个应用程序,它使用Recyclerview显示mp3文件,并提供封面图片和其他信息。它可以工作,但一旦它开始处理十几个或更多的封面艺术来检索,它就会变得很慢,因为我目前正在主线程上的id3中执行此操作,我知道这不是一个好主意。 理想情况下,我会使用占位符,以便在图像可用时添加它们。我一直在研究将检索转移到后台线程,并研究了不同的选项:AsyncTask、Service、WorkManager。AsyncTask似乎不是解决问题的方法,因为我面临内存泄漏(我需要上下文通过Metadat

我正在开发一个应用程序,它使用Recyclerview显示mp3文件,并提供封面图片和其他信息。它可以工作,但一旦它开始处理十几个或更多的封面艺术来检索,它就会变得很慢,因为我目前正在主线程上的id3中执行此操作,我知道这不是一个好主意。 理想情况下,我会使用占位符,以便在图像可用时添加它们。我一直在研究将检索转移到后台线程,并研究了不同的选项:AsyncTask、Service、WorkManager。AsyncTask似乎不是解决问题的方法,因为我面临内存泄漏(我需要上下文通过MetadataRetriever检索封面图片)。所以我倾向于远离这一点。然而,我正在努力找出哪种方法对我来说是最好的

据我所知,我需要找到一种允许多线程的方法,以及一种在用户已经移动(滚动或导航离开)的情况下取消检索的方法。我已经在使用Glide了,我知道它应该有助于缓存。 我知道我可以重新设计整个方法,并将封面艺术单独作为图像提供,但这似乎是我最后的选择,因为我不想用更多的数据来衡量应用程序

当前版本的应用程序是(请注意,它不会运行,因为我不能公开透露某些方面)。我正在检索封面艺术,如下所示(在主线上):


我已经找到了许多使用AsyncTask或仅将MetaDataRetriever保留在主线程上的示例,但还没有找到一个能够在不降低主线程速度的情况下检索十几个或更多封面艺术的示例。如果有任何帮助和指点,我将不胜感激

事实证明,只要AsyncTask本身不是一个类,而是从一个具有上下文的类进行设置和调用,它就可以使用AsyncTask。下面是我的方法的精简版本(我从适配器中调用它):

//是否有可重用的早期缓存映像?设置图像位图(lruCacheManager.getBitmapFromMemCache(selecteded.getId()+位置)); imageIv.setAlpha((浮动)1.0)

titleTv.setVisibility(View.GONE);
subtitleTv.setVisibility(View.GONE);
}否则{
//加载和显示图像的时间。为了更好地测量,还将查询持续时间,因为这还需要setDataSource,这会导致速度减慢
新建异步任务(){
@凌驾
受保护的位图doInBackground(Uri…Uri){
MediaMetadataRetriever mmr=新的MediaMetadataRetriever();
mmr.setDataSource(ctxt,medUri);
字节[]数据=mmr.getEmbeddedPicture();
Log.v(标记,“异步数据:”+Arrays.toString(数据));
String durationStr=mmr.extractMetadata(MediaMetadataRetriever.METADATA\u KEY\u DURATION);
duration[0]=Long.parseLong(durationStr);
如果(数据!=null){
InputStream is=newbytearrayinputstream(mmr.getEmbeddedPicture());
返回BitmapFactory.decodeStream(is);
}否则{
返回null;
}
}
@凌驾
受保护的void onPostExecute(位图){
onPostExecute(位图);
durationTv.setVisibility(View.VISIBLE);
durationTv.setText(getDisplayTime(duration[0],false));
if(位图!=null){
设置图像位图(位图);
imageIv.setAlpha((浮动)1.0);
标题设置可见性(视图已消失);
subtitleTv.setVisibility(View.GONE);
}否则{
标题v.setVisibility(视图可见);
subtitleTv.setVisibility(View.VISIBLE);
}
lruCacheManager.addBitMapToMeCache(位图,selectedMed.getId()+位置);
}
}.执行(medUri);
}
我曾尝试使用Glide进行缓存,但无法将文本视图的显示/隐藏与是否存在位图联系起来。在某种程度上,这是光滑的,因为我不需要加载大部分的Glide库。所以我现在对此感到高兴

static public Bitmap getCoverArt(Uri medUri, Context ctxt) {

    MediaMetadataRetriever mmr = new MediaMetadataRetriever();
    mmr.setDataSource(ctxt, medUri);

    byte[] data = mmr.getEmbeddedPicture();

    if (data != null) {
        return BitmapFactory.decodeByteArray(data, 0, data.length);
    } else {
        return null;
    }
}
//set up titles and placeholder image so we needn't wait on the image to load
titleTv.setText(selectedMed.getTitle());
subtitleTv.setText(selectedMed.getSubtitle());
imageIv.setImageResource(R.drawable.ic_launcher_foreground);
imageIv.setAlpha((float) 0.2);
final long[] duration = new long[1];

//a Caching system that helps reduce the amount of loading needed. See: https://github.com/cbonan/BitmapFun?files=1
if (lruCacheManager.getBitmapFromMemCache(selectedMed.getId() + position) != null) {
        titleTv.setVisibility(View.GONE);
        subtitleTv.setVisibility(View.GONE);
    } else {

        //time to load and show the image. For good measure, the duration is also queried, as this also needs the setDataSource which causes slow down
        new AsyncTask<Uri, Void, Bitmap>() {
            @Override
            protected Bitmap doInBackground(Uri... uris) {
                MediaMetadataRetriever mmr = new MediaMetadataRetriever();
                mmr.setDataSource(ctxt, medUri);
                byte[] data = mmr.getEmbeddedPicture();
                Log.v(TAG, "async data: " + Arrays.toString(data));

                String durationStr = mmr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
                duration[0] = Long.parseLong(durationStr);

                if (data != null) {
                    InputStream is = new ByteArrayInputStream(mmr.getEmbeddedPicture());
                    return BitmapFactory.decodeStream(is);
                } else {
                    return null;
                }
            }

            @Override
            protected void onPostExecute(Bitmap bitmap) {
                super.onPostExecute(bitmap);

                durationTv.setVisibility(View.VISIBLE);
                durationTv.setText(getDisplayTime(duration[0], false));

                if (bitmap != null) {
                    imageIv.setImageBitmap(bitmap);
                    imageIv.setAlpha((float) 1.0);

                    titleTv.setVisibility(View.GONE);
                    subtitleTv.setVisibility(View.GONE);
                } else {
                    titleTv.setVisibility(View.VISIBLE);
                    subtitleTv.setVisibility(View.VISIBLE);
                }

                lruCacheManager.addBitmapToMemCache(bitmap, selectedMed.getId() + position);
            }
        }.execute(medUri);
    }