Многопоточный кэшированный imageView loading - PullRequest
2 голосов
/ 17 мая 2011

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

Моя проблема в том, что иногда изображения не являются правильными (дубликат другого изображения), и когда я прокручиваю вниз список изображений, изображения мерцают и изменяются при прокрутке. Это то, с чем я должен жить?

Да, я создаю новую тему для каждого изображения. Я попробовал один поток в очереди, и загрузка заняла слишком много времени. Несмотря на то, что все они являются отдельными потоками, не следует загружать imageView с неправильными объектами рисования.

Вот мой код.

public void fetchDrawableOnThread(final String urlString, final ImageView imageView) {

    if (imageView != null && urlString != null && urlString.length() > 0)
    {

        final Handler handler = new Handler() {
            @Override
            public void handleMessage(Message message) {
                imageView.setImageDrawable((Drawable) message.obj);
            }
        };

        Thread thread = new Thread() {
            @Override
            public void run() {
                if (imageView != null && urlString != null && !urlString.equals(""))
                {
                    URL url;
                    try {
                        InputStream is = fetch(urlString);

                        if (is != null) {
                            Drawable drawable = Drawable.createFromStream((InputStream)is, "src");
                            Message message = handler.obtainMessage(1, drawable);
                            handler.sendMessage(message);
                        } 
                    }
                    catch (MalformedURLException e) {
                        Log.e(this.getClass().getSimpleName(), "fetchDrawable failed", e);
                    } catch (IOException e) {
                        Log.e(this.getClass().getSimpleName(), "fetchDrawable failed", e);
                    }
                }

            }
        };
        thread.start();
    }

}

private InputStream fetch(String urlString) throws MalformedURLException, IOException {
    if (urlString != null)
    {
        URL url = new URL(urlString);

        URLConnection connection = url.openConnection();
        connection.setUseCaches(true);
        Object response = connection.getContent();
        if (response instanceof InputStream) {
            return (InputStream) response;
        }
    }
    return null;
}

* EDIT ***

После всех комментариев вот мой код: в настоящее время я получаю 05-19 00: 16: 49.535: ОШИБКА / AndroidRuntime (12213): java.lang.RuntimeException: Canvas: попытка использовать переработанный растровый рисунок Android. graphics.Bitmap@4070dab0, когда я пытаюсь перезапустить.

Кроме того, когда у меня не было корзины, все работает, за исключением того, что я вижу, что изображения все еще мерцают. И даже иногда отображается неправильное изображение.

public void fetchDrawableOnThread(final String urlString, final ImageView imageView) {

    if (imageView != null && urlString != null && urlString.length() > 0)
    {
        imageView.setTag(urlString);

        final Handler handler = new Handler() {
            @Override
            public void handleMessage(Message message) {
                Drawable d = imageView.getDrawable();
                if(d != null){
                     ((BitmapDrawable)imageView.getDrawable()).getBitmap().recycle();
                     imageView.setImageBitmap(null);
                }

                imageView.setImageDrawable((Drawable) message.obj);
            }
        };

        Thread thread = new Thread() {
            @Override
            public void run() {
                if (imageView != null && urlString != null && !urlString.equals(""))
                {
                    URL url;
                    try {
                        if (imageView.getTag().equals(urlString))
                        {
                            InputStream is = fetch(urlString);

                            if (is != null) {
                                Drawable drawable = Drawable.createFromStream((InputStream)is, "src");
                                Message message = handler.obtainMessage(1, drawable);
                                handler.sendMessage(message);
                            } 
                        }
                    }
                    catch (MalformedURLException e) {
                        Log.e(this.getClass().getSimpleName(), "fetchDrawable failed", e);
                    } catch (IOException e) {
                        Log.e(this.getClass().getSimpleName(), "fetchDrawable failed", e);
                    }
                }

            }
        };
        thread.start();
    }

}

private InputStream fetch(String urlString) throws MalformedURLException, IOException {
    if (urlString != null)
    {
        URL url = new URL(urlString);

        URLConnection connection = url.openConnection();
        connection.setUseCaches(true);
        Object response = connection.getContent();
        if (response instanceof InputStream) {
            return (InputStream) response;
        }
    }
    return null;
}

Ответы [ 2 ]

3 голосов
/ 17 мая 2011

Если вы используете ListView, вы должны понимать, что ListView повторно использует ImageView экземпляры. Если ваша загрузка занимает слишком много времени и определенный ImageView, к которому он был привязан, был повторно использован для другого Image, тогда ваша реализация перезапишет правильное изображение старым изображением. Вам необходимо:

  1. Измените метод fetchDrawableOnThread, чтобы пометить ImageView URL-адресом для извлечения: обычно вы используете setTag и сохраняете URL.
  2. Включите метод run после выборки InputStream, чтобы убедиться, что значение getTag в ImageView соответствует URL, который вы только что выбрали. Если они не совпадают, ListView повторно использовал этот экземпляр ImageView, и вам не следует отправлять Message для отображения изображения.

Вам также, вероятно, следует позвонить recycle куда-нибудь, чтобы очистить память растрового изображения, которую вы потребляете, перед установкой нового Drawable, но это более длинная история.

РЕДАКТИРОВАТЬ: история recycle на самом деле проще, чем я помнил (я посмотрел старый код). В основном, прежде чем обновлять Drawable для ImageView, убедитесь, что у него уже есть набор Drawable и действительно ли он вызывает recycle. Так что-то вроде этого:

ImageView iv = getImageView(); // fetch the image view somehow before you set it;
Drawable d = iv.getDrawable();
if(d != null){
     ((BitmapDrawable)iv.getDrawable()).getBitmap().recycle();
     iv.setImageBitmap(null);
}

Это восстановит память, использованную предыдущим Drawable, и должно помочь с проблемами исчерпания памяти.

1 голос
/ 23 мая 2012

Быстрое и простое решение, основанное на возможностях асинхронной загрузки и кэширования веб-представления:

String htmlSnippet = "<html><head/><body>" +
                     "<img height=\"50px\" width=\"50px\" src=\"" + item.getImageUrl() + "\" />" +
                     "</body></html>";
imageWebview.loadData( htmlSnippet, "text/html", "utf-8" );
...