0

我正在使用 Android,我正在使用 Imagedownloader 类下载图像并按顺序设置为列表视图项目中的图像视图。

但是当我连续滚动列表视图时,有时会出现 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 ));
4

2 回答 2

0

中没有问题ImageDownloader,但与您的列表有关。

当您创建包含大量图像的列表时,请执行以下几个步骤:

  1. 重用项目视图。这由适配器支持并且易于实现。

  2. 不要在本地保存所有图像。如果您的位图占用大量空间,您应该释放对旧的引用,并让 GC 收集它们。当列表向后滚动时,不要忘记再次下载它们。

于 2013-01-28T10:28:45.890 回答
0

资源在移动设备上受到限制,这在 Android 上尤为突出,尤其是在处理诸如此类的列表视图中的多个图像时。

我会建议:

  1. 调查 LRUCache以缓存您的图像,以防止再次下载它们。您可以在此处找到有关此信息:http ://android-er.blogspot.com.au/2012/07/caching-bitmaps-with-lrucache.html和此处:http: //developer.android.com/reference/ android/util/LruCache.html。如果您针对的是旧平台,它也在支持库中。

  2. 实现一个列表适配器——这就是他们的目的。这样,您只会用尽资源来显示可见图像。这里有一些关于这个和下载图像的很好的讨论:How to display a list of images in a ListView in Android?

  3. 使用 Bitmpafactory缩小图像: http: //developer.android.com/reference/android/graphics/BitmapFactory.html - 这将确保您不会过度分配资源。仅使用您需要的尺寸/比例。

  4. 覆盖适配器类时,请确保您使用上面的 LRUCache 并首先 检查图像是否在缓存中,如果没有,则下载图像。这将大大加快向上滚动,而不必重新获取图像。

图像是棘手的资源消耗,但整洁的适配器、适当的缩放/重采样和缓存的使用将提供可靠的用户体验,而不会出现令人讨厌的内存不足错误。

于 2013-01-28T12:45:25.243 回答