3

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

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 数量的 3 倍(screenWidth * screenHeight * 4)。我正在运行 4.3 的 Nexus 4 上进行测试,我从未遇到过 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 使用有什么问题吗?我对 Pager 的过渡也不满意,有点滞后,这有关系吗?

更新: 这是 MAT 的截图,可能泄漏。这适用于使用 Volley 库的每个活动。我已经阅读了很多,但我无法解决问题。凌空是造成泄漏还是我做错了什么?

4

4 回答 4

3

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

一旦你找到一个泄露的对象,右键单击它并让 MAT 将其所有传入引用追溯到它们的 GC 根。泄漏的参考将是其中之一。

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

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

于 2013-11-11T23:25:11.660 回答
2

我猜你正在使用Viewpager 和 Imageviews

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

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

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

尝试学习Google android Gallary应用示例,使用图像视图加载动画来打造出色的用户体验。

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

致谢:FragmentPagerAdapter 与 FragmentStatePagerAdapter

于 2013-11-13T04:42:34.550 回答
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);
}
于 2014-03-30T20:35:55.027 回答
0

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

Volley 的新版本已经解决了这个问题。 请在这里查看

使用它可以帮助您找出泄漏的 Activity ,leakcanary

于 2015-07-19T03:47:26.337 回答