如何在Android的ListView中延迟加载图像

How to lazy load images in ListView in Android

提问人:lostInTransit 提问时间:2/12/2009 最后编辑:Vivek MishralostInTransit 更新时间:5/3/2022 访问量:577338

问:

我正在使用 a 来显示一些图像和与这些图像相关的标题。我正在从互联网上获取图像。有没有办法延迟加载图像,以便在文本显示时不会阻止 UI,并且在下载图像时显示图像?ListView

图像总数不是固定的。

Android 图像 列表查看 网址 universal-image-loader

评论

6赞 Ritesh Kumar Dubey 11/9/2012
你甚至可以尝试这个库:Android-http-image-manager,在我看来,它是异步加载图像的最佳选择。
13赞 Pascal Dimassimo 12/9/2011
您可以使用 GreenDroid 的 AsyncImageView。只需调用 .setUrl
8赞 borisstr 4/15/2012
我用过它。这是一个很棒的实现。坏消息是 AsyncImageView 是大型 GreenDroid 项目的一部分,即使您只需要 AsyncImageView,它也会使您的应用程序更大。此外,GreenDroid 项目似乎自 2011 年以来就没有更新过。
36赞 Anuj Sharma 1/23/2014
只要使用毕加索,它就会自己做所有事情。'Picasso.with(yourContext).load(img src/path/drawable here).into(imageView i.e your target);'就是这样!
10赞 krunal patel 8/16/2014
尝试使用 :github.com/nostra13/Android-Universal-Image-Loader ,这个库对于延迟加载和图像缓存非常快速和高效

答:

46赞 jasonhudgins 2/13/2009 #1

我的做法是启动一个线程,在后台下载图像,并为每个列表项提供回调。当图像完成下载时,它会调用回调,以更新列表项的视图。

但是,当您回收视图时,此方法效果不佳。

评论

0赞 James A Wilson 2/15/2009
对每个图像使用线程也是我使用的方法。如果将模型与视图分开,则可以将模型保存在 Activity 之外(例如在“application”类中)以保留它们。如果有许多映像,请注意资源不足。
0赞 lostInTransit 2/15/2009
你能详细说明一下吗?我是android开发的新手。谢谢你的提示
14赞 Fedor 7/1/2010
为每个映像启动一个新线程不是一个有效的解决方案。您最终可能会在内存中出现大量线程并冻结 UI。
0赞 jasonhudgins 7/5/2010
Fedor,同意,我通常使用队列和线程池,这是最好的方式。
1150赞 James A Wilson 2/18/2009 #2

下面是我创建的用于保存我的应用当前显示的图像的内容。请注意,这里使用的“Log”对象是我围绕 Android 中最终 Log 类的自定义包装器。

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(Looper.getMainLooper()) {
            @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();
    }
}

评论

109赞 AZ_ 1/18/2011
我认为您应该使用 SoftReferences,以便您的程序永远不会导致 OutOfMemoryException。由于 GC 可以在堆大小增加时清除软引用...您可以管理自己的生成,例如几秒钟后,您可以将图像放入该列表中,在加载之前,您应该检查图像是否存在,然后不要再次下载它,而是从该列表中收集它并将其放回您的 softref 列表,一段时间后您可以清除您的硬列表:)
41赞 AZ_ 1/18/2011
Google Shelves 项目就是一个很好的例子,看看他们是如何做到的 code.google.com/p/shelves
14赞 Karussell 3/30/2011
当drawableMap包含图像时,你不会错过一个返回吗?没有启动获取线程?
7赞 satur9nine 11/15/2011
此代码存在多个问题。首先,您应该缓存 Drawables,这将导致内存泄漏:stackoverflow.com/questions/7648740/... .其次,缓存本身永远不会被清除,所以它会永远增长,这是另一个内存泄漏。
12赞 Muhammad Babar 5/28/2013
没人听说过 developer.android.com/training/displaying-bitmaps/......LRU Cache
118赞 TalkLittle 5/5/2010 #3

更新:请注意,这个答案现在非常无效。垃圾回收器对 SoftReference 和 WeakReference 执行攻击性操作,因此此代码不适合新应用。(相反,请尝试其他答案中建议的 Universal Image Loader 等库。

感谢 James 提供的代码,感谢 Bao-Long 提供使用 SoftReference 的建议。我在 James 的代码上实现了 SoftReference 更改。不幸的是,SoftReferences 导致我的图像被垃圾回收得太快了。就我而言,没有 SoftReference 的东西也很好,因为我的列表大小有限,而且我的图像很小。

一年前有一个关于谷歌群组上的SoftReferences的讨论:链接到线程。作为过早垃圾回收的解决方案,他们建议使用 dalvik.system.VMRuntime.setMinimumHeapSize() 手动设置 VM 堆大小,这对我来说不是很有吸引力。

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

评论

4赞 AZ_ 1/18/2011
您可以创建像硬生成和软生成这样的生成。您可以修复时间清除缓存将清除 3 秒内未访问的所有图像。您可以查看 Google Shelves 项目
0赞 vokilam 2/26/2013
developer.android.com/reference/java/lang/ref/......SoftReference 文档有关于缓存的注释,请参阅“避免缓存的软引用”部分。大多数应用都应使用 android.util.LruCache,而不是软引用。
0赞 j2emanue 7/22/2013
我很欣赏你的代码,但现在在新的 Android 操作系统中,有“激进”的垃圾收集。持有弱参考对我来说没有任何意义。
0赞 TalkLittle 7/22/2013
@j2emanue 你是对的,正如我试图在我的答案顶部指出的那样,SoftReferences 的垃圾回收速度太快了。我将尝试编辑这个答案,使其更加清晰。
1051赞 Fedor 6/18/2010 #4

我用图像做了一个懒惰列表(位于 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

166赞 Thomas Ahle 8/12/2010 #5

Multithreading For Performance,Gilles Debunne 的教程。

本文摘自 Android 开发者博客。建议的代码使用:

  • AsyncTasks.
  • 一个坚硬的、有限的尺寸,.FIFO cache
  • 一个柔软的、易于编辑的缓存。garbage collect
  • 下载时的占位符Drawable

enter image description here

评论

11赞 Thomas Ahle 2/5/2011
它在 2.1 中也运行良好。只是不要使用 AndroidHttpClient。
3赞 Adinia 2/9/2011
@thomas-ahle 谢谢,我看到 AndroidHttpClient 在 2.1 中出现错误,因为它是从 2.2 实现的,但并没有真正尝试寻找其他东西来替换它。
5赞 Thomas Ahle 2/22/2011
@Adina 你是对的,我忘了。但是,配方中没有任何内容不能用普通的 HttpClient 来完成。
0赞 Muhammad Ahmed AbuTalib 5/26/2014
我在几个地方听说,谷歌不推荐软引用,因为与系统的早期版本相比,android内核非常渴望收集这些引用。
0赞 Priyanka Singh 6/29/2020
你能帮忙吗?stackoverflow.com/questions/62624070/......
56赞 Ben Ruijl 8/27/2011 #6

我写了一个教程,解释了如何在列表视图中延迟加载图像。我将详细介绍回收和并发性问题。我还使用固定线程池来防止产生大量线程。

在Listview教程中延迟加载图像

88赞 7 revs, 5 users 93%Asaf Pinhassi #7

高性能装载机 - 在检查了此处建议的方法后, 我使用了 Ben 的解决方案,并进行了一些更改 -

  1. 我意识到使用可绘制对象比使用位图更快,所以我改用可绘制对象

  2. 使用 SoftReference 很棒,但它会使缓存的图像被删除得太频繁,因此我添加了一个包含图像引用的链表,以防止图像被删除,直到它达到预定义的大小

  3. 为了打开 InputStream,我使用了 java.net.URLConnection,它允许我使用 Web 缓存(您需要先设置响应缓存,但那是另一回事)

我的代码:

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

评论

0赞 Mullins 12/7/2011
效果很好!顺便说一句,类名中有一个错别字。
6赞 Michael Reed 1/8/2012
如果它节省了其他人的时间: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;
0赞 Juan Hernandez 2/7/2012
非常感谢,这是一个很好的实现。我还为加载可绘制对象的时间放置了一个不同的占位符,以便用户可以获得一些反馈。
0赞 Juan Hernandez 2/8/2012
此外,我认为最好在 executorService (mThreadPool) 中使用 LIFO 队列而不是默认的 FIFO,这样最后请求的图像(可能是可见的图像)才会首先加载。查看 stackoverflow.com/questions/4620061/how-to-create-lifo-executor
10赞 SilithCrowe 3/20/2012
@MichaelReed,如果您是 Eclipse 用户,我建议使用 Ctrl-Shift-O(这是字母 O,而不是数字 0)。它自动执行添加导入的过程并为您组织它们。如果您使用的是 Mac,请改用 Command-Shift-O。
36赞 Arslan Anwar 12/14/2011 #8

我只想再添加一个很好的例子,XML 适配器。因为它被谷歌使用,我也使用相同的逻辑来避免 OutOfMemory 错误。

基本上,这个 ImageDownloader 就是您的答案(因为它涵盖了您的大部分要求)。有些你也可以在其中实现。

评论

3赞 Sam 2/4/2012
ImageDownloader 类未得到编译:请参阅下面的解决方案 code.google.com/p/parleys-android-nextgen/issues/detail?id=1
564赞 nostra13 12/19/2011 #9

我推荐开源工具 Universal Image Loader。它最初基于 Fedor Vlasov 的项目 LazyList,从那时起得到了极大的改进。

  • 多线程图像加载
  • 可以广泛调整 ImageLoader 的配置(线程执行器、下载器、解码器、内存和光盘缓存、显示图像选项等)
  • 可以在内存和/或设备的文件系统(或 SD 卡)上缓存图像
  • 可以“监听”加载过程
  • 可以使用单独的选项自定义每个显示图像调用
  • Widget 支持
  • Android 2.0+ 支持

22赞 PatrickNLT 10/16/2012 #10

看看 Shutterbug,Applidium 的轻量级 SDWebImage(iOS 上的一个不错的库)移植到 Android。 它支持异步缓存,存储失败的 URL,很好地处理并发性,并且包含有用的子类。

也欢迎拉取请求(和错误报告)!

27赞 Ritesh Kumar Dubey 12/12/2012 #11

我认为这个问题在 Android 开发人员中非常流行,并且有很多这样的库声称可以解决这个问题,但其中似乎只有少数几个是正确的。AQuery 就是这样一个库,但它在各个方面都比大多数库都好,值得一试。

82赞 Etienne Lawlor 12/28/2012 #12

我已经遵循了这个 Android 培训,我认为它在下载图像方面做得非常出色,而不会阻止主 UI。它还处理缓存和处理滚动浏览许多图像: 高效加载大型位图

评论

0赞 mkuech 1/31/2013
对不起,我只指出了 Google IO 应用程序的一个类(我来不及编辑了)。您应该真正研究它们的所有图像加载和缓存实用程序类,这些类可以在与缓存类相同的包中找到。
0赞 Gautam 4/26/2013
有人会建议从 iosched 应用程序的 util 文件夹中获取 DiskLruCache、Image*.java 文件以帮助处理列表视图的图像加载/缓存吗?我的意思是,绝对值得遵循有关该主题的在线开发人员指南,但这些类(来自 iosched)在模式上走得更远。
17赞 Soham 1/12/2013 #13

Novoda 还有一个很棒的懒惰图像加载库,许多应用程序(如 Songkick、Podio、SecretDJ 和 ImageSearch)都使用它们的库。

他们的库托管在 Github 上,他们也有一个非常活跃的问题跟踪器。他们的项目似乎也非常活跃,在撰写此回复时有超过 300+ 次提交。

评论

2赞 Nicolas Jafelle 4/15/2013
实际上,Novoda 是一个很棒的图书馆,但是......有时,您不需要庞大的库,只需要简单的解决方案方法。这就是为什么 Github 中的 LazyList 如此出色的原因,如果您的应用程序仅在 listView 中显示图像并且不是应用程序的主要功能,那么我更喜欢使用更轻量级的另一个活动。否则,如果您知道自己将不得不经常使用并且是核心的一部分,请尝试 Novoda。
29赞 Rahul Rawat 2/12/2013 #14

好吧,从Internet加载图像的时间有很多解决方案。您也可以使用库 Android-Query。它将为您提供所有必需的活动。确定您要执行的操作并阅读库 wiki 页面。并解决图像加载限制。

这是我的代码:

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

它应该解决您的延迟加载问题。

评论

0赞 Selim Raza 4/24/2017
对我来说工作不错,但需要一个 Jar 文件包含在您的项目中。你可以从这里下载那个JAR文件:AQuery androidAQuery = new AQuery(this);友情链接:code.google.com/archive/p/android-query/downloads
15赞 Nicolas Jafelle 2/13/2013 #15

检查我的 LazyList 分支。基本上,我通过延迟 ImageView 的调用来改进 LazyList,并创建两个方法:

  1. 当您需要放置诸如“正在加载图像...”
  2. 当您需要显示下载的图像时。

我还通过在此对象中实现单例来改进 ImageLoader。

18赞 yanchenko 5/6/2013 #16

DroidParts 具有 ImageFetcher,无需任何配置即可开始使用。

  • 使用磁盘和内存中最近最少使用 (LRU) 缓存。
  • 高效解码图像。
  • 支持在后台线程中修改位图。
  • 具有简单的交叉淡入淡出。
  • 有图片加载进度回调。

克隆 DroidPartsGram 示例:

Enter image description here

评论

0赞 masha 1/13/2014
嗨,我已经看过代码示例,但是我在使用 ImageFetcher 和 ArrayAdapter 时遇到问题,您介意看看我的问题吗?stackoverflow.com/questions/21089147/......谢谢 =]
33赞 droidment 6/13/2013 #17

我一直在使用来自新的 Android Volley Library 的 NetworkImageView,它似乎运行良好。显然,这与Google Play和其他新的Google应用程序中使用的视图相同。绝对值得一试。com.android.volley.toolbox.NetworkImageView

评论

2赞 Alexander Sidikov Pfeif 9/8/2014
我认为这是最好的解决方案 - 其他答案非常古老 - 凌空抽射非常快,并且与 jake warthons disklrucache 相结合,这是一个性能解决方案 - 我尝试了很多其他解决方案,但没有一个像凌空一样稳定和快速
5赞 Nikhil Gupta 7/18/2013 #18
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;
 }
}
5赞 j2emanue 7/22/2013 #19

我遇到了这个问题并实施了 lruCache。我相信您需要 API 12 及更高版本或使用兼容性 v4 库。lurCache 是快速内存,但它也有预算,所以如果你担心这一点,你可以使用 diskcache......缓存位图中对此进行了介绍。

我现在将提供我的实现,它是我从任何地方调用的单例,如下所示:

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

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

以下是在检索 Web 图像时缓存然后在适配器的 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);
        }
    }
}
8赞 Pratik Dasa 7/29/2013 #20

我可以推荐一种不同的方式,就像一个魅力: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);

它非常快速和准确,使用它,您可以找到更多功能,例如加载时的动画、获取位图(如果需要)等。

4赞 Phil 8/17/2013 #21

我使用droidQuery。有两种机制可以从 URL 加载图像。第一个(简写)很简单:

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

这可以很容易地添加到 的方法中。ArrayAdaptergetView(...)


longhand 方法将提供更多的控制权,并且具有此处未讨论的选项(例如缓存和回调),但可以在此处找到将输出大小指定为 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);
        }
    })
);
5赞 Chirag Ghori 9/3/2013 #22

您可以尝试使用 Aquery Android 库来延迟加载图像和列表视图...下面的代码可能对您有所帮助.....从这里下载库

AQuery aq = new AQuery(mContext);
aq.id(R.id.image1).image("http://data.whicdn.com/images/63995806/original.jpg");
33赞 howettl 10/16/2013 #23

这是Android上的一个常见问题,许多人已经以多种方式解决了这个问题。在我看来,我见过的最好的解决方案是相对较新的毕加索图书馆。以下是其中的亮点:

  • 开源,但以 ActionBarSherlock 的名声为首。Jake Wharton
  • 使用一行代码从网络或应用资源异步加载图像
  • 自动检测ListView
  • 自动磁盘和内存缓存
  • 可以进行自定义转换
  • 许多可配置的选项
  • 超级简单的API
  • 经常更新
8赞 user2779311 12/16/2013 #24

试试 Aquery。它具有异步加载和缓存图像的简单方法。

7赞 DiegoAlt 1/27/2014 #25

URLImageViewHelper 是一个了不起的库,可以帮助您做到这一点。

108赞 Ashwin S Ashok 4/4/2014 #26

毕加索

使用杰克·沃顿(Jake Wharton)的毕加索图书馆。 (来自 ActionBarSherlock 开发者的完美图像加载库)

适用于 Android 的强大图像下载和缓存库。

图像为 Android 应用程序增添了急需的背景和视觉风格。Picasso 允许在您的应用程序中轻松加载图像 - 通常只需一行代码!

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

在 Android 上加载图像的许多常见陷阱都由 Picasso 自动处理:

在适配器中处理 ImageView 回收和下载取消。 以最少的内存使用进行复杂的图像转换。 自动内存和磁盘缓存。

毕加索·杰克·沃顿的图书馆

滑行

Glide 是一个快速高效的 Android 开源媒体管理框架,它将媒体解码、内存和磁盘缓存以及资源池封装到一个简单易用的界面中。

Glide 支持获取、解码和显示视频静止图像、图像和动画 GIF。Glide 包括一个灵活的 API,允许开发人员插入几乎任何网络堆栈。默认情况下,Glide 使用基于 HttpUrlConnection 的自定义堆栈,但也包括 Google 的 Volley 项目或 Square 的 OkHttp 库的实用程序库插件。

Glide.with(this).load("your-url-here").into(imageView);

Glide 的主要重点是使滚动任何类型的图像列表尽可能流畅和快速,但 Glide 对于几乎任何需要获取、调整大小和显示远程图像的情况也很有效。

Glide 图像加载库

Fresco 的 Facebook

Fresco 是一个功能强大的系统,用于在 Android 应用程序中显示图像。

Fresco 负责图像加载和显示,因此您不必这样做。它将从网络、本地存储或本地资源加载图像,并显示占位符,直到图像到达。它有两个级别的缓存;一个在内存中,另一个在内部存储中。

壁画 Github

在 Android 4.x 及更低版本中,Fresco 会将图像放在 Android 内存的特殊区域中。这样可以让您的应用程序运行得更快 - 并且遭受可怕的 OutOfMemoryError 的频率要低得多。

Fresco 文档

评论

1赞 LordRaydenMK 5/17/2018
毕加索是由Square开发的库
73赞 chiragkyada 5/22/2014 #27

1. Picasso 允许在您的应用程序中轻松加载图像——通常只需一行代码!

使用 Gradle:

implementation 'com.squareup.picasso:picasso:(insert latest version)'

只需一行代码!

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.11.0'
   annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'
}

对于一个简单的视图:

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

3. Fresco 是一个强大的系统,用于在 Android 中显示图像 应用。Fresco 负责图像加载和显示,因此您没有 自。

Fresco 入门

评论

0赞 lalit vasan 5/10/2017
本教程可能会对 PICASOO :- androidtutorialshub.com/... 和 GLIDE :- androidtutorialshub.com/...
3赞 redGREENblue 6/8/2014 #28

一些答案已经提到使用各种图像库,如 Universal Image Loader 和 androidimageloader 等。这是一个老问题,但对于仍在寻找这样东西的人来说,有几个这样的库用于图像加载/缓存。

3赞 Samet 6/21/2014 #29

另一种方法是通过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 数组中加载图像。还要始终检查您是否没有泄漏内存,位图和可绘制对象通常占用大量内存。由你来优化你的代码。

评论

0赞 The Original Android 6/7/2015
当然,我喜欢在不同的线程中获取位图。但是我在getView()中使用此代码的唯一问题是会有许多线程为多个图像运行。getView 可能会尝试一次加载多个或多个图像。
17赞 Bijay Koirala 8/3/2014 #30

对于犹豫不决的人,对于使用哪个库进行延迟加载图像的人来说,这是一个快速提示:

有四种基本方法。

  1. DIY => 不是最好的解决方案,但对于一些图像,如果您想不麻烦地使用其他库

  2. Volley 的延迟加载库 => 来自 android 的家伙。它很好,一切都很好,但记录得很差,因此使用起来很有问题。

  3. 毕加索:一个简单的解决方案,你甚至可以指定你想要引入的确切图像尺寸。它使用起来非常简单,但对于必须处理大量图像的应用程序来说可能不是很“高性能”。

  4. UIL:延迟加载图像的最佳方式。您可以缓存图像(当然需要权限),初始化加载器一次,然后完成工作。迄今为止我见过的最成熟的异步图像加载库。

23赞 Girish Patel 2/5/2015 #31

您必须尝试此 Universal Loader 是最好的。 我在延迟加载上完成许多 RnD 后使用它。

通用映像加载器

特征

  • 多线程图像加载(异步或同步)
  • 广泛自定义 ImageLoader 的配置(线程执行器、下载器、解码器、内存和磁盘缓存、显示图像选项等)
  • 每个显示图像调用都有许多自定义选项(存根图像、缓存开关、解码选项、位图处理和显示等)
  • 内存和/或磁盘(设备的文件系统或 SD 卡)中的图像缓存
  • 监听加载过程(包括下载进度)

Android 2.0+ 支持

enter image description here

12赞 Akber 7/10/2015 #32

以上所有代码都有自己的价值,但根据我的个人经验,请尝试一下毕加索。

毕加索是一个专门用于此目的的库,实际上它将自动管理缓存和所有其他网络操作。您必须在项目中添加库,只需编写一行代码即可从远程 URL 加载图像。

请点击这里 : http://code.tutsplus.com/tutorials/android-sdk-working-with-picasso--cms-22149

4赞 Sanjeet A 12/3/2015 #33

我发现 Glide 是比 更好的选择。 我使用毕加索加载每个图像的大小,我总是得到.但是解决了我的所有问题。Picasso32200-500KBOOMGlideOOM

评论

0赞 anhtuannd 1/8/2018
当然,Picasso 会存储完整的图像大小用于缓存,而 Glide 仅存储优化的图像。
4赞 Jotiram Chavan 12/3/2015 #34

使用下面的类在 ListView 中下载和加载图像。下载后,它会缓存每个图像。还加载图像和延迟加载。

package com.fudiyoxpress.images;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.Collections;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
import android.widget.ImageView;

import com.fudiyoxpress.R;
import com.fudiyoxpress.config.Config;
import com.fudiyoxpress.twitter.ScaleBitmap;

public class ImageLoader {

    // Initialize MemoryCache
    MemoryCache memoryCache = new MemoryCache();

    FileCache fileCache;

    Context C;

    // Create Map (collection) to store image and image url in key value pair
    private Map<ImageView, String> imageViews = Collections
            .synchronizedMap(new WeakHashMap<ImageView, String>());
    ExecutorService executorService;

    // handler to display images in UI thread
    Handler handler = new Handler();

    public ImageLoader(Context context) {

        C = context;
        fileCache = new FileCache(context);

        // Creates a thread pool that reuses a fixed number of
        // threads operating off a shared unbounded queue.
        executorService = Executors.newFixedThreadPool(5);

    }

    // default image show in list (Before online image download)
    final int stub_id = R.drawable.restlogoplaceholder;

    public void DisplayImage(String url, ImageView imageView, Context context,
            boolean header_flag) {

        Bitmap largeIcon = BitmapFactory.decodeResource(context.getResources(),
                R.drawable.restlogoplaceholder);
        header_flag = false;
        // Store image and url in Map
        imageViews.put(imageView, url);

        // Check image is stored in MemoryCache Map or not (see
        // MemoryCache.java)
        Bitmap bitmap = memoryCache.get(url);

        if (bitmap != null) {
            // if image is stored in MemoryCache Map then
            // Show image in listview row
            Bitmap b = ScaleBitmap
                    .getScaledBitmap(context, bitmap, header_flag);
            imageView.setImageBitmap(b);

        } else {
            // queue Photo to download from url
            queuePhoto(url, imageView, header_flag);

            // Before downloading image show default image
            imageView.setImageBitmap(ScaleBitmap.getScaledBitmap(context,
                    largeIcon, header_flag));

        }
    }



    private void queuePhoto(String url, ImageView imageView, boolean header_flag) {
        // Store image and url in PhotoToLoad object
        PhotoToLoad p = new PhotoToLoad(url, imageView, header_flag);

        // pass PhotoToLoad object to PhotosLoader runnable class
        // and submit PhotosLoader runnable to executers to run runnable
        // Submits a PhotosLoader runnable task for execution

        executorService.submit(new PhotosLoader(p));
    }

    // Task for the queue
    private class PhotoToLoad {
        public String url;
        public ImageView imageView;
        public boolean b;

        public PhotoToLoad(String u, ImageView i, boolean header_flag) {
            url = u;
            imageView = i;
            b = header_flag;
        }
    }

    class PhotosLoader implements Runnable {
        PhotoToLoad photoToLoad;

        PhotosLoader(PhotoToLoad photoToLoad) {
            this.photoToLoad = photoToLoad;
        }

        @Override
        public void run() {
            try {
                // Check if image already downloaded
                if (imageViewReused(photoToLoad))
                    return;
                // download image from web url
                Bitmap bmp = getBitmap(photoToLoad.url);

                // set image data in Memory Cache
                memoryCache.put(photoToLoad.url, bmp);

                if (imageViewReused(photoToLoad))
                    return;

                // Get bitmap to display
                BitmapDisplayer bd = new BitmapDisplayer(bmp, photoToLoad);

                // Causes the Runnable bd (BitmapDisplayer) to be added to the
                // message queue.
                // The runnable will be run on the thread to which this handler
                // is attached.
                // BitmapDisplayer run method will call
                handler.post(bd);

            } catch (Throwable th) {
                // th.printStackTrace();
            }
        }
    }

    private Bitmap getBitmap(String url) {
        File f = fileCache.getFile(url);

        // from SD cache
        // CHECK : if trying to decode file which not exist in cache return null
        Bitmap b = decodeFile(f);
        if (b != null)
            return b;

        // Download image file from web
        try {

            // // download the image
            Bitmap bitmap = null;

            URL imageURL = null;
            try {

                imageURL = new URL(Config.WEB_URL + "/ServeBlob?id=" + url);

                HttpURLConnection connection = (HttpURLConnection) imageURL
                        .openConnection();
                connection.setDoInput(true);
                connection.connect();
                // if(!(new File(imageURL.toString())).exists())
                // {
                // imageURL=new URL("");
                // }
                InputStream inputStream = connection.getInputStream();

                // Constructs a new FileOutputStream that writes to
                // file
                // if file not exist then it will create file
                OutputStream os = new FileOutputStream(f);

                // See Utils class CopyStream method
                // It will each pixel from input stream and
                // write pixels to output stream (file)
                Utils.CopyStream(inputStream, os);

                os.close();

                BitmapFactory.Options options = new BitmapFactory.Options();
                options.inSampleSize = 8;

                bitmap = BitmapFactory.decodeStream(inputStream, null, options);

            } catch (IOException e) {

                // e.printStackTrace();
            }

            // Now file created and going to resize file with defined height
            // Decodes image and scales it to reduce memory consumption
            bitmap = decodeFile(f);

            return bitmap;

        } catch (Throwable ex) {
            ex.printStackTrace();
            if (ex instanceof OutOfMemoryError)
                memoryCache.clear();
            return null;
        }
    }

    // Decodes image and scales it to reduce memory consumption
    private Bitmap decodeFile(File f) {

        try {

            // Decode image size
            BitmapFactory.Options o = new BitmapFactory.Options();
            o.inJustDecodeBounds = true;
            FileInputStream stream1 = new FileInputStream(f);
            BitmapFactory.decodeStream(stream1, null, o);
            stream1.close();

            // Find the correct scale value. It should be the power of 2.

            // Set width/height of recreated image
            final int REQUIRED_SIZE = 85;

            int width_tmp = o.outWidth, height_tmp = o.outHeight;
            int scale = 1;
            while (true) {
                if (width_tmp / 2 < REQUIRED_SIZE
                        || height_tmp / 2 < REQUIRED_SIZE)
                    break;
                width_tmp /= 2;
                height_tmp /= 2;
                scale *= 2;
            }

            // decode with current scale values
            BitmapFactory.Options o2 = new BitmapFactory.Options();
            o2.inSampleSize = scale;
            FileInputStream stream2 = new FileInputStream(f);
            Bitmap bitmap = BitmapFactory.decodeStream(stream2, null, o2);
            stream2.close();
            return bitmap;

        } catch (FileNotFoundException e) {
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    boolean imageViewReused(PhotoToLoad photoToLoad) {

        String tag = imageViews.get(photoToLoad.imageView);
        // Check url is already exist in imageViews MAP
        if (tag == null || !tag.equals(photoToLoad.url))
            return true;
        return false;
    }

    // Used to display bitmap in the UI thread
    class BitmapDisplayer implements Runnable {
        Bitmap bitmap;
        PhotoToLoad photoToLoad;

        public BitmapDisplayer(Bitmap b, PhotoToLoad p) {
            bitmap = b;
            photoToLoad = p;
        }

        public void run() {
            if (imageViewReused(photoToLoad))
                return;

            // Show bitmap on UI
            if (bitmap != null) {
                photoToLoad.imageView.setImageBitmap(ScaleBitmap
                        .getScaledBitmap(C, bitmap, photoToLoad.b));
            } else {

            }
            // photoToLoad.imageView.setImageResource(stub_id);

        }
    }

    public void clearCache() {
        // Clear cache directory downloaded images and stored data in maps
        memoryCache.clear();
        fileCache.clear();
    }

}




package com.fudiyoxpress.images;

import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import android.graphics.Bitmap;
import android.util.Log;

public class MemoryCache {

    private static final String TAG = "MemoryCache";

    //Last argument true for LRU ordering
    private Map<String, Bitmap> cache = Collections.synchronizedMap(
            new LinkedHashMap<String, Bitmap>(10,1.5f,true));

   //current allocated size
    private long size=0; 

    //max memory cache folder used to download images in bytes
    private long limit = 1000000; 

    public MemoryCache(){

        //use 25% of available heap size
        setLimit(Runtime.getRuntime().maxMemory()/4);
    }

    public void setLimit(long new_limit){

        limit=new_limit;
        Log.i(TAG, "MemoryCache will use up to "+limit/1024./1024.+"MB");
    }

    public Bitmap get(String id){
        try{
            if(!cache.containsKey(id))
                return null;
            //NullPointerException sometimes happen here http://code.google.com/p/osmdroid/issues/detail?id=78 
            return cache.get(id);
        }catch(NullPointerException ex){
            ex.printStackTrace();
            return null;
        }
    }

    public void put(String id, Bitmap bitmap){
        try{
            if(cache.containsKey(id))
                size-=getSizeInBytes(cache.get(id));
            cache.put(id, bitmap);
            size+=getSizeInBytes(bitmap);
            checkSize();
        }catch(Throwable th){
            th.printStackTrace();
        }
    }

    private void checkSize() {
        Log.i(TAG, "cache size="+size+" length="+cache.size());
        if(size>limit){
            Iterator<Entry<String, Bitmap>> iter=cache.entrySet().iterator();//least recently accessed item will be the first one iterated  
            while(iter.hasNext()){
                Entry<String, Bitmap> entry=iter.next();
                size-=getSizeInBytes(entry.getValue());
                iter.remove();
                if(size<=limit)
                    break;
            }
            Log.i(TAG, "Clean cache. New size "+cache.size());
        }
    }

    public void clear() {
        try{
            //NullPointerException sometimes happen here http://code.google.com/p/osmdroid/issues/detail?id=78 
            cache.clear();
            size=0;
        }catch(NullPointerException ex){
            ex.printStackTrace();
        }
    }

    long getSizeInBytes(Bitmap bitmap) {
        if(bitmap==null)
            return 0;
        return bitmap.getRowBytes() * bitmap.getHeight();
    }
}




package com.fudiyoxpress.images;

import java.io.InputStream;
import java.io.OutputStream;

public class Utils {
    public static void CopyStream(InputStream is, OutputStream os)
    {
        final int buffer_size=1024;
        try
        {

            byte[] bytes=new byte[buffer_size];
            for(;;)
            {
              //Read byte from input stream

              int count=is.read(bytes, 0, buffer_size);
              if(count==-1)
                  break;

              //Write byte from output stream
              os.write(bytes, 0, count);
            }
        }
        catch(Exception ex){}
    }
}
3赞 BalaramNayak 1/19/2016 #35

您可以使用一些第三方库,例如 OR 进行有效的延迟加载。您还可以通过实现以下命令来创建自己的PiccasoVolley

  1. 实现用于从 url 下载图像的代码

  2. 实现存储和检索图像的缓存机制(使用android进行缓存)LruCache

1赞 Yessy 6/14/2016 #36

除了异步加载数据缓存外,您可能需要 UI 缓存,例如 setViewCacheSize

除了加载可见项目数据外,您可能需要加载近似可见项目数据

AndroidX 分页库是另一种选择,例如,您可以从 SQLite 数据库加载、缓存和显示 10,000,000 个项目到 RecyclerView。请参阅 PagedList

例: 假设列表视图可见项是 [6,7,8,9,10],您可能需要加载 [6,7,8,9,10] 并预加载项 [1, 2, 3, 4, 5] 和 [11, 12, 13, 14, 15],因为用户可能会滚动到前页或后页

评论

3赞 buczek 6/15/2016
除了您的描述外,请附上一些代码以进一步改进您的答案。
11赞 Saket Kumar 3/16/2017 #37

使用滑行库。它对我有用,也适用于您的代码。它也适用于图像和 GIF。

ImageView imageView = (ImageView) findViewById(R.id.test_image); 
    GlideDrawableImageViewTarget imagePreview = new GlideDrawableImageViewTarget(imageView);
    Glide
            .with(this)
            .load(url)
            .listener(new RequestListener<String, GlideDrawable>() {
                @Override
                public boolean onException(Exception e, String model, Target<GlideDrawable> target, boolean isFirstResource) {                       
                    return false;
                }

                @Override
                public boolean onResourceReady(GlideDrawable resource, String model, Target<GlideDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) {
                    return false;
                }
            })
            .into(imagePreview);
}
16赞 Zankrut Parmar 2/26/2018 #38

如果您想像 Facebook 一样显示 Shimmer 布局,有一个官方的 Facebook 库。FaceBook微光安卓

它负责一切,您只需要将所需的设计代码以嵌套方式放在微光框架中即可。 下面是一个示例代码。

<com.facebook.shimmer.ShimmerFrameLayout
     android:id=“@+id/shimmer_view_container”
     android:layout_width=“wrap_content”
     android:layout_height="wrap_content"
     shimmer:duration="1000">

 <here will be your content to display />

</com.facebook.shimmer.ShimmerFrameLayout>

这是它的 java 代码。

ShimmerFrameLayout shimmerContainer = (ShimmerFrameLayout) findViewById(R.id.shimmer_view_container);
shimmerContainer.startShimmerAnimation();

在 gradle 文件中添加此依赖项。

implementation 'com.facebook.shimmer:shimmer:0.1.0@aar'

这是它的样子。Shimmer Android screenshot

2赞 Ali Ahsan 11/3/2020 #39

更新:如果您正在寻找 2020 年由 Kotlin 协程支持的解决方案,请尝试 Coil。

Coil 是协程图像加载器的首字母缩写。

特征

  1. 快:Coil 执行许多优化,包括内存和磁盘缓存、内存中的图像下采样、重用 位图、自动暂停/取消请求等。
  2. 轻:Coil 会向 APK 添加 ~2000 个方法(适用于已经使用 OkHttp 和协程的应用),这与 Picasso 相当,但明显少于 GlideFresco
  3. 易于使用:Coil 的 API 利用 Kotlin 的语言功能来简化和减少样板。
  4. 摩登:Coil 首先是 Kotlin 的,并使用现代库,包括协程、OkHttp、Okio 和 AndroidX 生命周期。

Gradle 设置:

线圈可在 上找到。mavenCentral()

implementation("io.coil-kt:coil:1.0.0")

快速上手

若要将图像加载到 ImageView 中,请使用加载扩展函数:

// URL
imageView.load("https://www.example.com/image.jpg")

// Resource
imageView.load(R.drawable.image)

// File
imageView.load(File("/path/to/image.jpg"))

或在后台线程上

// Coil (suspends the current coroutine; non-blocking and thread safe)
val request = ImageRequest.Builder(context)
    .data(url)
    .size(width, height)
    .build()
val drawable = context.imageLoader.execute(request).drawable

您也可以从 Picasso/Glide 迁移

完整文档在这里

1赞 Rahul 5/11/2021 #40

滑行

Glide 是一个快速高效的 Android 开源媒体管理框架,它将媒体解码、内存和磁盘缓存以及资源池封装到一个简单易用的界面中。

Glide 支持获取、解码和显示视频静止图像、图像和动画 GIF。Glide 包括一个灵活的 API,允许开发人员插入几乎任何网络堆栈。默认情况下,Glide 使用基于 HttpUrlConnection 的自定义堆栈,但也包括 Google 的 Volley 项目或 Square 的 OkHttp 库的实用程序库插件。

Glide.with(this).load("your-url-here").into(imageView);

Glide 的主要重点是使滚动任何类型的图像列表尽可能流畅和快速,但 Glide 对于几乎任何需要获取、调整大小和显示远程图像的情况也很有效。

Glide 图书馆

毕加索

使用杰克·沃顿(Jake Wharton)的毕加索图书馆。 (来自 ActionBarSherlock 开发者的完美图像加载库)

适用于 Android 的强大图像下载和缓存库。

图像为 Android 应用程序增添了急需的背景和视觉风格。Picasso 允许在您的应用程序中轻松加载图像 - 通常只需一行代码!

Picasso.with(context).load("your-url-here").into(imageView);

在 Android 上加载图像的许多常见陷阱都由 Picasso 自动处理:

在适配器中处理 ImageView 回收和下载取消。 以最少的内存使用进行复杂的图像转换。 自动内存和磁盘缓存。

毕加索图书馆

0赞 Mahozad 8/26/2021 #41

这就是使用 Jetpack Compose 的方法。

implementation("io.coil-kt:coil-compose:1.3.1") // Add the Coil-Compose library
Image(
    painter = rememberImagePainter("https://www.example.com/image.jpg"),
    contentDescription = "My image description",
    modifier = Modifier.size(128.dp)
)

感谢 nglauber 和 Gabriele Mariotti 的这个这个答案。