Питання Ляле навантаження зображень у ListView


Я використовую a ListView щоб відобразити деякі зображення та підписи, пов'язані з цими зображеннями. Я отримую зображення з Інтернету. Чи є спосіб ненесабельний завантаження зображень так, поки текст відображається, користувальницький інтерфейс не заблоковано, а зображення відображаються під час завантаження?

Загальна кількість зображень не фіксується.


1733
2018-02-12 15:59


походження


Ви можете використовувати AsyncImageView GreenDroid. Просто дзвоніть setUrl. - Pascal Dimassimo
Я його використав. Це чудова реалізація. Неправильні новини про те, що AsyncImageView є частиною великого проекту GreenDroid, що робить вашу заявку ще більшою, навіть якщо все, що вам потрібно - це AsyncImageView. Також, здається, проект GreenDroid не оновлюється з 2011 року. - borisstr
Ви навіть можете спробувати цю бібліотеку: Android-http-image-manager що, на мій погляд, найкраще для асинхронного завантаження зображень. - Ritesh Kumar Dubey
Просто скористайся Picasso, він все зробить сам. 'Picasso.with (yourContext) .load (img src / path / drawable here) .into (imageView це ваша мета);' Це воно! - Anuj Sharma
спробуйте скористатись:github.com/nostra13/Android-Universal-Image-Loader , ця бібліотека дуже швидко і ефективно для ледачого завантаження та кешування зображень - krunal patel


Відповіді:


Ось що я створив, щоб тримати зображення, які моє додаток зараз показує. Зверніть увагу, що об'єкт "Журнал", який використовується тут, є моєю спеціальною обгорткою навколо останнього класу Журналу в 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();
    }
}

1017
2018-02-18 03:56



Я думаю, ви повинні використовувати SoftReferences, щоб ваша програма ніколи не викличе OutOfMemoryException. Оскільки GC може очистити програмне посилання, коли розмір кучи збільшується ... ви можете керувати власним поколінням, як через кілька секунд ви можете помістити свої зображення в цей список, і перед завантаженням слід перевірити, чи якщо зображення існує, то не завантажувати його знову, а збирати це з цього списку, а також поставити його назад у ваш softref список і після деякого часу ви можете очистити ваш hardlist :) - AZ_
Проект Google Shelves є прекрасним прикладом, як це робиться code.google.com/p/shelves - AZ_
Хіба ви не пропустите повернення, коли drawableMap містить зображення ... без початку завантаження-thread? - Karussell
Цей код має кілька проблем. По-перше, ви повинні кеш Drawables, що призведе до витоку пам'яті: stackoverflow.com/questions/7648740/... . По-друге, сама кеша ніколи не очищається, і вона буде зростати назавжди, це ще одна витік пам'яті. - satur9nine
ні про кого не чули LRU Cache  developer.android.com/training/displaying-bitmaps/... - Muhammad Babar


я зробив просте демонстрація лінивого списку (розташований в GitHub) з зображеннями. Це може бути корисно для когось. Він завантажує зображення у фоновому потоці. Зображення кешуються на SD-карті та в пам'яті. Реалізація кешу дуже проста, і для демонстрації достатньо. Я декодую зображення з inSampleSize, щоб зменшити споживання пам'яті. Я також намагаюся правильно обробляти перероблені перегляди.

Alt text


983
2018-06-18 08:04



Дякую, я використовую трохи модифіковану версію вашого коду майже скрізь у моєму проекті: code.google.com/p/vimeoid/source/browse/apk/src/com/fedorvlasov/... - shaman.sir
Чи хтось подивився на код, запропонований Джилсом Дебунне як альтернатива. Дві великі відмінності, які я бачу, є 1) у кешування пам'яті проти SD-карти та 2) використання AsyncTasks замість пулу потоків. android-developers.blogspot.com/2010/07/... - Richard
Існує помилка, іноді вона звільняється: 10-13 09: 58: 46.738: ПОМИЛКА / AndroidRuntime (24250): невичерпний обробник: основний випуск потоку через невичерпний виняток 10-13 09: 58: 46.768: ERROR / AndroidRuntime (24250) : java.lang.ArrayIndexOutOfBoundsException: Індекс маси за межами діапазону: 0 10-13 09: 58: 46.768: ERROR / AndroidRuntime (24250): на java.util.Vector.elementAt (Vector.java: 331) 10-13 09: 58: 46.768: ERROR / AndroidRuntime (24250): на java.util.Vector.get (Vector.java:445) 10-13 09: 58: 46.768: ERROR / AndroidRuntime (24250): на com.my.app.image .ImageLoader $ PhotosQueue.Clean (ImageLoader.java: 91) - Mikooos
Я хотів би додати для таких новачків, як я, що якщо ви хочете додати цей код до свого власного проекту, ви повинні додати зовнішні дозволи кешу в маніфесті. Дякую - Adam
@BruceHill У новому вихідному коді не існує жодних PhotosQueue. Ви, здається, використовуєте дуже стару версію. - Fedor


Я рекомендую інструмент open source Універсальний завантажувач зображень. Спочатку він базується на проекті Федора Власова LazyList і з того часу значно покращився.

  • Завантаження багатозадачного зображення
  • Можливість широкої настройки конфігурації ImageLoader (виконавців потоку, downlaoder, декодера, пам'яті та кешу диска, параметри зображення зображення та інші).
  • Можливість кешування зображень у пам'яті та / або на системному файлі пристрою (або SD-карті)
  • Можливість "прослуховувати" процес завантаження
  • Можливість налаштовувати кожний дзвінок із зображенням з окремими опціями
  • Підтримка віджетів
  • Підтримка Android 2.0+


530
2017-12-19 13:53



якщо ви шукаєте відмінний код завантаження зображень, який його творець трактує і підтримує як дорогоцінну дитину (не тільки одноразове рішення), ви просто знайшли його; великі підніжки Nostra - AndroidGecko
Дійсно велика робота, але трохи трохи перевищує мій список переглядів. Якщо ви могли б сказати, що найкраща версія "ImageLoader" побудувати ... або, можливо, це пов'язано з "DisplayImageOptions". - dinigo
Мені дуже подобається цей gridview, але я не можу використовувати його для мого підручника. Я новачок в Android. Я подаю заявку на gridview, але моя заявка була targetSdkVersion 15, тому мені потрібно використовувати Asynctask для процесу в фоновому потоці. Я використав цей gridview, але це не працює. як я можу використовувати його в targetSdkVersion 15? - kongkea
Ви можете створити окреме питання з тегом [універсальний образ-завантажувач] - NOSTRA
Прочитавши офіційні документи Android і намагаючись зробити це самостійно, я впевнений, що ця бібліотека закінчується усіма завантаженням зображень. Не можу вдарити. - Core


Багатопотоковий для виконання, підручник Джилса Дебунне.

Це з блогу розробників Android. Запропонований код використовує:

  • AsyncTasks.
  • Тяжкий, обмежений розмір FIFO cache.
  • Легкий, легко garbage collectкеш-пам'ять
  • А. заповнювач  Drawable поки ви завантажуєте.

enter image description here


147
2017-08-12 11:07



Але він побудований на Android 2.2 (Froyo) ... - Adinia
Це добре працює і в 2.1. Просто не використовуйте AndroidHttpClient. - Thomas Ahle
@ thomas-ahle Дякую, я бачив, що AndroidHttpClient надає помилки в 2.1, оскільки він реалізований з 2.2, але насправді не намагався знайти щось інше, щоб замінити його. - Adinia
@ Адіна Ви маєте рацію, я забув. Проте в рецепті немає нічого, що не може бути зроблено звичайним HttpClient. - Thomas Ahle
Все одно OOM залишається ... - Andro Selva


Оновлення: зверніть увагу, що ця відповідь є досить неефективною зараз. Garbage Collector агресивно діє на SoftReference і WeakReference, тому цей код НЕ придатний для нових додатків.  (Замість цього спробуйте бібліотеки, як Універсальний завантажувач зображень запропоновано в інших відповідях.)

Дякую Джеймсу за код, і Бао-Лонг за пропозицію використовувати SoftReference. Я реалізував зміни SoftReference на коді Джеймса. На жаль, SoftReferences змусило мої зображення бути сміттям, зібраним занадто швидко. У моєму випадку це було добре без матеріалу SoftReference, тому що мій розмір списку обмежений, а мої зображення невеликі.

Там обговорювали рік тому про SoftReferences в групах Google: посилання на тему. В якості рішення для занадто ранньої колекції сміття вони пропонують можливість вручну встановити розмір кубиків VM за допомогою 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();
}

102
2018-05-05 13:16



ви можете створювати такі покоління, як тверда генерація та покоління. ви можете зафіксувати час чистий кеш-пам'ять очистити всі зображення, які не були доступні через 3 сек .. ви можете подивитися на Google полів проекту - AZ_
developer.android.com/reference/java/lang/ref/... Документ SoftReference містить примітку про кешування, див. Розділ "Уникайте м'яких посилань для кешування". Більшість програм повинні використовувати android.util.LruCache замість м'яких посилань. - vokilam
Я захоплююсь вашим кодом, але зараз у новому Android O / S є "агресивні" збирання сміття. Зберігання слабкого посилання не має сенсу для мене. - j2emanue
@ j2emanue Ви маєте рацію, так як я намагався вказати у верхній частині моєї відповіді, SoftReferences - сміття, зібране занадто швидко. Я намагатимусь відредагувати цю відповідь, щоб зробити це ще чіткішим. - TalkLittle


Пікассо 

Використовуйте Бібліотеку Пікассо Джейка Вартона. (Відмінна бібліотека ImageLoading - розробник ActionBarSherlock)

Потужна завантаження та кешування зображень бібліотеки для Android.

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

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

Пікассо автоматично обробляє багато поширених помилок завантаження зображень на Android:

Використання ImageView для переробки та скасування скачування в адаптері. Комплексні перетворення зображень з мінімальним використанням пам'яті. Автоматична пам'ять та кешування дисків.

Бібліотека Пікассо Джейка Вартона

Ковзання

Glide - це швидка та ефективна платформа для керування мультимедіа за допомогою відкритого коду для Android, яка обгортає розшифрування медіа, кешування пам'яті та дисків, а також об'єднання ресурсів у простий та простий у використанні інтерфейс.

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

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

Головний фокус Glide полягає у тому, щоб прокручувати будь-який список зображень максимально гладко та швидко, але Glide також є ефективним практично у будь-якому випадку, коли вам потрібно завантажувати, змінювати розмір та відтворити віддалене зображення.

Завантаження бібліотеки зображень із зображенням ковзання

Фреска на Facebook 

Fresco - це потужна система для відображення зображень у додатках Android.

Fresco піклується про завантаження та відображення зображення, тому вам не потрібно. Він буде завантажувати зображення з мережі, місцевого зберігання або локальних ресурсів і відображати заповнювач, доки зображення не з'явиться. Він має два рівні кеш-пам'яті; один в пам'яті та інший у внутрішній пам'яті.

Фреска Github

У Android 4.x і нижче Fresco поміщає зображення в спеціальний регіон пам'яті Android. Це дозволяє вашій програмі працювати швидше - і страждати страшним OutOfMemoryError набагато рідше.

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


84
2018-04-04 12:35



Пікассо є бібліотекою, розробленою площею - LordRaydenMK


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

  1. Я зрозумів, що робота з drawables швидше, ніж з растровими зображеннями, тому я використовую drawables

  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
2017-12-27 23:27



Працює чудово! До речі, у назві класу є помилка. - Mullins
У випадку, коли хтось інший зберігає час: 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
Дуже дякую, це хороша реалізація. Я також встановлюю інший заповнювач місця, коли завантажується drawer, щоб користувач міг отримати зворотний зв'язок. - Juan Hernandez
Також я думаю, що краще використовувати чергу LIFO в executorService (mThreadPool), а не за замовчуванням FIFO, тому перші завантажені останніми зображеннями, які, ймовірно, є видимими. Побачити stackoverflow.com/questions/4620061/how-to-create-lifo-executor - Juan Hernandez
@MichaelReed, якщо ви є користувачем Eclipse, рекомендую скористатися Ctrl-Shift-O (це буква O, а не цифра 0). Він автоматизує процес додавання імпорту та організовує їх для вас. Якщо ви працюєте на Mac, скористайтеся командою Command-Shift-O. - SilithCrowe