在AlertDialog中使用imageView和TextView的ListView泄露内存并获取Java.Lang.OutOfMemoryError

时间:2017-06-24 10:28:29

标签: android listview xamarin xamarin.android out-of-memory

欢迎所有程序员。 当我在Visual Studio Xamarin中为Android创建应用程序时,我发现内存有问题。在我的应用中,用户需要选择国家/地区电话前缀。用户界面的UI是带ListView的Alert Dialog。我用扩展适配器填充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;
        }
    }

我在主要活动中使用的此适配器要填充ListView。如果我用这个ListView打开AlertDialog一切正常。如果我第二次打开相同的AlertDialog,则抛出Java.Lang.OutOfMemoryError。这里是代码,我在其中创建Adapter并将其发送到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();
        }

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

1 个答案:

答案 0 :(得分:0)

建议 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;
}