内存泄漏:使用位图的ListView

本文关键字:位图 ListView 泄漏 内存 | 更新日期: 2023-09-27 17:50:52

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

我的场景有一个简单的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 Exception:

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

感谢所有的回复。谢谢你

内存泄漏:使用位图的ListView

我为Xamarin使用了这个Picasso Binding库:https://github.com/jacksierkstra/Picasso

这个功能强大的图像下载和缓存库允许您简化您的图像管理。

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

希望能有所帮助