Отображение Coverart (из id3) в Recyclerview в фоновом режиме - PullRequest
0 голосов
/ 20 января 2020

Я работаю над приложением, которое использует Recyclerview для отображения mp3-файлов, предоставляя изображение обложки вместе с другой информацией. Это работает, но медленно, как только он начинает иметь дело с дюжиной или более обложек для извлечения, как я сейчас делаю это с id3 в главном потоке, что, я знаю, не очень хорошая идея. В идеале я бы работал с местозаполнителями, чтобы изображения можно было добавлять по мере их появления. Я пытался переместить поиск в фоновый поток и рассмотрел различные варианты: AsyncTask, Service, WorkManager. AsyncTask, похоже, не подходит для go, так как я сталкиваюсь с утечками памяти (мне нужен контекст, чтобы получить обложку через MetadataRetriever). Так что я от этого отклоняюсь. Тем не менее, я изо всех сил пытаюсь выяснить, какой подход является лучшим в моем случае.

Из того, что я понимаю, мне нужно найти подход, который позволяет использовать многопоточность, а также способ отменить поиск в случае, если пользователь уже перешел (прокрутка или навигация). Я уже использую 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;
    }
}

Я нашел много примеров с AsyncTask или просто оставил MetaDataRetriever в основном потоке, но пока не нашел пример, который позволяет получить дюжину или более обложек без замедления основного потока. Буду признателен за любую помощь и указатели.

1 Ответ

0 голосов
/ 23 января 2020

Оказывается, он работает с AsyncTask, если он не сам по себе класс, а настроен и вызывается из класса с контекстом. Вот сокращенная версия моего подхода (я называю это из моего адаптера.):

//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) {

// есть ли ранее кэшированное изображение для повторного использования? imageIv.setImageBitmap (lruCacheManager.getBitmapFromMemCache (selectedMed.getId () + position)); imageIv.setAlpha ((float) 1.0);

        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);
    }

Я пытался работать с Glide для кэширования, но я не смог связать показ / скрытие TextViews с тем, есть ли битовая карта. В некотором смысле, это более изящно, так как мне не нужно загружать основную часть библиотеки Glide. Так что сейчас я доволен этим.

...