我正在使用Android,我正在使用Imagedownloader类来下载图像并按顺序设置到listview项目中的图像视图。
但是当我连续滚动列表视图时,我有时会得到Outoffmemory异常。
例外情况如下。
01-28 15:30:47.580: E/AndroidRuntime(3723): FATAL EXCEPTION: main
01-28 15:30:47.580: E/AndroidRuntime(3723): java.lang.OutOfMemoryError
01-28 15:30:47.580: E/AndroidRuntime(3723): at android.graphics.Bitmap.nativeCreate(Native Method)
01-28 15:30:47.580: E/AndroidRuntime(3723): at android.graphics.Bitmap.createBitmap(Bitmap.java:605)
01-28 15:30:47.580: E/AndroidRuntime(3723): at android.graphics.Bitmap.createBitmap(Bitmap.java:551)
01-28 15:30:47.580: E/AndroidRuntime(3723): at android.graphics.Bitmap.createScaledBitmap(Bitmap.java:437)
01-28 15:30:47.580: E/AndroidRuntime(3723): at android.graphics.BitmapFactory.finishDecode(BitmapFactory.java:618)
01-28 15:30:47.580: E/AndroidRuntime(3723): at android.graphics.BitmapFactory.decodeStream(BitmapFactory.java:593)
01-28 15:30:47.580: E/AndroidRuntime(3723): at android.graphics.BitmapFactory.decodeResourceStream(BitmapFactory.java:445)
01-28 15:30:47.580: E/AndroidRuntime(3723): at android.graphics.drawable.Drawable.createFromResourceStream(Drawable.java:773)
01-28 15:30:47.580: E/AndroidRuntime(3723): at android.content.res.Resources.loadDrawable(Resources.java:1968)
01-28 15:30:47.580: E/AndroidRuntime(3723): at android.content.res.Resources.getDrawable(Resources.java:677)
01-28 15:30:47.580: E/AndroidRuntime(3723): at com.lt.appmedia.customise.SpeakersAdapter.getView(SpeakersAdapter.java:118)
01-28 15:30:47.580: E/AndroidRuntime(3723): at android.widget.AbsListView.obtainView(AbsListView.java:2197)
01-28 15:30:47.580: E/AndroidRuntime(3723): at android.widget.ListView.makeAndAddView(ListView.java:1774)
01-28 15:30:47.580: E/AndroidRuntime(3723): at android.widget.ListView.fillDown(ListView.java:672)
01-28 15:30:47.580: E/AndroidRuntime(3723): at android.widget.ListView.fillGap(ListView.java:636)
01-28 15:30:47.580: E/AndroidRuntime(3723): at android.widget.AbsListView.trackMotionScroll(AbsListView.java:5259)
01-28 15:30:47.580: E/AndroidRuntime(3723): at android.widget.AbsListView$FlingRunnable.run(AbsListView.java:4467)
01-28 15:30:47.580: E/AndroidRuntime(3723): at android.os.Handler.handleCallback(Handler.java:605)
01-28 15:30:47.580: E/AndroidRuntime(3723): at android.os.Handler.dispatchMessage(Handler.java:92)
01-28 15:30:47.580: E/AndroidRuntime(3723): at android.os.Looper.loop(Looper.java:137)
01-28 15:30:47.580: E/AndroidRuntime(3723): at android.app.ActivityThread.main(ActivityThread.java:4511)
01-28 15:30:47.580: E/AndroidRuntime(3723): at java.lang.reflect.Method.invokeNative(Native Method)
01-28 15:30:47.580: E/AndroidRuntime(3723): at java.lang.reflect.Method.invoke(Method.java:511)
01-28 15:30:47.580: E/AndroidRuntime(3723): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:980)
01-28 15:30:47.580: E/AndroidRuntime(3723): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:747)
01-28 15:30:47.580: E/AndroidRuntime(3723): at dalvik.system.NativeStart.main(Native Method)
我无法知道如何解决这个问题。
ImageDownloader类是
public class ImageDownloader {
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 = 150;
public int THREAD_POOL_SIZE = 3;
private static ImageDownloader imageDownloader;
public static ImageDownloader shareInstance(){
if(imageDownloader == null){
imageDownloader = new ImageDownloader();
}
return imageDownloader;
}
/**
* Constructor
*/
public ImageDownloader() {
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.setBackgroundDrawable(drawable);
} else {
imageView.setBackgroundDrawable(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.setBackgroundDrawable((Drawable) msg.obj);
} else {
// imageView.setImageDrawable(placeholder);
imageView.setBackgroundDrawable(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;
}
}
我正在通过以下方式将图像设置为ListView项目:
imageDownloader.loadDrawable(EventsListActivity.stylesheet.getBaseurl()+""+speaker.getPhoto(), imageView, mContext.getResources().getDrawable( R.drawable.noimage ));
答案 0 :(得分:0)
ImageDownloader
没有问题,但列表中没有问题。
创建包含大量图像的列表时,请按照以下几个步骤进行操作:
重复使用商品视图。这是由适配器支持并且易于实现。
不要在本地保存所有图像。如果位图需要占用大量空间,则应该释放对旧版本的引用,并让GC收集它们。当列表向后滚动时,也不要忘记再次下载它们。
答案 1 :(得分:0)
资源受到移动设备的限制,尤其是在处理列表视图中的多个图像时,尤其是在Android上。
我建议:
调查LRUCache 以缓存图片,以防止再次下载。您可以在此处找到相关信息:http://android-er.blogspot.com.au/2012/07/caching-bitmaps-with-lrucache.html和此处:http://developer.android.com/reference/android/util/LruCache.html。如果您的目标是较旧的平台,它也会位于支持库中。
实施列表适配器 - 这就是它们的用途。这样,您只会耗尽资源来显示可见图像。关于这个和下载图像有一些很好的讨论:How to display a list of images in a ListView in Android?
使用Bitmpafactory 缩小图像:http://developer.android.com/reference/android/graphics/BitmapFactory.html - 这将确保您不会分配资源。仅使用您需要的尺寸/比例。
覆盖适配器类时,请确保使用上面的LRUCache并首先 检查图像是否在缓存中,如果没有,则下载图片。这将大大加快向上滚动速度,而不必重新获取图像。
图像是棘手的资源占用,但是一个整洁的适配器,正确的缩放/重新采样和使用缓存将带来可靠的用户体验,而不会出现令人讨厌的内存错误。