使用位图和排球查看寻呼机内存泄漏

时间:2013-11-11 21:30:03

标签: android memory-management memory-leaks bitmap android-viewpager

我正在使用View Pager显示在我的应用程序中从网络下载的图像。图像的数量可以是5到20.我正在使用Volley库来进行网络操作。应用程序以前没有占用太多内存,但现在添加视图寻呼机后,应用程序需要大量内存,每次打开此活动时,堆中使用的内存会增加(从日志消息中检查)。我还使用Eclipse Memory分析器来检查泄漏的位置,这绝对是位图和此活动的多个实例。肯定存在泄漏,因为此活动未获得GC,一些参考文献正在阻止垃圾收集。我在这里添加了view viewr的实现。

public class ViewPagerAdapter extends PagerAdapter {
        Context context;

        public ViewPagerAdapter(Context context) {
            this.context = context;
        }

        @Override
        public int getCount() {
            return photoReferences.size();
        }

        @Override
        public boolean isViewFromObject(View view, Object object) {
            return view == ((RelativeLayout) object);
        }

        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            final ImageView im;
            final ProgressBar pb;

            View itemView = inflater.inflate(R.layout.place_photos_item, container, false);

            im = (ImageView) itemView.findViewById(R.id.placeImage);
            attributes = (TextView) itemView.findViewById(R.id.placeAttributes);
            pb = (ProgressBar) itemView.findViewById(R.id.progressBarPhoto);

            imageLoader.get(url, new ImageListener() {

                public void onErrorResponse(VolleyError arg0) {
                    im.setImageResource(R.drawable.onErrorImage); 
                }

                public void onResponse(ImageContainer response, boolean arg1) {
                    if (response.getBitmap() != null) {
                        im.startAnimation(AnimationUtils.loadAnimation(context, android.R.anim.fade_in));
                        im.setImageBitmap(response.getBitmap());
                        pb.setVisibility(View.GONE);
                    } 
                }
            });

            ((ViewPager) container).addView(itemView);

            return itemView;
        }

        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            ((ViewPager) container).removeView((RelativeLayout) object);
        }

    }

另外,我使用的是尺寸为screenBytes(screenWidth * screenHeight * 4)3倍的位图缓存。我正在测试Nexus 4运行4.3并且我从未遇到过OOM异常,因为此设备上的堆大小很大但是如果我打开活动,应用程序可能需要超过100 MB的内存(它会在大多数设备上崩溃)一次又一次,在此之前,无论怎样,它都需要大约16-20 mbs的内存。这是缓存代码。

public class BitmapCache extends LruCache<Object, Object> implements ImageCache {
        public BitmapCache(int maxSize) {
            super(maxSize);
        }

        @Override
        public Bitmap getBitmap(String url) {
            return (Bitmap) get(url);
        }

        @Override
        public void putBitmap(String url, Bitmap bitmap) {
            put(url, bitmap);
        }
    }

有人可以建议我该怎么办才能发现泄漏? View Pager或我的Volley用法有什么问题吗?我对寻呼机的过渡感到不满意,有点滞后,那是相关的吗?

更新: Here's the screenshot of MAT, possible leak。这适用于使用Volley库的每个活动。我一直在阅读,但我无法解决问题。排球是否会导致泄漏,或者我做错了什么?

4 个答案:

答案 0 :(得分:3)

您可以使用MAT找到您的泄漏。首先,您运行应用程序并泄漏一些活动实例。然后你抓住堆的快照并查找那些泄露的Activity对象......你可以使用'对象查询语言'(OQL)按类型查找它们(例如“SELECT * FROM com.foo.FooActivity”)。

找到泄漏的对象后,右键单击它并要求MAT将其所有传入的引用跟踪回GC根。泄露的参考文献将是其中之一。

为了更好地介绍该技术,您可以尝试这篇文章:

http://android-developers.blogspot.co.uk/2011/03/memory-analysis-for-android.html

答案 1 :(得分:2)

我猜你正在使用 Viewpager和Imageviews

关于图片视图您正在使用功能强大的图片下载和缓存库,例如最新的 Volley Imageloading(对大尺寸图片非常有用),可以高效地提高图片加载能力方式。

关于Viewpager 您必须使用高效的适配器 FragmentStatePagerAdapter : 当存在大量页面时,此版本的寻呼机更有用,更像列表视图。当页面对用户不可见时,它们的整个片段可能会被破坏,只保留该片段的保存状态。与FragmentPagerAdapter相比,这允许寻呼机保持与每个被访问页面相关联的更少内存,代价是在页面之间切换时可能产生更多开销。

请在使用FragmentPagerAdapter之前考虑一下因为它将整个片段存储在内存中,如果在ViewPager中使用了大量片段,可能会增加内存开销。与其兄弟相反,FragmentStatePagerAdapter仅存储片段的savedInstanceState,并在失去焦点时销毁所有片段。因此,当我们必须使用动态片段(如具有小部件的片段)时,应使用FragmentStatePagerAdapter,因为它们的数据可以存储在savedInstanceState中。即使存在大量碎片,它也不会影响性能。相反,当我们需要将整个片段存储在内存中时,应该使用它的兄弟FragmentPagerAdapter。当我说整个片段保存在内存中意味着,它的实例不会被破坏并且会产生内存开销。因此,建议仅在ViewPager的片段数较少时才使用FragmentPagerAdapter。如果片段是静态的,那将会更好,因为它们不会有大量的实例存储的对象。希望这能清除Android FragmentPagerAdapter和FragmentStatePagerAdapter之间的区别。

尝试学习Google android gallary应用示例,使用图片视图加载动画来提供出色的用户体验。

我希望这能解决你的堆积问题。

现金:FragmentPagerAdapter vs FragmentStatePagerAdapter

答案 2 :(得分:0)

您忘了回收已下载的Bitmaps,因为它们不再需要。

基本上,每手动处理Bitmap,您必须recyle()

话虽如此,您的destroyItem()方法看起来应该是这样的:

public void destroyItem(ViewGroup container, int position, Object object) {

    RelativeLayout rl = (RelativeLayout) object;
    ImageView im = rl.findViewById(R.id.image_view);

    bitmapDrawable = (BitmapDrawable) im.getDrawable();
    if (bitmapDrawable != null && bitmapDrawable.getBitmap() != null) {
        bitmap = bitmapDrawable.getBitmap();
        bitmap.recycle();
    }
    container.removeView(rl);
}

答案 3 :(得分:0)

你应该查看新版本的Volley,旧版本确实导致了泄漏问题。 在旧版本中,Volley有4个线程做请求,并且每个都会保留一个请求,并请求保持强大的监听器引用,并且你的响应监听器用ImageView做一些事情,ImageView保留Activity的上下文。所以你的所有视图都泄露了。 在MAT中使用select * from instanceof android.app.Activity,您将看到您的活动被泄露。

新版Volley解决了这个问题。 please check out here

使用它可以帮助您找到泄露的活动leakcanary