我正在使用ListView
来显示一些与图像相关的标题和图像。 我正在从互联网上获取这些图像。 有没有一种方法可以懒加载图像,以便在文本显示时不会阻塞UI,并且图像会随着下载而显示?
图片的总数并不是固定的。
我正在使用ListView
来显示一些与图像相关的标题和图像。 我正在从互联网上获取这些图像。 有没有一种方法可以懒加载图像,以便在文本显示时不会阻塞UI,并且图像会随着下载而显示?
图片的总数并不是固定的。
这是我创建的用于容纳应用程序当前显示的图像的代码。请注意,此处使用的“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();
}
}
我使用了图片制作了一个简单的懒加载列表示例(位于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解码图像以减少内存消耗。我还尝试正确处理回收的视图。
《多线程提升性能》,由Gilles Debunne所写的教程。
这是来自Android Developers Blog的内容。建议使用以下代码:
AsyncTasks
。FIFO缓存
。垃圾回收
的缓存。Drawable
。更新:请注意,这个答案现在已经不太有效了。垃圾收集器在SoftReference和WeakReference上表现得很积极,因此该代码不适用于新的应用程序。 (相反,您可以尝试像其他答案中建议的Universal Image Loader等库。)
感谢James提供的代码,以及Bao-Long建议使用SoftReference的建议。我在James的代码上实现了SoftReference更改。不幸的是,SoftReferences使我的图片被垃圾回收得太快了。在我的情况下,没有SoftReference也没关系,因为我的列表大小有限,我的图片很小。
一年前在Google群组中有讨论SoftReference的话题:链接到线程。作为避免过早垃圾回收的解决方案,他们建议可能使用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();
}
毕加索
使用Jake Wharton的Picasso库。(来自ActionBarSherlock的开发者的完美图像加载库)
Picasso是一款强大的Android图像下载和缓存库。
在Android应用程序中,图像为应用程序添加了必要的上下文和视觉效果。Picasso允许您在应用程序中轻松加载图像,通常只需一行代码!
Picasso.with(context).load("http://i.imgur.com/DvpvklR.png").into(imageView);
在Android上,Picasso自动处理许多常见的图像加载陷阱:
适配器中的ImageView回收和下载取消处理。 最小内存使用的复杂图像转换。 自动内存和磁盘缓存。
Picasso Jake Wharton's Library
Glide
Glide是一个快速高效的开源媒体管理框架,将媒体解码、内存和磁盘缓存以及资源池包装成一个简单易用的接口。
Glide支持获取、解码和显示视频静帧、图像和动画GIF。Glide包括一个灵活的API,允许开发人员插入几乎任何网络堆栈。默认情况下,Glide使用基于自定义HttpUrlConnection的堆栈,但也包括实用程序库插件到Google的Volley项目或Square的OkHttp库。
Glide.with(this).load("your-url-here").into(imageView);
Glide主要专注于使任何类型的图像列表滚动尽可能流畅和快速,但Glide也适用于几乎所有需要获取、调整大小和显示远程图像的情况。
Facebook 的 Fresco
Fresco是Android应用程序中展示图片的强大系统。
Fresco会负责图像的加载和显示,因此您不需要这样做。它将从网络、本地存储或本地资源加载图像,并显示占位符,直到图像到达为止。它有两个级别的缓存:内存中的一个和内部存储中的另一个。
在 Android 4.x 及更低版本中,Fresco会将图像放置在 Android 内存的特殊区域中。这可以让您的应用程序运行得更快,也可以避免经常出现的 OutOfMemoryError。
高性能加载器 - 在研究了这里提供的方法后,我使用了Ben的解决方案,并做了一些改动:
我发现使用Drawable比Bitmap更快,所以我使用了Drawable。
使用SoftReference很好,但它会导致缓存图片被频繁删除,因此我添加了一个保存图片引用的LinkedList,防止图片被删除,直到达到预定义的大小。
为了打开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;
}
}
import java.util.Map;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Collections;
import java.util.WeakHashMap;
import java.lang.ref.SoftReference;
import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
import android.graphics.drawable.Drawable;
import android.widget.ImageView;
import android.os.Handler;
import android.os.Message;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.io.IOException;
import java.net.URL;
import java.net.URLConnection;
- Michael Reed我已经学习了这个Android培训课程,认为它在下载图像时做得非常出色,而不会阻塞主UI。它还处理缓存并处理浏览多个图像的滚动:
高效加载大型位图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负责图片的加载和展示,因此您无需自行处理。
我写了一篇教程,详细解释了如何在列表视图中进行图片的懒加载。我深入讲解了回收和并发问题。同时,我使用了一个固定的线程池来避免产生大量线程。
setUrl
即可。 - Pascal Dimassimo