如何解决以下内存异常?

时间:2013-01-28 10:16:32

标签: android out-of-memory

我正在使用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 ));

2 个答案:

答案 0 :(得分:0)

ImageDownloader没有问题,但列表中没有问题。

创建包含大量图像的列表时,请按照以下几个步骤进行操作:

  1. 重复使用商品视图。这是由适配器支持并且易于实现。

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

答案 1 :(得分: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并首先 检查图像是否在缓存中,如果没有,则下载图片。这将大大加快向上滚动速度,而不必重新获取图像。

  5. 图像是棘手的资源占用,但是一个整洁的适配器,正确的缩放/重新采样和使用缓存将带来可靠的用户体验,而不会出现令人讨厌的内存错误。