Ленивая загрузка изображений в ListView - PullRequest
1845 голосов
/ 12 февраля 2009

Я использую ListView для отображения некоторых изображений и подписей, связанных с этими изображениями. Я получаю изображения из Интернета. Есть ли способ ленивой загрузки изображений, чтобы при отображении текста пользовательский интерфейс не блокировался и изображения отображались по мере загрузки?

Общее количество изображений не фиксировано.

Ответы [ 39 ]

1057 голосов
/ 18 февраля 2009

Вот то, что я создал для хранения изображений, которые мое приложение отображает в данный момент. Обратите внимание, что используемый здесь объект «Log» - это моя специальная оболочка для заключительного класса Log внутри Android.

package com.wilson.android.library;

/*
 Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements.  See the NOTICE file
distributed with this work for additional information
regarding copyright ownership.  The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License.  You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied.  See the License for the
specific language governing permissions and limitations
under the License.
*/
import java.io.IOException;

public class DrawableManager {
    private final Map<String, Drawable> drawableMap;

    public DrawableManager() {
        drawableMap = new HashMap<String, Drawable>();
    }

    public Drawable fetchDrawable(String urlString) {
        if (drawableMap.containsKey(urlString)) {
            return drawableMap.get(urlString);
        }

        Log.d(this.getClass().getSimpleName(), "image url:" + urlString);
        try {
            InputStream is = fetch(urlString);
            Drawable drawable = Drawable.createFromStream(is, "src");


            if (drawable != null) {
                drawableMap.put(urlString, drawable);
                Log.d(this.getClass().getSimpleName(), "got a thumbnail drawable: " + drawable.getBounds() + ", "
                        + drawable.getIntrinsicHeight() + "," + drawable.getIntrinsicWidth() + ", "
                        + drawable.getMinimumHeight() + "," + drawable.getMinimumWidth());
            } else {
              Log.w(this.getClass().getSimpleName(), "could not get thumbnail");
            }

            return drawable;
        } catch (MalformedURLException e) {
            Log.e(this.getClass().getSimpleName(), "fetchDrawable failed", e);
            return null;
        } catch (IOException e) {
            Log.e(this.getClass().getSimpleName(), "fetchDrawable failed", e);
            return null;
        }
    }

    public void fetchDrawableOnThread(final String urlString, final ImageView imageView) {
        if (drawableMap.containsKey(urlString)) {
            imageView.setImageDrawable(drawableMap.get(urlString));
        }

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

        Thread thread = new Thread() {
            @Override
            public void run() {
                //TODO : set imageView to a "pending" image
                Drawable drawable = fetchDrawable(urlString);
                Message message = handler.obtainMessage(1, drawable);
                handler.sendMessage(message);
            }
        };
        thread.start();
    }

    private InputStream fetch(String urlString) throws MalformedURLException, IOException {
        DefaultHttpClient httpClient = new DefaultHttpClient();
        HttpGet request = new HttpGet(urlString);
        HttpResponse response = httpClient.execute(request);
        return response.getEntity().getContent();
    }
}
1010 голосов
/ 18 июня 2010

Я сделал простую демонстрацию ленивого списка (расположенного на GitHub) с изображениями.

Основное использование

ImageLoader imageLoader=new ImageLoader(context); ...
imageLoader.DisplayImage(url, imageView); 

Не забудьте добавить следующие разрешения для вашего AndroidManifest.xml:

 <uses-permission android:name="android.permission.INTERNET"/>
 <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> Please

создайте только один экземпляр ImageLoader и используйте его все вокруг приложение. Таким образом, кэширование изображений будет намного более эффективным.

Это может быть полезно кому-то. Он загружает изображения в фоновом потоке. Изображения кэшируются на SD-карте и в памяти. Реализация кеша очень проста и ее достаточно для демонстрации. Я декодирую изображения с помощью inSampleSize, чтобы уменьшить потребление памяти. Я также стараюсь правильно обрабатывать переработанные виды.

Alt text

546 голосов
/ 19 декабря 2011

Я рекомендую инструмент с открытым исходным кодом Универсальный загрузчик изображений . Первоначально он основан на проекте Федора Власова LazyList и с тех пор значительно улучшился.

  • Многопоточная загрузка изображений
  • Возможность широкой настройки конфигурации ImageLoader (исполнители потоков, загрузчик, декодер, память и дисковый кеш, опции отображения изображений и другие)
  • Возможность кэширования изображений в памяти и / или в системном файле устройства (или на SD-карте)
  • Возможность "прослушать" процесс загрузки
  • Возможность настроить каждый вызов отображения изображения с раздельными параметрами
  • Поддержка виджетов
  • Поддержка Android 2.0+

154 голосов
/ 12 августа 2010

Многопоточность для производительности , учебное пособие Жиля Дебунна.

Это из блога разработчиков Android. Предлагаемый код использует:

  • AsyncTasks.
  • Твердый, ограниченный размер, FIFO cache.
  • Мягкий, легко garbage collect -эшированный кеш.
  • A заполнитель Drawable во время загрузки.

enter image description here

105 голосов
/ 05 мая 2010

Обновление: обратите внимание, что этот ответ сейчас довольно неэффективен. Сборщик мусора активно работает с SoftReference и WeakReference, поэтому этот код НЕ подходит для новых приложений. (Вместо этого попробуйте библиотеки, такие как Universal Image Loader , предлагаемый в других ответах.)

Спасибо Джеймсу за код и Бао-Лонгу за предложение использовать SoftReference. Я реализовал изменения SoftReference в коде Джеймса. К сожалению, из-за SoftReferences мои изображения собирались слишком быстро. В моем случае это было нормально без SoftReference, потому что мой размер списка ограничен, а мои изображения маленькие.

Существует год назад обсуждение SoftReferences в группах Google: ссылка на тему . В качестве решения слишком ранней сборки мусора они предлагают возможность вручную устанавливать размер кучи виртуальной машины с помощью dalvik.system.VMRuntime.setMinimumHeapSize (), что не очень привлекательно для меня.

public DrawableManager() {
    drawableMap = new HashMap<String, SoftReference<Drawable>>();
}

public Drawable fetchDrawable(String urlString) {
    SoftReference<Drawable> drawableRef = drawableMap.get(urlString);
    if (drawableRef != null) {
        Drawable drawable = drawableRef.get();
        if (drawable != null)
            return drawable;
        // Reference has expired so remove the key from drawableMap
        drawableMap.remove(urlString);
    }

    if (Constants.LOGGING) Log.d(this.getClass().getSimpleName(), "image url:" + urlString);
    try {
        InputStream is = fetch(urlString);
        Drawable drawable = Drawable.createFromStream(is, "src");
        drawableRef = new SoftReference<Drawable>(drawable);
        drawableMap.put(urlString, drawableRef);
        if (Constants.LOGGING) Log.d(this.getClass().getSimpleName(), "got a thumbnail drawable: " + drawable.getBounds() + ", "
                + drawable.getIntrinsicHeight() + "," + drawable.getIntrinsicWidth() + ", "
                + drawable.getMinimumHeight() + "," + drawable.getMinimumWidth());
        return drawableRef.get();
    } catch (MalformedURLException e) {
        if (Constants.LOGGING) Log.e(this.getClass().getSimpleName(), "fetchDrawable failed", e);
        return null;
    } catch (IOException e) {
        if (Constants.LOGGING) Log.e(this.getClass().getSimpleName(), "fetchDrawable failed", e);
        return null;
    }
}

public void fetchDrawableOnThread(final String urlString, final ImageView imageView) {
    SoftReference<Drawable> drawableRef = drawableMap.get(urlString);
    if (drawableRef != null) {
        Drawable drawable = drawableRef.get();
        if (drawable != null) {
            imageView.setImageDrawable(drawableRef.get());
            return;
        }
        // Reference has expired so remove the key from drawableMap
        drawableMap.remove(urlString);
    }

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

    Thread thread = new Thread() {
        @Override
        public void run() {
            //TODO : set imageView to a "pending" image
            Drawable drawable = fetchDrawable(urlString);
            Message message = handler.obtainMessage(1, drawable);
            handler.sendMessage(message);
        }
    };
    thread.start();
}
93 голосов
/ 04 апреля 2014

Пикассо

Используйте Библиотеку Пикассо Джейка Уортона. (Идеальная библиотека загрузки изображений от разработчика ActionBarSherlock)

Мощная библиотека загрузки и кэширования изображений для Android.

Изображения добавляют столь необходимый контекст и визуальный стиль приложениям Android. Picasso позволяет без проблем загружать изображения в ваше приложение - часто в одной строке кода!

Picasso.with(context).load("http://i.imgur.com/DvpvklR.png").into(imageView);

Многие распространенные ошибки при загрузке изображений на Android обрабатываются автоматически Пикассо:

Обработка утилизации ImageView и отмена загрузки в адаптере. Сложные преобразования изображений с минимальным использованием памяти. Автоматическое кеширование памяти и диска.

Библиотека Пикассо Джейка Уортона

Glide

Glide - это быстрая и эффективная среда управления мультимедиа с открытым исходным кодом для Android, которая объединяет декодирование мультимедиа, кэширование памяти и диска и пул ресурсов в простой и удобный интерфейс.

Glide поддерживает выборку, декодирование и отображение видеоизображений, изображений и анимированных GIF-файлов. Glide включает гибкий API, который позволяет разработчикам подключаться практически к любому сетевому стеку. По умолчанию Glide использует собственный стек на основе HttpUrlConnection, но вместо этого включает в себя подключаемые библиотеки утилит для проекта Google Volley или библиотеки OkHttp от Square.

Glide.with(this).load("http://goo.gl/h8qOq7").into(imageView);

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

Библиотека загрузки скользящих изображений

Фреска от Facebook

Fresco - мощная система для отображения изображений в приложениях Android.

Fresco заботится о загрузке и отображении изображений, так что вам не нужно. Он будет загружать изображения из сети, локального хранилища или локальных ресурсов и отображать заполнитель до тех пор, пока изображение не будет получено. Имеет два уровня кеша; один в памяти и другой во внутренней памяти.

Фреска Гитхуб

В Android 4.x и ниже Fresco помещает изображения в специальную область памяти Android. Это позволяет вашему приложению работать быстрее - и гораздо реже страдать от страшного OutOfMemoryError.

Документация на фреску

79 голосов
/ 22 октября 2011

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

  1. Я понял, что работать с рисованными объектами быстрее, чем с растровыми изображениями, поэтому вместо них я использую рисованные элементы

  2. Использование SoftReference прекрасно, но из-за этого кэшированное изображение будет удаляться слишком часто, поэтому я добавил Связанный список, который содержит ссылки на изображения, предотвращая удаление изображения до достижения предопределенного размера

  3. Чтобы открыть InputStream, я использовал java.net.URLConnection, который позволяет мне использовать веб-кеш (сначала нужно установить кеш ответов, но это уже другая история)

Мой код:

import java.util.Map; 
import java.util.HashMap; 
import java.util.LinkedList; 
import java.util.Collections; 
import java.util.WeakHashMap; 
import java.lang.ref.SoftReference; 
import java.util.concurrent.Executors; 
import java.util.concurrent.ExecutorService; 
import android.graphics.drawable.Drawable;
import android.widget.ImageView;
import android.os.Handler;
import android.os.Message;
import java.io.InputStream;
import java.net.MalformedURLException; 
import java.io.IOException; 
import java.net.URL;
import java.net.URLConnection;

public class DrawableBackgroundDownloader {    

private final Map<String, SoftReference<Drawable>> mCache = new HashMap<String, SoftReference<Drawable>>();   
private final LinkedList <Drawable> mChacheController = new LinkedList <Drawable> ();
private ExecutorService mThreadPool;  
private final Map<ImageView, String> mImageViews = Collections.synchronizedMap(new WeakHashMap<ImageView, String>());  

public static int MAX_CACHE_SIZE = 80; 
public int THREAD_POOL_SIZE = 3;

/**
 * Constructor
 */
public DrawableBackgroundDownloader() {  
    mThreadPool = Executors.newFixedThreadPool(THREAD_POOL_SIZE);  
}  


/**
 * Clears all instance data and stops running threads
 */
public void Reset() {
    ExecutorService oldThreadPool = mThreadPool;
    mThreadPool = Executors.newFixedThreadPool(THREAD_POOL_SIZE);
    oldThreadPool.shutdownNow();

    mChacheController.clear();
    mCache.clear();
    mImageViews.clear();
}  

public void loadDrawable(final String url, final ImageView imageView,Drawable placeholder) {  
    mImageViews.put(imageView, url);  
    Drawable drawable = getDrawableFromCache(url);  

    // check in UI thread, so no concurrency issues  
    if (drawable != null) {  
        //Log.d(null, "Item loaded from mCache: " + url);  
        imageView.setImageDrawable(drawable);  
    } else {  
        imageView.setImageDrawable(placeholder);  
        queueJob(url, imageView, placeholder);  
    }  
} 


private Drawable getDrawableFromCache(String url) {  
    if (mCache.containsKey(url)) {  
        return mCache.get(url).get();  
    }  

    return null;  
}

private synchronized void putDrawableInCache(String url,Drawable drawable) {  
    int chacheControllerSize = mChacheController.size();
    if (chacheControllerSize > MAX_CACHE_SIZE) 
        mChacheController.subList(0, MAX_CACHE_SIZE/2).clear();

    mChacheController.addLast(drawable);
    mCache.put(url, new SoftReference<Drawable>(drawable));

}  

private void queueJob(final String url, final ImageView imageView,final Drawable placeholder) {  
    /* Create handler in UI thread. */  
    final Handler handler = new Handler() {  
        @Override  
        public void handleMessage(Message msg) {  
            String tag = mImageViews.get(imageView);  
            if (tag != null && tag.equals(url)) {
                if (imageView.isShown())
                    if (msg.obj != null) {
                        imageView.setImageDrawable((Drawable) msg.obj);  
                    } else {  
                        imageView.setImageDrawable(placeholder);  
                        //Log.d(null, "fail " + url);  
                    } 
            }  
        }  
    };  

    mThreadPool.submit(new Runnable() {  
        @Override  
        public void run() {  
            final Drawable bmp = downloadDrawable(url);
            // if the view is not visible anymore, the image will be ready for next time in cache
            if (imageView.isShown())
            {
                Message message = Message.obtain();  
                message.obj = bmp;
                //Log.d(null, "Item downloaded: " + url);  

                handler.sendMessage(message);
            }
        }  
    });  
}  



private Drawable downloadDrawable(String url) {  
    try {  
        InputStream is = getInputStream(url);

        Drawable drawable = Drawable.createFromStream(is, url);
        putDrawableInCache(url,drawable);  
        return drawable;  

    } catch (MalformedURLException e) {  
        e.printStackTrace();  
    } catch (IOException e) {  
        e.printStackTrace();  
    }  

    return null;  
}  


private InputStream getInputStream(String urlString) throws MalformedURLException, IOException {
    URL url = new URL(urlString);
    URLConnection connection;
    connection = url.openConnection();
    connection.setUseCaches(true); 
    connection.connect();
    InputStream response = connection.getInputStream();

    return response;
}
}
77 голосов
/ 28 декабря 2012

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

image

63 голосов
/ 22 мая 2014

1. Picasso позволяет без проблем загружать изображения в ваше приложение - часто в одной строке кода!

Использование Gradle:

implementation 'com.squareup.picasso:picasso:2.71828'

Всего одна строка кода!

Picasso.get().load("http://i.imgur.com/DvpvklR.png").into(imageView);

2. Glide Библиотека загрузки и кэширования изображений для Android, ориентированная на плавную прокрутку

Использование Gradle:

repositories {
  mavenCentral() 
  google()
}

dependencies {
   implementation 'com.github.bumptech.glide:glide:4.7.1'
   annotationProcessor 'com.github.bumptech.glide:compiler:4.7.1'
}

// Для простого просмотра:

  Glide.with(this).load("http://i.imgur.com/DvpvklR.png").into(imageView);

3. fresco - мощная система для отображения изображений в Android Applications.Fresco заботится о загрузке и отображении изображений, поэтому у вас нет к.

Начало работы с фреской

51 голосов
/ 27 августа 2011

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

Ленивая загрузка изображений в Listview Tutorial

...