位图内存泄漏-Android

问题描述 投票:4回答:4

我的应用程序中发生内存泄漏,这多次触发GC并导致性能问题。我使用leak suspect report生成了MAT。这是报告:

问题嫌疑人1:由“”加载的“ android.graphics.Bitmap”的一个实例占用4,194,368(20.13%)字节。内存在由“”加载的“ byte []”的一个实例中累积。

问题嫌疑人2:

由“”加载的类“ android.content.res.Resources”占用3,962,504(19.02%)字节。内存累积在由“”加载的“ java.lang.Object []”的一个实例中。

问题嫌疑人3:由“”加载的“ android.graphics.Bitmap”的一个实例占用3,145,792(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);
                    }
                }
            });
        }
    }

}

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

注意:

我没有使用过bitmap.recycle(),因为该文档说,蜂窝后GC会收集位图,并且不再需要强行回收它!

我的应用程序中发生内存泄漏,这多次触发GC并导致性能问题。我使用MAT生成了泄漏怀疑报告。这是报告:问题嫌疑人1:一个...

内存泄漏问题始终是Java的问题。我了解您的代码,代码简单的imagecache工具。检查SampleSize值,并且执行程序服务仅在一个线程上运行。四个线程有很大的内存,并且此后台线程有作用。您的“处理程序”交换为“ runOnUIThread”

您应使用;

活动活动

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

我认为问题是Singleton实例...我制作了LazyList项目的分支,请检查以下内容:

https://github.com/nicolasjafelle/LazyList

我有相同的内存泄漏,但是,也许我是错的,除非您使用System.exit()终止进程,否则永远不会垃圾收集此单例。

这就是原始LazyList项目不使用Singleton的原因。我还认为,如果您需要缓存,那么对于所有应用程序来说,它都将是快速且相同的。

此ImageLoader中重要的是FileCache和MemoryCache,当您调用clearCache收集的位图时。

我认为答案很简单,当不需要内存时/当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;
    }

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

android memory-management memory-leaks bitmap out-of-memory
4个回答
0
投票

内存泄漏问题始终是Java的问题。我了解您的代码,代码简单的imagecache工具。检查SampleSize值,并且执行程序服务仅在一个线程上运行。四个线程有很大的内存,并且此后台线程有作用。您的“处理程序”交换为“ runOnUIThread”

您应使用;


0
投票

我认为问题是Singleton实例...我制作了LazyList项目的分支,请检查以下内容:

https://github.com/nicolasjafelle/LazyList

我有相同的内存泄漏,但是,也许我是错的,除非您使用System.exit()终止进程,否则永远不会垃圾收集此单例。


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
投票

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

© www.soinside.com 2019 - 2024. All rights reserved.