11

我不确定是否可以/应该将带有 Universal Image Loader 的 ViewPager 用作类似画廊的界面的替代品,因为我在从 SD 卡加载图像并以全屏模式查看它们时遇到了内存不足错误。无论数字是多少,GridView 都可以正常工作,但是在 View Pager 中查看图像时,每个位图都会占用大量内存,并且在 10 个左右的图像之后,它会出现内存不足的错误。

在使用通用图像加载器时,我已经看到了几乎所有与内存不足错误相关的问题,并且在每个问题中,都存在配置错误作为原因。

我不知道我是否使用了错误的配置或什么,但我已经浪费了很多时间并且有点卡住了,任何帮助/建议将不胜感激。

ImageLoader 的配置:

ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(getApplicationContext())
            .memoryCache(new WeakMemoryCache())
            .denyCacheImageMultipleSizesInMemory()
            .discCacheFileNameGenerator(new Md5FileNameGenerator())
            .imageDownloader(new ExtendedImageDownloader(getApplicationContext()))
            .tasksProcessingOrder(QueueProcessingType.LIFO)
//          .enableLogging() // Not necessary in common
            .build();

显示图像选项是:

options = new DisplayImageOptions.Builder()
            .showImageForEmptyUri(R.drawable.image_for_empty_url)
            .resetViewBeforeLoading()
            .imageScaleType(ImageScaleType.IN_SAMPLE_INT)
            .bitmapConfig(Bitmap.Config.RGB_565)
            .displayer(new FadeInBitmapDisplayer(300))
            .build();

我正在使用库提供的示例项目,但这些设置也不起作用,它只是在一段时间后崩溃。我的猜测是有一个特定的回调,我必须从不可见的视图中回收位图。

编辑:我知道它是内存泄漏,不可见的视图在应该被破坏时被破坏,但内存没有按应有的方式释放。这是destroyItem回调的实现,按照不同问题中给出的提示,但仍然找不到内存泄漏。

@Override
        public void destroyItem(View container, int position, Object object) {
//          ((ViewPager) container).removeView((View) object);
            Log.d("DESTROY", "destroying view at position " + position);
            View view = (View)object;
            ((ViewPager) container).removeView(view);
            view = null;
        }
4

7 回答 7

5

这可能不是解决它的最佳实现,但它对我有用。删除 ImageViews 是不够的,所以我决定回收 'destroyItem' 中的位图:

@Override
public void destroyItem(ViewGroup container, int position, Object object) {
    View view = (View) object;
    ImageView imageView = (ImageView) view.findViewById(R.id.image);
    if (imageView != null) {
        Bitmap bitmap = ((BitmapDrawable) imageView.getDrawable()).getBitmap();
        bitmap.recycle();
        bitmap = null;
    }
    ((ViewPager) container).removeView(view);
    view = null;
}

当您离开活动时,这不会清除最后 3 个活动页面,尽管我希望 GC 处理它们。

于 2013-03-12T17:31:21.877 回答
4

尝试应用下一个建议:

  1. 采用ImageScaleType.EXACTLY
  2. 启用光盘缓存(在显示选项中)。
  3. 最后尝试使用.discCacheExtraOptions(maxImageWidthForDiscCache, maxImageHeightForDiscCache, CompressFormat.PNG, 0);
于 2013-02-24T16:13:56.750 回答
2

之所以发布这个,是因为在搜索 UIL 和 OOP 时,Google 上会出现这个问题。无论什么配置,我都遇到了 OOP 问题,解决了我所有问题的是两个类和RecyclingImageView这个示例项目。RecyclingBitmapDrawable

于 2013-03-26T08:10:46.793 回答
1

我也使用了相同的库并且有相同的错误。作为解决方案,我创建了一个 sparseArray 来保留 photoView 实例。并像这样使用它:

 private SparseArray<PhotoView> photoViewHolder;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
       ...

       photoViewHolder = new SparseArray<PhotoView>();
       ...
 }

private class GalleryPagerAdapter extends PagerAdapter {

@Override
public View instantiateItem(ViewGroup container, int position) { 

        PhotoView photoView = new PhotoView(container.getContext());

        ImageHolder holder = new ImageHolder();
        holder.position = position;
        holder.loaded = false;

        photoView.setTag(holder);
        photoViewHolder.put(position, photoView);

                    // I used LazyList loading
        loader.DisplayImage(items.get(position), photoView);

        // Now just add PhotoView to ViewPager and return it
        container.addView(photoView, LayoutParams.MATCH_PARENT,
                LayoutParams.MATCH_PARENT);

        return photoView;
    }

@Override
public void destroyItem(ViewGroup container, int position, Object object) {
    container.removeView((View) object);
    photoViewHolder.remove(position);
}

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

}

并处理 viewPager 的监听器:

   pager.setOnPageChangeListener(new OnPageChangeListener() { 

    @Override
    public void onPageScrollStateChanged(int position) { 

    } 

    @Override
    public void onPageScrolled(int position, float arg1, int arg2) { 

    } 

    @Override
    public void onPageSelected(int position) { 
        if(photoViewHolder.get(position) != null) {
            ImageHolder holder = (ImageHolder)photoViewHolder.get(position).getTag();
            // Do something...
        }
    } 
});

希望这可以帮助...

于 2013-03-07T13:48:54.440 回答
0

我从github问题页面使用了 kutothe 的实现。

于 2013-02-21T14:32:33.843 回答
0

我在简单地将 Uri 设置为ImageView使用时遇到了这个问题:iv.setImageURI(Uri.fromFile(imgFile)); 我在使用 Universal Image Loader 时遇到了同样的问题,我什至在外面寻找了其他的 Image Loader,并找到了另一个名为“ Picasso ”的好东西,但它也有同样的问题。

所以对我有用的是通过 XML 使用GestureImageView并设置gesture-image:recycle为 true,并使用以下代码加载图像:

            Drawable yourDrawable = null;

            try {
                InputStream inputStream = getActivity().getContentResolver().openInputStream(Uri.fromFile(img));
                yourDrawable = Drawable.createFromStream(inputStream, Uri.fromFile(img).toString() );
                inputStream.close();
            } catch (FileNotFoundException e) {
                yourDrawable = getResources().getDrawable(R.drawable.ic_launcher);
            } catch (IOException e) {
                e.printStackTrace();
            }

            if (yourDrawable != null)
                iv.setImageDrawable(yourDrawable);

它崩溃并给出OOM错误的原因是当图像不再显示在屏幕上时,位图不会被回收,因此会发生内存泄漏。

如果有另一种方法可以在 normal 中回收位图ImageView,那将是一个更好的解决方案。

希望我有所帮助。

于 2013-09-10T10:29:25.417 回答
-1

我知道已经晚了,但也许我的回答会节省一些人的时间。经过几个小时的尝试解决这个问题(几乎所有答案都在堆栈溢出中找到),我终于用 Fresco 图像库解决了这个问题。这是一个由 Facebook 编写的库,它的主要目标是有效地使用内存。这真的很棒,我的内存不足错误消失了。我强烈推荐使用它。

http://frescolib.org/

于 2016-01-21T18:35:01.687 回答