c# - 内存泄漏: ListView with Bitmaps

标签 c# android android-listview memory-leaks xamarin

我有一个明显的内存泄漏问题,我需要一些帮助/建议来解决它。

我的场景有一个简单的 ListView,显示图像和标题。该图像是从服务器下载的位图图像。

在如此快速地上下滚动后,这个 ListView 使我的应用程序崩溃,如果我检查控制台,我会遇到这样的 OOM 异常:

[art] Clamp target GC heap from 111MB to 96MB
[art] Alloc concurrent mark sweep GC freed 3(96B) AllocSpace objects, 0(0B) LOS objects, 0% free, 95MB/96MB, paused 1.187ms total 38.840ms

为了避免 OOM,我实现了 LRUCache 和 DiskCache,用于将下载的位图存储到设备中,并获取此文件,而不是再次下载图像。

这是我的 ListView 适配器:

public class LazyLoadAdapter : BaseAdapter
{
Activity _activity;
List _products;
BitmapCache cache;
ImageView _imgView;
Dictionary<string, Task> pendingFetch = new Dictionary<string, Task> ();
Bitmap NoMapPicture;

    public LazyLoadAdapter(Activity activity, List<CouponExchange> products)
    {
        _activity = activity;
        _products = products;
        NoMapPicture = PrepareNoMapPicture (Resource.Drawable.default_coupon);
        this.cache = BitmapCache.CreateCache (activity, "MapCache");
    }

    public override int Count
    {
        get { return _products.Count; }
    }

    public override Java.Lang.Object GetItem(int position)
    {
        return position;
    }

    public override long GetItemId(int position)
    {
        return position;
    }

    public override Android.Views.View GetView(int position, Android.Views.View convertView, Android.Views.ViewGroup parent)
    {
        if (convertView == null)
        {
            convertView = _activity.LayoutInflater.Inflate(Resource.Layout.ShopItemList, parent, false);
        }

        CouponExchange product = _products[position];

        TextView txtProductName = convertView.FindViewById<TextView>(Resource.Id.textView24);
        txtProductName.Text = product.CouponTitle;


        TextView txtProductCost = convertView.FindViewById<TextView>(Resource.Id.textView24b);
        txtProductCost.Text = product.Cost.ToString();

        _imgView = convertView.FindViewById<ImageView>(Resource.Id.imgProduct);

        GetPersonPicture (product.CouponImageUrl);

        return convertView;
    }


    Bitmap DownloadoCacher (string url)
    {
        Bitmap map = null;
        using(map){
            map = cache.TryGet2 (url);
            if (map!=null)
            return map;

        byte[] bytes;
        using (var wc = new WebClient ()) {
             bytes = wc.DownloadData (url);
        };

        if (bytes != null && bytes.Length > 0) {
            BitmapFactory.Options options = new BitmapFactory.Options();
            options.InJustDecodeBounds = true;
            map = DecodeSampledBitmapFromResource (bytes, 400, 200);

        } else {
            return map;
        }
        cache.AddOrUpdate (url, map, TimeSpan.FromDays (1));
        return map;
        };
    }

    public static Bitmap DecodeSampledBitmapFromResource(byte[] bytes,
        int reqWidth, int reqHeight) {

        // First decode with inJustDecodeBounds=true to check dimensions
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.InJustDecodeBounds = true;
        BitmapFactory.DecodeByteArray(bytes, 0,  bytes.Length, options);

        // Calculate inSampleSize
        options.InSampleSize = CalculateInSampleSize(options, reqWidth, reqHeight);

        // Decode bitmap with inSampleSize set
        options.InJustDecodeBounds = false;

        return BitmapFactory.DecodeByteArray(bytes, 0,  bytes.Length, options);
    }

    public static int CalculateInSampleSize(
        BitmapFactory.Options options, int reqWidth, int reqHeight) {
        // Raw height and width of image
        int height = options.OutHeight;
           int width = options.OutWidth;
        int inSampleSize = 1;

        if (height > reqHeight || width > reqWidth) {

             int halfHeight = height / 2;
             int halfWidth = width / 2;

            // Calculate the largest inSampleSize value that is a power of 2 and keeps both
            // height and width larger than the requested height and width.
            while ((halfHeight / inSampleSize) > reqHeight
                && (halfWidth / inSampleSize) > reqWidth) {
                inSampleSize *= 2;
            }
        }

        return inSampleSize;
    }

    Bitmap PrepareNoMapPicture (int baseImage)
    {
        return BitmapFactory.DecodeResource (_activity.Resources, baseImage);
    }


    Bitmap GetPersonPicture (string url){
        if (_imgView == null)
            return null;
        Bitmap map = null;
        using (map) {

            map = cache.TryGet2 (url);

        if (map!=null) {
            _imgView.SetImageBitmap (map);
        } else {
            _imgView.SetImageBitmap (NoMapPicture);
            Action doMapSetting = () => {
                _activity.RunOnUiThread (() => {
                    if (map == null){
                        map = cache.TryGet2 (url);
                    }
                    _imgView.SetImageBitmap (map);
                });
            };
            if (pendingFetch.ContainsKey (url))
                pendingFetch [url].ContinueWith (t => doMapSetting (), TaskContinuationOptions.ExecuteSynchronously);
            else
                pendingFetch[url] = SerialScheduler.Factory.StartNew (() => {
                    map = DownloadoCacher (url);
                    doMapSetting ();
                });
        }

        return map;
        };
    }

下载图像后,我的缓存会从设备文件中获取图像。 如此快速地上下滚动后,cacheDisk 尝试从文件中获取图像并抛出 OOM 异常:

 try {
            bmp = Android.Graphics.BitmapFactory.DecodeFile (Path.Combine (basePath, key));
    } catch (Exception e) {
            var err = e.Message;
            return null;
    }

所有回复将不胜感激。谢谢你

最佳答案

我将这个 Picasso Binding 库用于 Xamarin: https://github.com/jacksierkstra/Picasso

这个强大的图像下载和缓存库可让您简化图像管理。

官方文档: http://square.github.io/picasso/

希望这有帮助

关于c# - 内存泄漏: ListView with Bitmaps,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30456218/

相关文章:

c# - WPF InitializeComponent 性能问题

C# 脚本 API 不加载程序集

android - 如何将android中的堆转储转换为eclipse格式

android - block 后代时关注 ListView 中的 EditText (Android)

android.R.color.transparent 不完全透明

c# - 将 JSON 反序列化为 C# 对象以在网格中将嵌套数组显示为字符串

java - 为什么只有使用反射才能使用此 Java 方法?

java - 是否有 MPI 的 android 实现?

android - 添加新列表后如何在android中更新 ListView

c# - MonoTouch 中的 ViewForZoomingInScrollView