AlertDialog 中带有 imageView 和 TextView 的 ListView 泄漏内存并获取 Java.Lang.OutOfMemoryError

获得机械师

各位程序员大家好。当我在 Visual Studio Xamarin 中为 Android 创建应用程序时,我发现内存有问题。在我的应用程序中,用户需要选择一个国家电话前缀。此 UI 是带有 ListView 的警报对话框。我用扩展适配器填充 ListView。

internal class CountryListViewHolderClass : Java.Lang.Object
    {
        internal Action viewClicked { get; set; }
        internal TextView countryCodeView;
        internal ImageView countryImageView;
        internal ImageView countryCheckView;
        public void initialize(View view)
        {
            view.Click += delegate
            {
                viewClicked();
            };
        }
    }
    public class CountryInfo
    {
        public string _flag { get; private set; }
        public string _code { get; private set; }
        public string _prefix { get; private set; }
        public CountryInfo(string flag, string code, string prefix)
        {
            _flag = flag;
            _code = code;
            _prefix = prefix;
        }
    }
    public class CountryListAdapter : BaseAdapter<string>
    {
        Activity _context;
        string[] _countryCode;
        string[] _countryPrefix;
        string[] _countryFlag;
        internal event Action<CountryInfo> actionCountrySelected;
        public CountryListAdapter(Activity context, string[] countryCode, string[] countryFlag, string[] countryPrefix)
        {
            _context = context;
            _countryCode = countryCode;
            _countryFlag = countryFlag;
            _countryPrefix = countryPrefix;
        }
        public override View GetView(int position, View convertView, ViewGroup parent)
        {
            CountryListViewHolderClass countryListViewHolderClass;
            View view;
            view = convertView;
            Android.Graphics.Color viewBg;
            if (view == null)
            {
                view = _context.LayoutInflater.Inflate(Resource.Layout.CountryListItem, parent, false);
                countryListViewHolderClass = new CountryListViewHolderClass();
                countryListViewHolderClass.countryCodeView = view.FindViewById<TextView>(Resource.Id.countryCodeText);
                countryListViewHolderClass.countryImageView = view.FindViewById<ImageView>(Resource.Id.countryFlagImg);
                countryListViewHolderClass.countryCheckView = view.FindViewById<ImageView>(Resource.Id.countryCheckImage);
                countryListViewHolderClass.initialize(view);
                view.Tag = countryListViewHolderClass;   
            }
            else
            {
                countryListViewHolderClass = (CountryListViewHolderClass)view.Tag;
            }

            countryListViewHolderClass.countryCodeView.Text = "(+" + _countryPrefix[position] + ") " + _countryCode[position];
            Stream flag = _context.Resources.Assets.Open("cflags/" + _countryFlag[position] + ".png");
                countryListViewHolderClass.countryImageView.SetImageDrawable(Drawable.CreateFromStream(flag, _countryFlag[position]));
                flag.Dispose();
                flag.Close();
            }
            countryListViewHolderClass.viewClicked = () =>
            {
                if (actionCountrySelected != null)
                {
                    for (int i=0; i < parent.ChildCount; i++)
                    {
                        View otherView = parent.GetChildAt(i);
                        ImageView checkIcon = otherView.FindViewById<ImageView>(Resource.Id.countryCheckImage);
                        checkIcon.Alpha = 0;
                    }
                    countryListViewHolderClass.countryCheckView.Alpha = 1;
                    CountryInfo result = new CountryInfo(_countryFlag[position], _countryCode[position], _countryPrefix[position]);
                    actionCountrySelected(result);
                }
            };
            return view;
        }
    }

我在 Main Activity 中使用的这个 Adapter 来填充 ListView。如果我用这个 ListView 打开 AlertDialog 一切正常。如果我第二次打开同一个 AlertDialog,这会抛出 Java.Lang.OutOfMemoryError。这是我创建适配器并将其发送到 ListView 的代码

void BindCountryList()
        {
            string[] countryCodes = Resources.GetStringArray(Resource.Array.countries_names);
            string[] countryPrefixes = Resources.GetStringArray(Resource.Array.countries_iso_prefixes);
            string[] countryFlags = Resources.GetStringArray(Resource.Array.countries_iso_codes);
            if (countryListAdapter != null)
            {
                countryListAdapter.actionCountrySelected -= CountrySelected;
                countryListAdapter = null;
            }
            countryListAdapter = new CountryListAdapter(this, countryCodes, countryFlags, countryPrefixes);
            countryListAdapter.actionCountrySelected += CountrySelected;
            Stream flag = Resources.Assets.Open("cflags/tr.png");
            countryCodeSelectButton.SetImageDrawable(Drawable.CreateFromStream(flag, "tr.png"));
            flag.Dispose();
            flag.Close();
            phoneCountryPrefix.Text = "+90";
        }
void CountrySelected(CountryInfo countryInfo)
        {
            phoneCountryPrefix.Text = "+"+countryInfo._prefix;
            phonePrefix = countryInfo._prefix;
            countrySelectAlert.Cancel();
            Stream flag = Resources.Assets.Open("cflags/" + countryInfo._flag + ".png");
            countryCodeSelectButton.SetImageDrawable(Drawable.CreateFromStream(flag, countryInfo._flag));
            flag.Close();
            string toast = string.Format("The prefix is {0}", countryInfo._code);
            Toast.MakeText(this, toast, ToastLength.Long).Show();
        }

我真的不明白内存泄漏在哪里。所有帮助将不胜感激。

获得机械师

正如Demitrian建议的那样,我使用了 IDisposable 模式。所以我这种方式更改CountryListAdapter类的GetView函数

public override View GetView(int position, View convertView, ViewGroup parent)
{
    CountryListViewHolderClass countryListViewHolderClass;
    View view;
    view = convertView;
    if (view == null)
    {
        view = _context.LayoutInflater.Inflate(Resource.Layout.CountryListItem, parent, false);
        countryListViewHolderClass = new CountryListViewHolderClass();
        countryListViewHolderClass.countryCodeView = view.FindViewById<TextView>(Resource.Id.countryCodeText);
        countryListViewHolderClass.countryImageView = view.FindViewById<ImageView>(Resource.Id.countryFlagImg);
        countryListViewHolderClass.countryCheckView = view.FindViewById<ImageView>(Resource.Id.countryCheckImage);
        countryListViewHolderClass.initialize(view);
        view.Tag = countryListViewHolderClass;   
    }
    else
    {
        countryListViewHolderClass = (CountryListViewHolderClass)view.Tag;
    }

    countryListViewHolderClass.countryCodeView.Text = "(+" + _countryPrefix[position] + ") " + _countryCode[position];
    StreamReader reader=null;
    try
    {

        reader = new StreamReader(_context.Resources.Assets.Open("cflags/" + _countryFlag[position] + ".png"));
        countryListViewHolderClass.countryImageView.SetImageDrawable(Drawable.CreateFromStream(reader.BaseStream, _countryFlag[position]));
    }
    finally
    {
        if (reader != null)
        {
            reader.Dispose();
        }
    }
    countryListViewHolderClass.viewClicked = () =>
    {
        if (actionCountrySelected != null)
        {
            for (int i=0; i < parent.ChildCount; i++)
            {
                View otherView = parent.GetChildAt(i);
                ImageView checkIcon = otherView.FindViewById<ImageView>(Resource.Id.countryCheckImage);
                checkIcon.Alpha = 0;
            }
            countryListViewHolderClass.countryCheckView.Alpha = 1;
            CountryInfo result = new CountryInfo(_countryFlag[position], _countryCode[position], _countryPrefix[position]);
            actionCountrySelected(result);
        }
    };
    return view;
}

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章