我正在使用ListView
来显示与这些图片相关联的一些图片和字幕。我从互联网上获取图像。有没有办法延迟加载图像,以便文本显示时,UI不会被锁定,图像会在下载时显示?
图像总数不固定。
答案 0 :(得分:1057)
这是我创建的用于保存我的应用当前正在显示的图像的内容。请注意,这里使用的“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() {
@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();
}
}
答案 1 :(得分:1010)
我用图片制作了a simple demo of a lazy list(位于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解码图像以减少内存消耗。我也尝试正确处理回收的视图。
答案 2 :(得分:154)
Multithreading For Performance,Gilles Debunne的教程。
这是来自Android开发者博客。建议的代码使用:
AsyncTasks
。FIFO cache
。garbage collect
- 缓存。Drawable
。
答案 3 :(得分:105)
更新:请注意,此答案现在非常无效。垃圾收集器对SoftReference和WeakReference采取积极行动,因此此代码不适用于新的应用程序。(相反,请尝试在其他答案中提供Universal Image Loader之类的库。)
感谢James的代码,以及Bao-Long对使用SoftReference的建议。我在James的代码上实现了SoftReference的更改。不幸的是,SoftReferences导致我的图像垃圾收集过快。在我的情况下没有SoftReference的东西很好,因为我的列表大小有限,我的图像很小。
一年前有关于Google群组的SoftReferences的讨论:link to thread。作为过早垃圾收集的解决方案,他们建议使用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 :(得分:93)
毕加索
使用杰克沃顿的毕加索图书馆。 (一个完美的ImageLoading Library构成ActionBarSherlock的开发者)
Android的强大图像下载和缓存库。
图像为Android应用程序添加了急需的上下文和视觉风格。 Picasso允许在您的应用程序中轻松加载图像 - 通常只需一行代码!
Picasso.with(context).load("http://i.imgur.com/DvpvklR.png").into(imageView);
Picasso自动处理Android上图像加载的许多常见缺陷:
在适配器中处理ImageView回收和下载取消。 复杂的图像转换,内存使用最少。 自动内存和磁盘缓存。
Picasso Jake Wharton's Library
<强>滑翔强>
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负责图片加载和显示,因此您不必这样做。它将从网络,本地存储或本地资源加载图像,并显示占位符,直到图像到达。它有两级缓存;一个在内存中,另一个在内部存储中。
在Android 4.x及更低版本中,Fresco将图像放在Android内存的特殊区域。这样可以让您的应用程序运行得更快 - 并且更少经历可怕的OutOfMemoryError。
答案 5 :(得分:79)
高性能加载器 - 在检查了此处建议的方法后, 我使用Ben's solution进行了一些更改 -
我意识到使用drawables比使用位图更快,所以我使用drawables
使用SoftReference非常棒,但它会使缓存的图像被删除得太频繁,因此我添加了一个包含图像引用的链接列表,防止图像被删除,直到达到预定义的大小
要打开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;
}
}
答案 6 :(得分:77)
我已经关注了这个Android培训,我认为它在不阻止主UI的情况下下载图像方面表现非常出色。它还处理缓存和处理滚动许多图像:Loading Large Bitmaps Efficiently
答案 7 :(得分:63)
1。 Picasso允许您的应用程序中无障碍图像加载 - 通常只需一行代码!
使用Gradle:
implementation 'com.squareup.picasso:picasso:2.71828'
只需一行代码!
Picasso.get().load("http://i.imgur.com/DvpvklR.png").into(imageView);
2. Glide适用于Android的图片加载和缓存库专注于平滑滚动
使用Gradle:
repositories {
mavenCentral()
google()
}
dependencies {
implementation 'com.github.bumptech.glide:glide:4.7.1'
annotationProcessor 'com.github.bumptech.glide:compiler:4.7.1'
}
//对于简单的观点:
Glide.with(this).load("http://i.imgur.com/DvpvklR.png").into(imageView);
3. fresco是一个用于在Android中显示图片的强大系统 applications.Fresco负责图像加载和显示,所以你没有 到。
答案 8 :(得分:51)
我编写了一个教程,解释了如何在列表视图中对图像进行延迟加载。我详细介绍了回收和并发问题。我还使用固定的线程池来防止产生大量线程。
答案 9 :(得分:39)
我这样做的方法是启动一个线程来在后台下载图像,并为每个列表项提供一个回调。当图像下载完成后,它会调用回调来更新列表项的视图。
但是,当您回收视图时,此方法效果不佳。
答案 10 :(得分:31)
我只想添加一个更好的示例 XML Adapters 。因为它被谷歌使用,我也使用相同的逻辑来避免OutOfMemory错误。
基本上this ImageDownloader是您的答案(因为它涵盖了您的大部分要求)。有些你也可以在那里实现。
答案 11 :(得分:28)
我一直在使用新的Android Volley Library com.android.volley.toolbox.NetworkImageView
中的NetworkImageView,它看起来效果很好。显然,这与Google Play和其他新Google应用程序中使用的视图相同。绝对值得一试。
答案 12 :(得分:27)
这是Android上的一个常见问题,许多人已经通过多种方式解决了这个问题。在我看来,我见过的最好的解决方案是名为Picasso的相对较新的库。以下是重点:
Jake Wharton
ActionBarSherlock成名。ListView
检测答案 13 :(得分:25)
嗯,来自互联网的图像加载时间有很多解决方案。您也可以使用库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;
}
应该解决你的延迟加载问题。
答案 14 :(得分:24)
我认为这个问题在Android开发人员中非常受欢迎,并且有很多这样的图书馆声称可以解决这个问题,但其中只有少数几个似乎已经出现了问题。 AQuery是一个这样的图书馆,但它在各方面都比大多数图书馆都好,值得一试。
答案 15 :(得分:21)
你必须尝试这种通用装载机是最好的。 在懒惰加载完成许多RnD之后我正在使用它。
功能强>
Android 2.0+支持
答案 16 :(得分:20)
看看Shutterbug,Applidium的轻量级SDWebImage(iOS上的一个不错的库)移植到Android的端口。 它支持异步缓存,存储失败的URL,很好地处理并发,并包含有用的子类。
欢迎拉取请求(和错误报告)!
答案 17 :(得分:16)
对于一个犹豫不决的人来说,只是一个快速的提示,关于用于延迟加载图像的库:
有四种基本方式。
DIY =&gt;不是最好的解决方案,但对于一些图像,如果你想没有使用其他库的麻烦
Volley的Lazy Loading库=&gt;来自android的人。它很好,但是文档记录很少,因此使用起来很困难。
Picasso:一个简单的解决方案,你甚至可以指定你想要引入的确切图像大小。它使用起来非常简单,但对于必须处理的应用程序可能不是非常“高效”大量的图像。
UIL:延迟加载图片的最佳方式。您可以缓存图像(当然需要权限),初始化加载程序一次,然后完成工作。到目前为止我见过的最成熟的异步图像加载库。
答案 18 :(得分:16)
DroidParts有ImageFetcher,需要零配置才能开始使用。
克隆DroidPartsGram以获取示例:
答案 19 :(得分:15)
Novoda也有一个很棒的lazy image loading library,许多应用程序,如Songkick,Podio,SecretDJ和ImageSearch使用他们的库。
他们的图书馆在Github上托管here,他们也有一个非常活跃的issues tracker。他们的项目似乎也相当活跃,在撰写此回复时提交了超过300次。
答案 20 :(得分:13)
检查LazyList的分叉。基本上,我通过延迟ImageView的调用来改进LazyList并创建两个方法:
我还通过在此对象中实现singleton来改进ImageLoader。
答案 21 :(得分:11)
以上所有代码都有自己的价值,但根据我的个人经验,只需尝试毕加索。
Picasso 是专门用于此目的的库,实际上它将自动管理缓存和所有其他网络操作。您必须在项目中添加库并只编写一行代码从远程URL加载图像。
请访问此处:http://code.tutsplus.com/tutorials/android-sdk-working-with-picasso--cms-22149
答案 22 :(得分:9)
使用滑动库。它对我有用,也适用于你的代码。它既适用于图像,也适用于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);
}
答案 23 :(得分:9)
如果你想显示像Facebook这样的Shimmer布局,那么就有一个官方的facebook库。 FaceBook Shimmer Android
它可以处理所有事情,您只需要将您想要的设计代码以嵌套的方式放在闪烁的框架中。 这是一个示例代码。
<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'
答案 24 :(得分:7)
我可以推荐一种不同于魅力的方式:Android查询。
您可以从JAR
下载here个文件AQuery androidAQuery = new AQuery(this);
举个例子:
androidAQuery.id(YOUR IMAGEVIEW).image(YOUR IMAGE TO LOAD, true, true, getDeviceWidth(), ANY DEFAULT IMAGE YOU WANT TO SHOW);
它非常快速和准确,使用它你可以找到更多的功能,如加载时的动画,获取位图(如果需要)等。
答案 25 :(得分:7)
尝试Aquery。它具有非常简单的方法来异步加载和缓存图像。
答案 26 :(得分:7)
URLImageViewHelper是一个很棒的图书馆,可以帮助你做到这一点。
答案 27 :(得分:4)
您可以尝试使用 Aquery Android 库进行延迟加载图片和listview ...以下代码可以帮助您..... download library from here。
AQuery aq = new AQuery(mContext);
aq.id(R.id.image1).image("http://data.whicdn.com/images/63995806/original.jpg");
答案 28 :(得分:4)
我遇到了这个问题并实施了lruCache。我相信您需要API 12及更高版本或使用兼容性v4库。 lurCache是快速内存,但它也有预算,所以如果你担心它可以使用磁盘缓存...这些都在 Caching Bitmaps 中描述。
我现在将提供我的实现,这是singleton我从以下任何地方打电话:
//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);
}
}
}
答案 29 :(得分:4)
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;
}
}
答案 30 :(得分:3)
另一种方法是通过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数组。还要经常检查您是否没有泄漏内存,位图和可绘制内容通常会占用大量内存。由您来优化代码。
答案 31 :(得分:3)
一些答案已经提到使用各种图像库,如Universal Image Loader和androidimageloader等。这是一个古老的问题,但对于仍在寻找类似内容的人来说,有several such libraries用于图像加载/缓存。
答案 32 :(得分:3)
我使用droidQuery。有两种从URL加载图像的机制。第一个(简写)就是:
$.with(myView).image(url);
这可以很容易地添加到ArrayAdapter
的{{1}}方法中。
longhand方法将提供更多控制,并且具有此处未讨论的选项(例如缓存和回调),但是可以在此处找到指定输出大小为200px x 200px的基本实现:
getView(...)
答案 33 :(得分:2)
我发现Glide比Picasso
更好。
我正在使用毕加索来加载大约32
大小200-500KB
大小的OOM
张图像,我总是得到Glide
。但是OOM
解决了我的所有{{1}}问题。
答案 34 :(得分:2)
使用下面的类来下载和加载listview中的图像。它会在下载后缓存每个图像。还会加载图像广告延迟加载。
INSERT INTO access_log (id,action,previous_data,current_data)
答案 35 :(得分:2)
您可以使用某些第三方库(例如Piccaso
或Volley
)进行有效的延迟加载。您也可以通过实施以下
实施从网址下载图片的代码
实现用于存储和检索图像的缓存机制(使用android的LruCache
进行缓存)
答案 36 :(得分:1)
更新:如果您正在寻找2020年由Kotlin Coroutines支持的解决方案,请尝试Coil。
Coil是 Coroutine Image Loader 的首字母缩写。
功能
成绩设置:
线圈在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
您也可以migrate来自Picasso / Glide
完整文档here
答案 37 :(得分:0)
除了异步加载数据缓存外,您可能还需要UI缓存
除了加载可见项数据外,您可能需要加载近似可见项数据
实施例: 假设listview可见项为[6,7,8,9,10],您可能需要加载[6,7,8,9,10]并预加载项[1,2,3,4,5] ]&amp; [11,12,13,14,15],因为用户可能会滚动到前页或后页
答案 38 :(得分:0)
您可以使用GreenDroid的AsyncImageView。只需调用setUrl 我想这可能会帮助您完成您不想做的事情 并参考以下链接:
答案 39 :(得分:0)
滑行
Glide 是一个快速高效的 Android 开源媒体管理框架,它将媒体解码、内存和磁盘缓存以及资源池封装到一个简单易用的界面中。
Glide 支持获取、解码和显示视频静止图像、图像和动画 GIF。 Glide 包含一个灵活的 API,允许开发人员插入几乎任何网络堆栈。默认情况下,Glide 使用自定义的基于 HttpUrlConnection 的堆栈,但也包含了 Google 的 Volley 项目或 Square 的 OkHttp 库的实用程序库插件。
Glide.with(this).load("your-url-here").into(imageView);
Glide 的主要重点是尽可能平滑和快速地滚动任何类型的图像列表,但 Glide 也适用于几乎所有需要获取、调整大小和显示远程图像的情况。
毕加索
使用杰克沃顿的毕加索图书馆。 (来自 ActionBarSherlock 开发者的完美图片加载库)
适用于 Android 的强大图像下载和缓存库。
图像为 Android 应用程序添加了急需的上下文和视觉风格。 Picasso 允许在您的应用程序中轻松加载图像 - 通常只需一行代码!
Picasso.with(context).load("your-url-here").into(imageView);
在 Android 上加载图像的许多常见陷阱都由 Picasso 自动处理:
在适配器中处理 ImageView 回收和下载取消。 以最少的内存使用复杂的图像转换。 自动内存和磁盘缓存。