Lazy load of images in ListView
我使用
图像总数不固定。
以下是我创建的用于保存应用程序当前显示的图像的内容。请注意,这里使用的"log"对象是我在Android中对最终日志类的自定义包装。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 | 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(); } } |
我用图片制作了一个简单的惰性列表演示(位于Github)。
Basic Usage
1
2 ImageLoader imageLoader=new ImageLoader(context); ...
imageLoader.DisplayImage(url, imageView);Don't forget to add the
following permissions to your AndroidManifest.xml:
1
2 <uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> Pleasecreate only one instance of ImageLoader and reuse it all around your
application. This way image caching will be much more efficient.
它可能对某人有帮助。它在后台线程中下载图像。图像被缓存在SD卡和内存中。缓存实现非常简单,只需演示即可。我解码图像的采样,以减少内存消耗。我还尝试正确处理回收视图。
我推荐开源仪器通用图像加载器。它最初基于FedorVlasov的Lazylist项目,从那时起已经得到了很大的改进。
- 多线程图像加载
- 可以对ImageLoader的配置进行广泛的调整(线程执行器、下载器、解码器、内存和磁盘缓存、显示图像选项等)
- 在内存和/或设备文件系统(或SD卡)中缓存图像的可能性
- "倾听"加载过程的可能性
- 可以使用单独的选项自定义每个显示图像调用
- 小部件支持
- Android 2.0+支持
性能的多线程处理,由GillesDebunne指导。
这是来自Android开发者博客。建议的代码使用:
AsyncTasks 。- 硬的,有限的尺寸,
FIFO cache 。 - 一个软的、容易的
garbage collect ed缓存。 - 下载时使用占位符
Drawable 。
更新:请注意,这个答案现在非常无效。垃圾收集器在SoftReference和Weakreference上起着积极的作用,因此此代码不适用于新的应用程序。(相反,尝试使用其他答案中建议的通用图像加载器之类的库。)
多亏了詹姆斯的代码,包龙建议使用软参考。我在James的代码上实现了软引用更改。不幸的是,软引用导致我的图像被垃圾收集得太快。在我的例子中,没有软参考资料是可以的,因为我的列表大小是有限的,我的图像是小的。
一年前有一个关于谷歌群组的软引用的讨论:链接到线程。作为过早垃圾收集的解决方案,他们建议使用dalvik.system.vmruntime.setMinimumHeapsize()手动设置VM堆大小,这对我来说不是很有吸引力。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 | 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(); } |
毕加索
使用杰克·沃顿的毕加索图书馆。(来自ActionBarsherlock开发人员的完美图像加载库)
一个强大的Android图像下载和缓存库。
图像为Android应用程序添加了急需的上下文和视觉功能。毕加索允许在应用程序中以一行代码轻松加载图像!
1 | 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库。
1 | Glide.with(this).load("http://goo.gl/h8qOq7").into(imageView); |
glide的主要焦点是使滚动任何类型的图像列表尽可能平滑和快速,但是glide对于几乎任何需要获取、调整和显示远程图像的情况都是有效的。
滑动图像加载库
Facebook的壁画
Fresco是一个在Android应用程序中显示图像的强大系统。
壁画负责图像的加载和显示,所以你不必这么做。它将从网络、本地存储或本地资源加载图像,并在图像到达之前显示一个占位符。它有两个级别的缓存:一个在内存中,另一个在内部存储器中。
壁画吉特布
在android 4.x及更低版本中,fresco将图像放在android内存的一个特殊区域。这使您的应用程序运行得更快,并且更少地遭受可怕的内存不足的错误。
壁画文献
高性能装载机-在检查了这里建议的方法之后,我用了本的解决方案,做了一些改动-
我意识到使用Drawables比使用位图更快,所以我改用Drawables。
使用SoftReference很好,但是它会使缓存的图像被删除的太频繁,所以我添加了一个包含图像引用的链接列表,防止删除图像,直到它达到预定的大小。
要打开inputstream,我使用了java.net.urlconnection,它允许我使用web缓存(您需要先设置响应缓存,但这是另一个情况)
我的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 | 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; } } |
我接受过这个Android培训,我认为它在下载图片时非常出色,不会阻塞主用户界面。它还处理缓存和处理滚动浏览许多图像:高效加载大型位图
1。毕加索允许在应用程序中以一行代码轻松加载图像!
使用Gradle:
1 | implementation 'com.squareup.picasso:picasso:2.71828' |
只有一行代码!
1 | Picasso.get().load("http://i.imgur.com/DvpvklR.png").into(imageView); |
2。glide一个面向Android的图像加载和缓存库,专注于平滑滚动
使用Gradle:
1 2 3 4 5 6 7 8 9 | repositories { mavenCentral() google() } dependencies { implementation 'com.github.bumptech.glide:glide:4.7.1' annotationProcessor 'com.github.bumptech.glide:compiler:4.7.1' } |
//对于简单视图:
1 | Glide.with(this).load("http://i.imgur.com/DvpvklR.png").into(imageView); |
三。Fresco是一个在Android中显示图像的强大系统。应用程序。Fresco负责图像加载和显示,因此您没有去。
壁画入门
我编写了一个教程,解释如何在ListView中延迟加载图像。我将详细介绍回收和并发性问题。我还使用固定线程池来防止产生大量线程。
在ListView教程中延迟加载图像
我的方法是启动一个线程,在后台下载图片,并为每个列表项传递一个回调。当图像下载完成后,它会调用回调来更新列表项的视图。
然而,当您回收视图时,这种方法并不能很好地工作。
我只想再添加一个好例子,XML适配器。因为它被谷歌使用,我也在使用同样的逻辑来避免内存不足的错误。
基本上,这个图片下载器是你的答案(因为它涵盖了你的大部分需求)。其中一些您也可以实现。
我一直在使用新安卓Volley库
Google I/O 2013 Volley图像缓存教程
开发者谷歌活动
这是安卓系统上的一个常见问题,许多人通过多种方式解决了这个问题。在我看来,我所看到的最好的解决方案是一个相对较新的图书馆,叫做毕加索。以下是重点:
- 开源,但以
Jake Wharton 为领袖的ActionBarsherlock声名远扬。 - 使用一行代码从网络或应用程序资源异步加载图像
- 自动
ListView 检测 - 自动磁盘和内存缓存
- 可以进行自定义转换
- 许多可配置选项
- 超简单API
- 经常更新
嗯,从互联网上传图片的时间有很多解决方案。您也可以使用库android查询。它将为您提供所有必需的活动。确保你想做什么,并阅读图书馆的wiki页面。解决了图像加载限制。
这是我的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | @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; } |
它应该可以解决您的延迟加载问题。
我认为这个问题在Android开发人员中非常流行,有很多这样的库声称可以解决这个问题,但似乎只有少数库是正确的。aquery就是这样一个图书馆,但它在各个方面都比它们中的大多数要好,值得一试。
你一定要试试这个万能装载机是最好的。我在完成了很多次延迟加载后使用这个。
通用图像加载器
特征
- 多线程图像加载(异步或同步)
- 图像加载器配置的广泛定制(线程执行器、下载器、解码器、内存和磁盘缓存、显示图像选项等)
- 每个显示图像调用的许多自定义选项(存根图像、缓存开关、解码选项、位图处理和显示等)
- 内存和/或磁盘上的图像缓存(设备的文件系统或SD卡)
- 监听加载过程(包括下载进度)
Android 2.0+支持
看看Shutterbug,applidium的轻量级SDWebImage(iOS上的一个不错的库)和Android的端口。它支持异步缓存,存储失败的URL,很好地处理并发性,并包含有用的子类。
拉请求(和错误报告)也是受欢迎的!
对于不确定要使用哪个库进行延迟加载图像的人来说,这只是一个快速提示:
有四种基本方法。
DIY=>不是最好的解决方案,但是对于一些图片,如果你想不费吹灰之力使用其他库的话
Volley的懒惰加载库=>来自Android的家伙。它很好,而且所有的东西都没有很好的文档记录,因此使用起来很困难。
毕加索:这是一个简单的解决方案,你甚至可以指定你想要引入的确切图像大小。对于必须处理大量图像的应用程序来说,它的使用非常简单,但可能不会非常"性能"。
UIL:延迟加载图像的最佳方法。您可以缓存图像(当然您需要权限),初始化一次加载程序,然后完成您的工作。迄今为止我所见过的最成熟的异步映像加载库。
DroidParts的ImageFetcher需要零配置才能启动。
- 使用磁盘和内存中最近使用的(LRU)缓存。
- 有效地解码图像。
- 支持在后台线程中修改位图。
- 有简单的交叉淡入淡出。
- 有图像加载进度回调。
克隆DroidPartsGram例如:
Novoda也有一个非常懒惰的图片加载库,许多应用程序,如Songkick、Podio、Secretdj和ImageSearch都使用它们的库。
他们的库托管在Github上,他们也有一个相当活跃的问题跟踪程序。他们的项目似乎也相当活跃,在写这个回复时提交了超过300个。
检查我的Lazylist叉子。基本上,我通过延迟ImageView的调用来改进Lazylist,并创建两种方法:
我还通过在这个对象中实现一个单例来改进了ImageLoader。
以上所有的代码都有自己的价值,但根据我的个人经验,请尝试一下毕加索。
毕加索是一个专门用于此目的的库,实际上它将自动管理缓存和所有其他网络操作。您必须在项目中添加库,只需编写一行代码即可从远程URL加载图像。
请访问:http://code.tutsplus.com/tutorials/android-sdk-working-with-picasso--cms-22149
使用glide库。它适用于我,也适用于您的代码。它既适用于图像,也适用于gif。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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); } |
如果你想像facebook那样显示闪光的布局,那就有一个官方的facebook库。Facebook闪影安卓
它可以处理所有的事情,您只需要将所需的设计代码以嵌套的方式放入Shimmer框架中。这是一个示例代码。
1 2 3 4 5 6 7 8 9 | <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代码。
1 2 | ShimmerFrameLayout shimmerContainer = (ShimmerFrameLayout) findViewById(R.id.shimmer_view_container); shimmerContainer.startShimmerAnimation(); |
在渐变文件中添加此依赖项。
1 | implementation 'com.facebook.shimmer:shimmer:0.1.0@aar' |
这是它的样子.。
UrlimageViewHelper是一个非常棒的库,可以帮助您完成这项工作。
我可以推荐一种类似于魅力的不同方式:Android查询。
你可以从这里下载JAR文件
1 | AQuery androidAQuery = new AQuery(this); |
举个例子:
1 | androidAQuery.id(YOUR IMAGEVIEW).image(YOUR IMAGE TO LOAD, true, true, getDeviceWidth(), ANY DEFAULT IMAGE YOU WANT TO SHOW); |
它非常快速和准确,使用它,您可以在加载时找到更多的功能,如动画、获取位图(如果需要)等。
试一试。它有非常简单的方法来异步加载和缓存图像。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 | 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; } } |
我有这个问题并实现了lRucache。我相信您需要API 12及以上版本或使用兼容v4库。lurcache是快速内存,但它也有预算,所以如果您担心可以使用diskcache…所有这些都在缓存位图中描述。
现在,我将提供我的实现,它是我从类似这样的任何地方调用的单例实现:
1 2 3 | //Where the first is a string and the other is a imageview to load. DownloadImageTask.getInstance().loadBitmap(avatarURL, iv_avatar); |
以下是在检索Web映像时在适配器的getview中缓存然后调用上述代码的理想代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 | 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); } } } |
你可以尝试使用aquery android库来延迟加载图片和列表视图…下面的代码可以帮助您…..从此处下载库。
1 2 | AQuery aq = new AQuery(mContext); aq.id(R.id.image1).image("http://data.whicdn.com/images/63995806/original.jpg"); |
一些答案已经提到使用各种图像库,如通用图像加载器和AndroidImage加载器等。这是一个老问题,但对于仍在寻找类似问题的任何人来说,有几个这样的库用于图像加载/缓存。
另一种方法是通过getView()方法中的线程中的适配器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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(); |
当然,您应该始终缓存您的图像以避免额外的操作,您可以将图像放在哈希映射数组中,检查该图像是否存在于数组中,如果不存在,则继续执行该线程,或者从哈希映射数组加载图像。另外,一定要检查内存是否泄漏,位图和可绘制文件通常占用大量内存。由您来优化代码。
我使用DroidQuery。有两种从URL加载图像的机制。第一个(简写)是:
1 | $.with(myView).image(url); |
这可以很容易地添加到
longhand方法将提供更多的控制,这里甚至没有讨论过选项(如缓存和回调),但是可以在这里找到指定输出大小为200px x 200px的基本实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | $.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); } }) ); |
我发现滑翔比
使用下面的类在ListView中下载和加载图像。下载后它会缓存每个图像。同时加载图像和延迟加载。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 | 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){} } } |
您可以使用一些第三方库,如
实现从URL下载图像的代码
实现存储和检索图像的缓存机制(使用android的
您可以使用Greendroid的AsyncImageView。只需调用seturl我想这可能有助于做你不想做的事并参考以下链接:
Greendroid的AsyncImageView链接
除了异步加载数据缓存外,您可能需要UI缓存
除了加载可见项数据外,您可能需要加载近似可见项数据
例子:假设ListView可见项为[6,7,8,9,10],则可能需要加载[6,7,8,9,10]并预加载项[1,2,3,4,5]&;[11,12,13,14,15],因为用户可能会滚动到前页或后页。