位图内存泄漏 - Android

4
我在我的应用程序中遇到了内存泄漏问题,触发了多次GC并导致性能问题。我使用MAT生成了一个“泄漏嫌疑报告”。以下是报告内容:
问题嫌疑1: 由“”加载的“android.graphics.Bitmap”实例占用4194368(20.13%)字节。该内存累积在由“”加载的“byte[]”实例中。
问题嫌疑2: 由“”加载的“android.content.res.Resources”类占用3962504(19.02%)字节。该内存累积在由“”加载的“java.lang.Object[]”实例中。
问题嫌疑3: 由“”加载的“android.graphics.Bitmap”实例占用3145792(15.10%)字节。该内存累积在由“”加载的“byte[]”实例中。
根据报告,很明显内存泄漏是由位图引起的。我进行了大量研究,但无法解决这个泄漏问题。请帮我解决这个问题。我正在使用ImageLoader类来下载和显示位图。为了使用此类,我只需调用displayImage()方法。以下是代码:
public class ImageLoader {

    private static ImageLoader imageLoader;
    private int maxNoOfConnections = 4;
    FileCache fileCache;
    ExecutorService executorService;
    HttpURLConnection conn;
    InputStream is;
    OutputStream os;
    PhotosLoader photosLoader;
    Handler handler;
    Bitmap bitmap;

    private ImageLoader(Context context) {
        fileCache = new FileCache(context);
        executorService = Executors.newFixedThreadPool(maxNoOfConnections);
        handler = new Handler();
    }

    public static ImageLoader getInstance(Context context) {
        if (imageLoader == null)
            imageLoader = new ImageLoader(context);
        return imageLoader;
    }

    public void displayImage(String url, ProgressBar pBar, ImageView imageView) {
        photosLoader = new PhotosLoader(url, imageView, pBar);
        executorService.submit(photosLoader);
    }

    private Bitmap getBitmap(String url) {
        File f = fileCache.getFile(url);

        bitmap = decodeFile(f);
        if (bitmap != null)
            return bitmap;

        try
        {
            URL imageUrl = new URL(url);
            conn = (HttpURLConnection) imageUrl.openConnection();
            conn.setConnectTimeout(30000);
            conn.setReadTimeout(30000);
            conn.setInstanceFollowRedirects(true);
            is = conn.getInputStream();
            os = new FileOutputStream(f);
            Utils.CopyStream(is, os);
            os.close();
            bitmap = decodeFile(f);
            return bitmap;
        } catch (Exception ex)
        {
            Log.e("inNews", "Image Url Malformed");
            return null;
        }
    }

    private Bitmap decodeFile(File f) {
        try
        {
            BitmapFactory.Options o = new BitmapFactory.Options();
            o.inJustDecodeBounds = true;
            BitmapFactory.decodeStream(new FileInputStream(f), null, o);

            final int REQUIRED_SIZE = 70;
            int width_tmp = o.outWidth, height_tmp = o.outHeight;
            int scale = 1;
            while (true)
            {
                if (width_tmp / 2 < REQUIRED_SIZE || height_tmp / 2 < REQUIRED_SIZE)
                    break;
                width_tmp /= 2;
                height_tmp /= 2;
                scale *= 2;
            }

            BitmapFactory.Options o2 = new BitmapFactory.Options();
            o2.inSampleSize = scale;
            return BitmapFactory.decodeStream(new FileInputStream(f), null, o2);
        } catch (FileNotFoundException e)
        {
        }
        return null;
    }

    class PhotosLoader implements Runnable {
        String url;
        ImageView imageView;
        ProgressBar pBar;
        Bitmap bmp;

        public PhotosLoader(String url, ImageView imageView, ProgressBar pBar) {
            this.url = url;
            this.imageView = imageView;
            this.pBar = pBar;
        }

        @Override
        public void run() {
            bmp = getBitmap(url);
            handler.post(new Runnable() {
                @Override
                public void run() {
                    if (bmp != null)
                    {
                        pBar.setVisibility(View.GONE);
                        imageView.setImageBitmap(bmp);
                    } else
                    {
                        pBar.setVisibility(View.GONE);
                        imageView.setImageResource(R.drawable.img_no_image_grid);
                    }
                }
            });
        }
    }

}

请帮我纠正我的代码。谢谢!

注意:自从Honeycomb之后,GC会收集位图,因此不必强制回收它!因此,我没有使用bitmap.recycle()


当您旋转设备时,是否在随机时间总是在同一时间收到内存泄漏? - Fustigador
@Fustigador 每当我执行任何涉及加载图像的操作时,而不仅仅是旋转设备。 - Gaurav Arora
@PlumillonForge 我不想存储引用。我的内存已经很短了! :) - Gaurav Arora
@gauravsapiens 我有5个图像泄漏。我该如何解决?希望你能成功,所以请给我建议。由“<系统类加载器>”加载的27个“android.graphics.Bitmap”实例占用2,977,016(24.40%)字节。最大的实例:•android.graphics.Bitmap @ 0x4122a3e8 - 1,048,640(8.60%)字节。 •android.graphics.Bitmap @ 0x41256058 - 635,856(5.21%)字节。 •android.graphics.Bitmap @ 0x41aeda78 - 326,104(2.67%)字节。 •android.graphics.Bitmap @ 0x4187fb90 - 157,504(1.29%)字节。 •android.graphics.Bitmap @ 0x41a1a4e8 - 144,064(1.18%)字节。 - Md Maidul Islam
@gauravsapiens 我也遇到了三个可疑的泄漏错误,请问我该如何解决? - Md Maidul Islam
显示剩余2条评论
4个回答

0

我认为答案很简单,当您不需要内存/发生OOM异常时,只需保持清除缓存即可。我已经替你做了这件事

MemoryCache memoryCache = new MemoryCache();

try {
        Bitmap bitmap = null;
        URL imageUrl = new URL(url);
        HttpURLConnection conn = (HttpURLConnection) imageUrl
                .openConnection();
        conn.setConnectTimeout(30000);
        conn.setReadTimeout(30000);
        conn.setInstanceFollowRedirects(true);
        InputStream is = conn.getInputStream();
        OutputStream os = new FileOutputStream(f);
        Utils.CopyStream(is, os);
        os.close();
        conn.disconnect();
        bitmap = decodeFile(f);
        return bitmap;
    } catch (Throwable ex) {
        ex.printStackTrace();
        if (ex instanceof OutOfMemoryError)
            memoryCache.clear();
        return null;
    }

0

我认为问题出在单例实例上... 我创建了一个LazyList项目的分支,请查看:

https://github.com/nicolasjafelle/LazyList

我有同样的内存泄漏问题,也许我错了,除非你使用System.exit()来终止进程,否则这个单例永远不会被垃圾回收。

这就是为什么原始的LazyList项目没有使用单例。我还认为,如果你需要一个缓存,你需要它快速且适用于整个应用程序。

在这个ImageLoader中,重要的是FileCache和MemoryCache,当你调用clearCache时,位图将被收集。


0

当您需要在活动中初始化库时,请始终传递应用程序上下文,而不是活动上下文。


0

内存泄漏问题总是Java的问题。

我理解你的代码,编写了一个简单的图像缓存工具。检查SampleSize值并且执行器服务只在一个线程上运行。四个线程有很大的内存和这个后台线程操作。你的“handler”应该换成“runOnUIThread”

你应该使用:

Activity activity = (Activity)imageView.getContext();

__activity__.runOnUIThread(new Runnable()
{
 if (bmp != null)
                    {
                        pBar.setVisibility(View.GONE);
                        imageView.setImageBitmap(bmp);
                    } else
                    {
                        pBar.setVisibility(View.GONE);
                        imageView.setImageResource(R.drawable.img_no_image_grid);
                    }
});

我已经尝试过了,但没有帮助,反而可能会不必要地保留对“Activity”的引用。 - Gaurav Arora
我使用与你的代码相同的imagecache,但我没有遇到任何内存泄漏错误。如果你需要的话,我会在2-3小时后发送我的代码。你可以给我一个沟通链接,比如“电子邮件”,我会发送Java文件组(包)。 - nurisezgin
是的,我的邮箱是g.arora217@gmail.com。谢谢! :) - Gaurav Arora
@nurisezgin 你好,我遇到了很多内存泄漏问题。你能否把代码发送到我的邮箱mmaidul.islam@gmail.com?谢谢 :) - Md Maidul Islam
也许你会使用 Google Volley 库 "git clone https://android.googlesource.com/platform/frameworks/volley" 或者使用 BitmapFactory.Options ==> inSampleSize 属性。 - nurisezgin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接