image - ListView中Lazy load图像

  显示原文与译文双语对照的内容

我正在使用 ListView 来显示一些与这些图像相关的图像和字幕。 我正在从互联网上获取图像。 是否有一种方法可以在文本显示时使用 延迟加载,当文本显示时,用户界面不会被锁定,图像也会被下载?

图像的数目不固定。

时间:

这是我创建的用来保存我的应用当前显示的图像的东西。 请注意,这里使用的"日志"对象是我在Android中最后一个日志类的定制包装。


package com.wilson.android.library;

/*
 Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
import java.io.IOException;

public class DrawableManager {
 private final Map<String, Drawable> drawableMap;

 public DrawableManager() {
 drawableMap = new HashMap<String, Drawable>();
 }

 public Drawable fetchDrawable(String urlString) {
 if (drawableMap.containsKey(urlString)) {
 return drawableMap.get(urlString);
 }

 Log.d(this.getClass().getSimpleName(),"image url:" + urlString);
 try {
 InputStream is = fetch(urlString);
 Drawable drawable = Drawable.createFromStream(is,"src");


 if (drawable!= null) {
 drawableMap.put(urlString, drawable);
 Log.d(this.getClass().getSimpleName(),"got a thumbnail drawable:" + drawable.getBounds() +","
 + drawable.getIntrinsicHeight() +"," + drawable.getIntrinsicWidth() +","
 + drawable.getMinimumHeight() +"," + drawable.getMinimumWidth());
 } else {
 Log.w(this.getClass().getSimpleName(),"could not get thumbnail");
 }

 return drawable;
 } catch (MalformedURLException e) {
 Log.e(this.getClass().getSimpleName(),"fetchDrawable failed", e);
 return null;
 } catch (IOException e) {
 Log.e(this.getClass().getSimpleName(),"fetchDrawable failed", e);
 return null;
 }
 }

 public void fetchDrawableOnThread(final String urlString, final ImageView imageView) {
 if (drawableMap.containsKey(urlString)) {
 imageView.setImageDrawable(drawableMap.get(urlString));
 }

 final Handler handler = new Handler() {
 @Override
 public void handleMessage(Message message) {
 imageView.setImageDrawable((Drawable) message.obj);
 }
 };

 Thread thread = new Thread() {
 @Override
 public void run() {
//TODO : set imageView to a"pending" image
 Drawable drawable = fetchDrawable(urlString);
 Message message = handler.obtainMessage(1, drawable);
 handler.sendMessage(message);
 }
 };
 thread.start();
 }

 private InputStream fetch(String urlString) throws MalformedURLException, IOException {
 DefaultHttpClient httpClient = new DefaultHttpClient();
 HttpGet request = new HttpGet(urlString);
 HttpResponse response = httpClient.execute(request);
 return response.getEntity().getContent();
 }
}

我让简单地演示了一个带有图像的惰性列表 ( 位于 GitHub ) 。 对某人有帮助。 它在后台线程中下载图像。 图像被缓存在SD卡和内存中。 缓存实现非常简单,只适用于演示。 我用inSampleSize解码图像以减少内存消耗。 我也试图正确处理回收的视图。

Alt text

我推荐开源工具 通用图像加载程序 。 它最初是基于 Fedor vlasov的项目 LazyList 和已经被大幅改进的自那以后。

  • 多线程图像加载
  • imageloader ( 线程执行器,downlaoder,解码器,内存和光盘缓存,显示图像选项等)的广泛优化配置
  • 在内存和/或者设备系统( 或者SD卡) 中缓存图像的可能性
  • "收听"加载过程的可能性
  • 使用分离选项自定义每个显示图像调用的可能性
  • 小部件支持
  • Android 2.0 + 支持

多线程处理性能,Gilles Debunne教程。

这是来自Android开发者博客的。 建议的代码使用:

  • AsyncTasks
  • 一个硬的,有限的大小,FIFO cache
  • 一个软的,易于使用的garbage collect -ed缓存。
  • 一个占位符 Drawable 虽然你下载。

enter image description here

请注意:现在这个答案是非常无效的。 垃圾收集器积极地在SoftReference和WeakReference上操作,所以这个代码不适用于新的应用。 ( 相反,尝试使用通用图像加载器建议的库) 。

感谢James的代码,以及Bao-Long使用SoftReference的建议。 我在James的代码上实现了SoftReference更改。 不幸的是SoftReferences让我的图像收集得太快了。 在我的例子中,也没关系,而这SoftReference之类的,因为我的列表的大小是有限的。我的图片是小。

在一年前,有一个关于SoftReferences网上论坛的讨论: 链接到线程 。 使用 dalvik.system.VMRuntime.setMinimumHeapSize(), 哪个不是很吸引me,作为他们推荐的解决了垃圾收集 too-early 。手动设置虚拟机堆size.的可能性


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

高性能加载器- 在检查本文建议的方法之后,我使用了 的解决方案,并进行了一些更改

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

在下载不会阻塞用户的图像主要ui,我有按照本安卓培训和我认为那样的行为一个很好的工作, 它还处理缓存和处理许多图像: 正在高效地加载大位图

我已经编写了一个教程,解释如何在一个listview中执行图像 lazy-loading 。 有关回收和并发问题的详细信息,我将介绍。 我也使用一个固定的线程池来防止大量线程的产生。

延迟加载Listview教程中的图像

我这样做的方法是启动一个线程来下载背景中的图像并为每个列表项提供一个回调。 当图像完成下载时,它将调用更新列表项视图的回调。

当你在回收视图时,这里方法不工作。

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

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

...