Как лениво загружать изображения в ListView в Android

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

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

12.02.2009 15:59:00
Вы можете использовать AsyncImageView GreenDroid . Просто позвони setUrl.
Pascal Dimassimo 9.12.2011 15:11:52
Я использовал это. Это прекрасная реализация. Плохая новость в том, что AsyncImageView является частью большого проекта GreenDroid, который увеличивает размер вашего приложения даже в том случае, если вам нужен только AsyncImageView. Также, кажется, проект GreenDroid не обновляется с 2011 года.
borisstr 15.04.2012 11:06:07
Вы даже можете попробовать эту библиотеку: Android-http-image-manager, который, на мой взгляд, является лучшим для асинхронной загрузки изображений.
Ritesh Kumar Dubey 9.11.2012 11:43:54
Просто используйте Пикассо, он все сделает сам. 'Picasso.with (yourContext) .load (img src / path / drawable здесь) .into (imageView т.е. ваша цель);' Это оно!
Anuj Sharma 23.01.2014 06:40:59
попробуйте использовать: github.com/nostra13/Android-Universal-Image-Loader , эта библиотека очень быстрая и эффективная для
krunal patel 16.08.2014 05:44:44
30 ОТВЕТОВ
РЕШЕНИЕ

Вот то, что я создал для хранения изображений, которые мое приложение отображает в данный момент. Обратите внимание, что используемый здесь объект «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();
    }
}
1084
22.03.2019 13:02:14
Я думаю, что вы должны использовать SoftReferences, чтобы ваша программа никогда не вызывала OutOfMemoryException. Поскольку GC может очищать программные ссылки при увеличении размера кучи ... вы можете управлять своим собственным поколением, как через несколько секунд вы можете поместить свои изображения в этот список, и перед загрузкой вы должны убедиться, что если изображение существует, не загружайте его снова, а собирайте это из этого списка, а также положить его обратно в список мягких ссылок, и через некоторое время вы сможете очистить свой жесткий список :)
AZ_ 18.01.2011 08:08:32
Проект Google Shelves
AZ_ 18.01.2011 08:09:41
Не пропустите ли вы возврат, когда drawableMap содержит изображение ... без запуска потока извлечения?
Karussell 29.03.2011 22:06:40
Этот код имеет несколько проблем. Во-первых, вам следует кэшировать Drawables, что приведет к утечке памяти: stackoverflow.com/questions/7648740/… . Во-вторых, сам кеш никогда не очищается, поэтому он будет расти вечно, это очередная утечка памяти.
satur9nine 15.11.2011 05:35:45
никто не слышал о LRU Cache developer.android.com/training/displaying-bitmaps/…
Muhammad Babar 28.05.2013 07:26:38

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

Однако этот метод работает не очень хорошо, когда вы перерабатываете представления.

41
12.02.2009 20:07:14
Я использую нить для каждого изображения. Если вы отделите свою модель от своего представления, вы можете сохранить модель вне Activity (как в вашем классе 'application), чтобы сохранить их в кэше. Остерегайтесь нехватки ресурсов, если у вас много изображений.
James A Wilson 15.02.2009 00:40:11
Можете ли вы уточнить, пожалуйста. Я новичок в разработке Android. Спасибо за советы, хотя
lostInTransit 15.02.2009 14:23:55
Создание новой темы для каждого изображения не является эффективным решением. Вы можете получить много потоков в памяти и заморозить пользовательский интерфейс.
Fedor 1.07.2010 00:04:22
Федор, согласился, я обычно использую очередь и пул потоков, это лучший способ для начала.
jasonhudgins 4.07.2010 22:48:08

Обновление: обратите внимание, что этот ответ сейчас довольно неэффективен. Сборщик мусора активно работает с 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();
}
107
22.07.2013 00:17:58
Вы можете создавать поколения, такие как жесткое поколение и мягкое поколение. вы можете исправить время очистки кеша очистит все изображения, которые не были доступны в течение 3 секунд .. вы можете взглянуть на проект Google полки
AZ_ 18.01.2011 08:06:23
developer.android.com/reference/java/lang/ref/… Документ SoftReference содержит примечание о кэшировании, см. раздел «Избегайте мягких ссылок для кэширования». Большинство приложений должны использовать android.util.LruCache вместо мягких ссылок.
vokilam 26.02.2013 11:48:00
Я восхищаюсь вашим кодом, но теперь в новом Android O / S идет «агрессивный» сбор мусора. Держать слабую ссылку не имеет никакого смысла для меня.
j2emanue 21.07.2013 17:54:15
@ j2emanue Вы правы, как я пытался указать в верхней части моего ответа, SoftReferences - сборщик мусора слишком быстро. Я постараюсь отредактировать этот ответ, чтобы сделать его еще яснее.
TalkLittle 22.07.2013 00:13:50

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

Альтернативный текст

1024
22.03.2019 18:08:02
Спасибо, я использую слегка измененную версию вашего кода почти везде в своем проекте: code.google.com/p/vimeoid/source/browse/apk/src/com/fedorvlasov/…
shaman.sir 12.10.2010 18:18:02
Кто-нибудь рассматривал код Жиля Дебунна как альтернативу. Я вижу две большие разницы: 1) в кэшировании памяти и SD-карте и 2) использование AsyncTasks вместо пула потоков. android-developers.blogspot.com/2010/07/…
Richard 29.10.2010 20:39:35
Существует ошибка, иногда она возникает: 10-13 09: 58: 46.738: ОШИБКА / AndroidRuntime (24250): обработчик Uncaught: выход основного потока из-за невыполненного исключения 10-13 09: 58: 46.768: ERROR / AndroidRuntime (24250) : java.lang.ArrayIndexOutOfBoundsException: индекс массива вне диапазона: 0 10-13 09: 58: 46.768: ОШИБКА / AndroidRuntime (24250): в java.util.Vector.elementAt (Vector.java:331) 10-13 09: 58: 46.768: ОШИБКА / AndroidRuntime (24250): на java.util.Vector.get (Vector.java:445) 10-13 09: 58: 46.768: ОШИБКА / AndroidRuntime (24250): на com.my.app.image .ImageLoader $ PhotosQueue.Clean (ImageLoader.java:91)
Mikooos 13.10.2011 08:02:27
Я хотел бы добавить для новичков, как я, что если вы хотите добавить этот код в свой собственный проект, вы должны добавить разрешения внешнего кэша в манифесте. Спасибо
Adam 3.11.2011 22:29:35
@BruceHill Там нет фотографийQueue в последнем исходном коде. Вы, кажется, используете очень старую версию.
Fedor 28.03.2013 03:58:46

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

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

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

введите описание изображения здесь

158
13.03.2014 11:45:30
Он прекрасно работает и в 2.1. Только не используйте AndroidHttpClient.
Thomas Ahle 5.02.2011 02:22:08
@ thomas-ahle Спасибо, я видел, что AndroidHttpClient выдавал ошибки в 2.1, так как он реализован в 2.2, но на самом деле не пытался найти что-то еще, чтобы заменить его.
Adinia 9.02.2011 08:02:54
@ Адина Вы правы, я забыл. Однако в рецепте нет ничего, что нельзя было бы сделать с обычным HttpClient.
Thomas Ahle 22.02.2011 12:49:33
Я слышал в нескольких местах, что Google не рекомендует мягкие ссылки, потому что ядро ​​Android очень стремится собрать эти ссылки по сравнению с более ранними версиями системы.
Muhammad Ahmed AbuTalib 26.05.2014 07:46:35

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

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

52
4.07.2013 20:40:56

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

  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;
}
}
82
14.05.2012 09:37:15
Работает отлично! Кстати, в названии класса есть опечатка.
Mullins 7.12.2011 14:25:06
На случай, если это сэкономит кому-то время: 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;
Michael Reed 8.01.2012 05:01:58
Большое спасибо, это хорошая реализация. Я также добавил другой заполнитель для загрузки загружаемого объекта, чтобы пользователь мог получить некоторую обратную связь.
Juan Hernandez 7.02.2012 15:53:55
Также я думаю, что лучше использовать очередь LIFO в executorService (mThreadPool) вместо FIFO по умолчанию, так что последние запрошенные изображения (которые, вероятно, являются видимыми) загружаются первыми. См. Stackoverflow.com/questions/4620061/how-to-create-lifo-executor
Juan Hernandez 7.02.2012 16:19:21
@MichaelReed, если вы пользователь Eclipse, я рекомендую использовать Ctrl-Shift-O (это буква O, а не цифра 0). Он автоматизирует процесс добавления импорта и организует их для вас. Если вы на Mac, используйте вместо этого Command-Shift-O.
SilithCrowe 20.03.2012 15:21:45

Я просто хочу добавить еще один хороший пример, адаптеры XML . Так как он используется Google, и я также использую ту же логику, чтобы избежать ошибки OutOfMemory.

В основном этот ImageDownloader является вашим ответом (так как он охватывает большинство ваших требований). Некоторые вы также можете реализовать в этом.

32
22.11.2012 19:32:50
Класс ImageDownloader не соблюдается: см. Решение ниже code.google.com/p/parleys-android-nextgen/issues/detail?id=1
Sam 4.02.2012 04:48:55

Я рекомендую инструмент с открытым исходным кодом Universal Image Loader . Первоначально он основан на проекте Федора Власова LazyList и с тех пор значительно улучшился.

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

551
28.08.2014 23:19:24
если вы ищете отличный код «загрузки изображений», который его создатель рассматривает и поддерживает как своего драгоценного ребенка (а не единовременное решение), вы просто нашли его; большие взлеты Nostra
AndroidGecko 29.03.2012 11:12:20
Действительно отличная работа, но она немного отстает от моего списка. Если бы вы могли сказать лучшую сборку 'ImageLoader' с лучшей производительностью ... или, может быть, это из-за 'DisplayImageOptions'.
dinigo 21.10.2012 16:58:26
Мне очень нравится это gridview, но я не могу использовать его для моего урока. Я новичок в Android. Я делаю приложение о gridview, но мое приложение было targetSdkVersion 15, поэтому мне нужно использовать Asynctask для процесса в фоновом потоке. Я использовал этот вид сетки, но он не работает. как я могу использовать его в targetSdkVersion 15?
kongkea 25.10.2012 07:30:52
Вы можете создать отдельный вопрос с тегом [universal-image-loader]
nostra13 25.10.2012 11:03:48
После прочтения официальных документов по Android и попыток сделать это самому, я почти уверен, что эта библиотека - конец загрузки изображений. Невозможно поднять голос.
Core 30.12.2013 18:06:55

Взгляните на Shutterbug , облегченный порт Applidium SDWebImage (хорошая библиотека на iOS) для Android. Он поддерживает асинхронное кэширование, сохраняет сбойные URL-адреса, хорошо обрабатывает параллелизм и содержит полезные подклассы.

Запросы на извлечение (и сообщения об ошибках) также приветствуются!

20
2.11.2012 17:42:16

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

25
16.03.2014 13:19:04

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

78
2.05.2013 21:01:16
Извините, я указал только на один класс для приложения Google IO (и я слишком поздно для редактирования). Вы действительно должны изучить все их служебные классы загрузки и кэширования изображений, которые вы можете найти в том же пакете, что и класс кэширования .
mkuech 31.01.2013 05:28:29
Кто-нибудь порекомендует взять файлы DiskLruCache, Image * .java из папки util приложения iosched, чтобы помочь с обработкой загрузки / кэширования изображений для просмотра списка? Я имею в виду, что определенно стоит следовать интерактивным руководствам для разработчиков по этому вопросу, но эти классы (от iosched) идут немного дальше по шаблону.
Gautam 25.04.2013 16:58:08

У Novoda также есть отличная библиотека для загрузки ленивых изображений, и многие приложения, такие как Songkick, Podio, SecretDJ и ImageSearch, используют свою библиотеку.

Их библиотека находится здесь, на Github, и у них также есть довольно активный трекер . Их проект, кажется, тоже довольно активен: более 300 коммитов на момент написания этого ответа.

15
12.01.2013 11:00:25
На самом деле Novoda - отличная библиотека, но ... иногда вам не нужна огромная библиотека, а только простой подход к решению. Вот почему LazyList в Github так хорош, если ваши приложения показывают только изображение в listView и не являются основной функцией вашего приложения, просто еще одно занятие, которое я предпочел бы использовать что-то более светлое. В противном случае, если вы знаете, что вам придется часто пользоваться и является частью ядра, попробуйте Novoda.
Nicolas Jafelle 15.04.2013 15:34:20

Ну, время загрузки изображения из Интернета имеет много решений. Вы также можете использовать библиотеку Android-Query . Это даст вам всю необходимую активность. Убедитесь, что вы хотите сделать, и прочитайте вики-страницу библиотеки. И решить ограничение загрузки изображения.

Это мой код:

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    View v = convertView;
    if (v == null) {
        LayoutInflater vi = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        v = vi.inflate(R.layout.row, null);
    }

    ImageView imageview = (ImageView) v.findViewById(R.id.icon);
    AQuery aq = new AQuery(convertView);

    String imageUrl = "http://www.vikispot.com/z/images/vikispot/android-w.png";

    aq.id(imageview).progress(this).image(imageUrl, true, true, 0, 0, new BitmapAjaxCallback() {
        @Override
        public void callback(String url, ImageView iv, Bitmap bm, AjaxStatus status) {
            iv.setImageBitmap(bm);
        }
    ));

    return v;
}

Это должно решить вашу проблему с отложенной загрузкой.

26
24.04.2015 21:10:00
Хорошая работа для меня, но нужен файл Jar для включения в ваш проект. Вы можете скачать этот JAR-файл отсюда. AQuery androidAQuery = new AQuery (this); ссылка: code.google.com/archive/p/android-query/downloads
Selim Raza 24.04.2017 09:13:58

Проверьте мой форк LazyList . По сути, я улучшаю LazyList, задерживая вызов ImageView и создаю два метода:

  1. Когда вам нужно поставить что-то вроде «Загрузка изображения ...»
  2. Когда вам нужно показать загруженное изображение.

Я также улучшил ImageLoader, внедрив в этот объект синглтон .

13
16.03.2014 13:24:50

DroidParts имеет ImageFetcher, который требует нулевой конфигурации, чтобы начать.

  • Использование диска и в памяти Наименее недавно использовавшихся (LRU) кэша.
  • Эффективно декодирует изображения.
  • Поддерживает изменение растровых изображений в фоновом потоке.
  • Имеет простое затухание.
  • Имеет обратный вызов процесса загрузки изображения.

Клон DroidPartsGram для примера:

Введите описание изображения здесь

16
16.03.2014 13:26:19
Привет, я посмотрел примеры кода, но у меня возникли проблемы с использованием ImageFetcher с ArrayAdapter, не могли бы вы взглянуть на мой вопрос? stackoverflow.com/questions/21089147/… Спасибо =]
masha 13.01.2014 10:37:02

Я использовал NetworkImageView из новой библиотеки Android Volley com.android.volley.toolbox.NetworkImageView, и, похоже, он работает довольно хорошо. По-видимому, это тот же самый вид, который используется в Google Play и других новых приложениях Google. Определенно стоит проверить.

29
16.03.2014 13:28:38
Я думаю, что это лучшее решение - другие ответы очень старые - залп действительно быстрый и в сочетании с jake warthons disklrucache - это идеальное решение - я пробовал много других, но ни один из них не стабилен и быстр как залп
Alexander Sidikov Pfeif 7.09.2014 22:19:10
public class ImageDownloader {

Map<String, Bitmap> imageCache;

public ImageDownloader() {
    imageCache = new HashMap<String, Bitmap>();

}

// download function
public void download(String url, ImageView imageView) {
    if (cancelPotentialDownload(url, imageView)) {

        // Caching code right here
        String filename = String.valueOf(url.hashCode());
        File f = new File(getCacheDirectory(imageView.getContext()),
                filename);

        // Is the bitmap in our memory cache?
        Bitmap bitmap = null;

        bitmap = (Bitmap) imageCache.get(f.getPath());

        if (bitmap == null) {

            bitmap = BitmapFactory.decodeFile(f.getPath());

            if (bitmap != null) {
                imageCache.put(f.getPath(), bitmap);
            }

        }
        // No? download it
        if (bitmap == null) {
            try {
                BitmapDownloaderTask task = new BitmapDownloaderTask(
                        imageView);
                DownloadedDrawable downloadedDrawable = new DownloadedDrawable(
                        task);
                imageView.setImageDrawable(downloadedDrawable);
                task.execute(url);
            } catch (Exception e) {
                Log.e("Error==>", e.toString());
            }

        } else {
            // Yes? set the image
            imageView.setImageBitmap(bitmap);
        }
    }
}

// cancel a download (internal only)
private static boolean cancelPotentialDownload(String url,
        ImageView imageView) {
    BitmapDownloaderTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView);

    if (bitmapDownloaderTask != null) {
        String bitmapUrl = bitmapDownloaderTask.url;
        if ((bitmapUrl == null) || (!bitmapUrl.equals(url))) {
            bitmapDownloaderTask.cancel(true);
        } else {
            // The same URL is already being downloaded.
            return false;
        }
    }
    return true;
}

// gets an existing download if one exists for the imageview
private static BitmapDownloaderTask getBitmapDownloaderTask(
        ImageView imageView) {
    if (imageView != null) {
        Drawable drawable = imageView.getDrawable();
        if (drawable instanceof DownloadedDrawable) {
            DownloadedDrawable downloadedDrawable = (DownloadedDrawable) drawable;
            return downloadedDrawable.getBitmapDownloaderTask();
        }
    }
    return null;
}

// our caching functions
// Find the dir to save cached images
private static File getCacheDirectory(Context context) {
    String sdState = android.os.Environment.getExternalStorageState();
    File cacheDir;

    if (sdState.equals(android.os.Environment.MEDIA_MOUNTED)) {
        File sdDir = android.os.Environment.getExternalStorageDirectory();

        // TODO : Change your diretcory here
        cacheDir = new File(sdDir, "data/ToDo/images");
    } else
        cacheDir = context.getCacheDir();

    if (!cacheDir.exists())
        cacheDir.mkdirs();
    return cacheDir;
}

private void writeFile(Bitmap bmp, File f) {
    FileOutputStream out = null;

    try {
        out = new FileOutputStream(f);
        bmp.compress(Bitmap.CompressFormat.PNG, 80, out);
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        try {
            if (out != null)
                out.close();
        } catch (Exception ex) {
        }
    }
}

// download asynctask
public class BitmapDownloaderTask extends AsyncTask<String, Void, Bitmap> {
    private String url;
    private final WeakReference<ImageView> imageViewReference;

    public BitmapDownloaderTask(ImageView imageView) {
        imageViewReference = new WeakReference<ImageView>(imageView);
    }

    @Override
    // Actual download method, run in the task thread
    protected Bitmap doInBackground(String... params) {
        // params comes from the execute() call: params[0] is the url.
        url = (String) params[0];
        return downloadBitmap(params[0]);
    }

    @Override
    // Once the image is downloaded, associates it to the imageView
    protected void onPostExecute(Bitmap bitmap) {
        if (isCancelled()) {
            bitmap = null;
        }

        if (imageViewReference != null) {
            ImageView imageView = imageViewReference.get();
            BitmapDownloaderTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView);
            // Change bitmap only if this process is still associated with
            // it
            if (this == bitmapDownloaderTask) {
                imageView.setImageBitmap(bitmap);

                // cache the image

                String filename = String.valueOf(url.hashCode());
                File f = new File(
                        getCacheDirectory(imageView.getContext()), filename);

                imageCache.put(f.getPath(), bitmap);

                writeFile(bitmap, f);
            }
        }
    }

}

static class DownloadedDrawable extends ColorDrawable {
    private final WeakReference<BitmapDownloaderTask> bitmapDownloaderTaskReference;

    public DownloadedDrawable(BitmapDownloaderTask bitmapDownloaderTask) {
        super(Color.WHITE);
        bitmapDownloaderTaskReference = new WeakReference<BitmapDownloaderTask>(
                bitmapDownloaderTask);
    }

    public BitmapDownloaderTask getBitmapDownloaderTask() {
        return bitmapDownloaderTaskReference.get();
    }
}

// the actual download code
static Bitmap downloadBitmap(String url) {
    HttpParams params = new BasicHttpParams();
    params.setParameter(CoreProtocolPNames.PROTOCOL_VERSION,
            HttpVersion.HTTP_1_1);
    HttpClient client = new DefaultHttpClient(params);
    final HttpGet getRequest = new HttpGet(url);

    try {
        HttpResponse response = client.execute(getRequest);
        final int statusCode = response.getStatusLine().getStatusCode();
        if (statusCode != HttpStatus.SC_OK) {
            Log.w("ImageDownloader", "Error " + statusCode
                    + " while retrieving bitmap from " + url);
            return null;
        }

        final HttpEntity entity = response.getEntity();
        if (entity != null) {
            InputStream inputStream = null;
            try {
                inputStream = entity.getContent();
                final Bitmap bitmap = BitmapFactory
                        .decodeStream(inputStream);
                return bitmap;
            } finally {
                if (inputStream != null) {
                    inputStream.close();
                }
                entity.consumeContent();
            }
        }
    } catch (Exception e) {
        // Could provide a more explicit error message for IOException or
        // IllegalStateException
        getRequest.abort();
        Log.w("ImageDownloader", "Error while retrieving bitmap from "
                + url + e.toString());
    } finally {
        if (client != null) {
            // client.close();
        }
    }
    return null;
 }
}
4
21.10.2013 12:21:40

У меня была эта проблема и я реализовал lruCache. Я считаю, что вам нужен API 12 и выше или использовать библиотеку v4 совместимости. lurCache - это быстрая память, но она также имеет бюджет, поэтому, если вы беспокоитесь об этом, вы можете использовать дисковый кэш ... Все это описано в разделе Кэширование растровых изображений .

Теперь я предоставлю свою реализацию, которая является единственной, которую я вызываю из любого места, например:

//Where the first is a string and the other is a imageview to load.

DownloadImageTask.getInstance().loadBitmap(avatarURL, iv_avatar);

Вот идеальный код для кеширования и последующего вызова вышеуказанного в getView адаптера при получении веб-изображения:

public class DownloadImageTask {

    private LruCache<String, Bitmap> mMemoryCache;

    /* Create a singleton class to call this from multiple classes */

    private static DownloadImageTask instance = null;

    public static DownloadImageTask getInstance() {
        if (instance == null) {
            instance = new DownloadImageTask();
        }
        return instance;
    }

    //Lock the constructor from public instances
    private DownloadImageTask() {

        // Get max available VM memory, exceeding this amount will throw an
        // OutOfMemory exception. Stored in kilobytes as LruCache takes an
        // int in its constructor.
        final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);

        // Use 1/8th of the available memory for this memory cache.
        final int cacheSize = maxMemory / 8;

        mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
            @Override
            protected int sizeOf(String key, Bitmap bitmap) {
                // The cache size will be measured in kilobytes rather than
                // number of items.
                return bitmap.getByteCount() / 1024;
            }
        };
    }

    public void loadBitmap(String avatarURL, ImageView imageView) {
        final String imageKey = String.valueOf(avatarURL);

        final Bitmap bitmap = getBitmapFromMemCache(imageKey);
        if (bitmap != null) {
            imageView.setImageBitmap(bitmap);
        } else {
            imageView.setImageResource(R.drawable.ic_launcher);

            new DownloadImageTaskViaWeb(imageView).execute(avatarURL);
        }
    }

    private void addBitmapToMemoryCache(String key, Bitmap bitmap) {
        if (getBitmapFromMemCache(key) == null) {
            mMemoryCache.put(key, bitmap);
        }
    }

    private Bitmap getBitmapFromMemCache(String key) {
        return mMemoryCache.get(key);
    }

    /* A background process that opens a http stream and decodes a web image. */

    class DownloadImageTaskViaWeb extends AsyncTask<String, Void, Bitmap> {
        ImageView bmImage;

        public DownloadImageTaskViaWeb(ImageView bmImage) {
            this.bmImage = bmImage;
        }

        protected Bitmap doInBackground(String... urls) {

            String urldisplay = urls[0];
            Bitmap mIcon = null;
            try {
                InputStream in = new java.net.URL(urldisplay).openStream();
                mIcon = BitmapFactory.decodeStream(in);

            } 
            catch (Exception e) {
                Log.e("Error", e.getMessage());
                e.printStackTrace();
            }

            addBitmapToMemoryCache(String.valueOf(urldisplay), mIcon);

            return mIcon;
        }

        /* After decoding we update the view on the main UI. */
        protected void onPostExecute(Bitmap result) {
            bmImage.setImageBitmap(result);
        }
    }
}
4
16.03.2014 13:34:56

Я могу рекомендовать другой способ, который работает как шарм: Android Query.

Вы можете скачать этот файл JAR отсюда

AQuery androidAQuery = new AQuery(this);

Например:

androidAQuery.id(YOUR IMAGEVIEW).image(YOUR IMAGE TO LOAD, true, true, getDeviceWidth(), ANY DEFAULT IMAGE YOU WANT TO SHOW);

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

7
16.03.2014 13:36:00

Я использую droidQuery . Существует два механизма загрузки изображения из URL. Первый (сокращение) это просто:

$.with(myView).image(url);

Это может быть добавлен в ArrayAdapter«S getView(...)метод очень легко.


Длинный метод даст намного больший контроль и имеет опции, которые даже не обсуждались здесь (такие как кэширование и обратные вызовы), но здесь можно найти базовую реализацию, которая определяет размер вывода как 200px x 200px:

$.ajax(new AjaxOptions().url(url)
    .type("GET")
    .dataType("image")
    .imageWidth(200).imageHeight(200)
    .success(new Function() {
        @Override
        public void invoke($ droidQuery, Object... params) {
            myImageView.setImageBitmap((Bitmap) params[0]);
        }
    })
    .error(new Function() {
        @Override
        public void invoke($ droidQuery, Object... params) {
            AjaxError e = (AjaxError) params[0];
            Log.e("$", "Error " + e.status + ": " + e.error);
        }
    })
);
3
24.04.2015 20:51:42

Вы можете попробовать библиотеку Aquery Android для ленивой загрузки изображения и просмотра списка ... Код ниже может помочь вам ..... скачать библиотеку отсюда .

AQuery aq = new AQuery(mContext);
aq.id(R.id.image1).image("http://data.whicdn.com/images/63995806/original.jpg");
4
20.03.2014 04:22:17

Это общая проблема на Android, которая была решена многими людьми многими способами. На мой взгляд, лучшее решение, которое я видел, - это относительно новая библиотека под названием Picasso . Вот основные моменты:

  • Открытый исходный код, но во главе Jake Whartonс известностью ActionBarSherlock .
  • Асинхронная загрузка изображений из сетевых ресурсов или ресурсов приложения с помощью одной строки кода
  • Автоматическое ListViewобнаружение
  • Автоматическое кеширование дисков и памяти
  • Может сделать пользовательские преобразования
  • Много настраиваемых опций
  • Супер простой API
  • Часто обновляется
29
15.10.2013 23:22:19

Дайте Аквери попробовать. Он имеет удивительно простые методы для асинхронной загрузки и кэширования изображений.

7
16.03.2014 13:37:22

URLImageViewHelper - это удивительная библиотека, которая поможет вам сделать это.

7
16.03.2014 13:37:45

Пикассо

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

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

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

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

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

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

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

скольжение

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

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

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

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

Библиотека загрузки изображений Glide

Фреска от Facebook

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

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

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

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

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

96
11.08.2015 18:02:34
Picasso - это библиотека, разработанная Square
LordRaydenMK 16.05.2018 21:03:32

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. Fresco заботится о загрузке и отображении изображений, поэтому вам не нужно этого делать.

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

65
25.07.2018 10:58:53
Этот учебник может помочь вам больше для PICASOO: - androidtutorialshub.com/… и GLIDE: - androidtutorialshub.com/…
lalit vasan 10.05.2017 06:54:29

В некоторых ответах уже упоминалось использование различных библиотек изображений, таких как Universal Image Loader, androidimageloader и т. Д. Это старый вопрос, но для тех, кто все еще ищет что-то подобное, есть несколько таких библиотек для загрузки / кэширования изображений.

3
8.06.2014 09:04:32

Другой способ сделать это через ваш адаптер в потоке в вашем методе getView ():

Thread pics_thread = new Thread(new Runnable() {
    @Override
    public void run() {
        Bitmap bitmap = getPicture(url);
        if(bitmap != null) {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    holder.imageview.setImageBitmap(bitmap);            
                    adapter.notifyDataSetChanged();
                }                       
            });             
        }       
    }                       
});

pics_thread.start();

Конечно, вы всегда должны кэшировать свои изображения, чтобы избежать дополнительных операций, вы можете поместить свои изображения в массив HashMap, проверить, существует ли изображение в массиве, если нет, продолжить работу с потоком или загрузить изображение из вашего массива HashMap. Также всегда проверяйте, что у вас нет утечек памяти, растровые изображения и графические объекты часто сильно загружают память. Это зависит от вас, чтобы оптимизировать ваш код.

4
24.04.2015 21:01:40
Конечно, мне нравится получать растровые изображения в другом потоке. Но единственная проблема, с которой я столкнулся, имея этот код в getView (), это то, что для нескольких изображений будет запущено много потоков. И getView может попытаться загрузить много или несколько изображений одновременно.
The Original Android 7.06.2015 08:31:02

Просто быстрый совет для тех, кто не уверен в том, какую библиотеку использовать для отложенной загрузки изображений:

Есть четыре основных способа.

  1. DIY => Не лучшее решение, но для нескольких изображений и если вы хотите обойтись без использования других библиотек

  2. Volley's Lazy Загрузка библиотеки => От парней на андроид. Это хорошо и все, но плохо документировано и, следовательно, является проблемой для использования.

  3. Пикассо: простое решение, которое просто работает, вы даже можете указать точный размер изображения, которое вы хотите ввести. Он очень прост в использовании, но может быть не очень «производительным» для приложений, которые имеют дело с огромным количеством изображений.

  4. UIL: лучший способ ленивой загрузки изображений. Вы можете кэшировать изображения (вам, конечно, нужно разрешение), один раз инициализировать загрузчик, а затем выполнить свою работу. Самая зрелая библиотека асинхронной загрузки изображений, которую я когда-либо видел.

16
2.12.2015 05:23:43