AlertDialog 中带有 imageView 和 TextView 的 ListView 泄漏内存并获取 Java.Lang.OutOfMemoryError
ListView with imageView and TextView in AlertDialog leeaks memory and get Java.Lang.OutOfMemoryError
各位程序员大家好。
当我在 Visual Studio Xamarin 中为 Android 创建应用程序时,我发现内存有问题。在我的应用程序中,用户需要 select 国家 phone 前缀。 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;
}
}
这个 Adapter 我在 Main 中使用 Activity 来填充 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();
}
我真的不明白内存泄漏在哪里。我们将不胜感激。
根据建议 Demitrian 我使用了 IDisposable 模式。所以我改变 GetView CountryListAdapter class 的函数
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;
}
各位程序员大家好。 当我在 Visual Studio Xamarin 中为 Android 创建应用程序时,我发现内存有问题。在我的应用程序中,用户需要 select 国家 phone 前缀。 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;
}
}
这个 Adapter 我在 Main 中使用 Activity 来填充 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();
}
我真的不明白内存泄漏在哪里。我们将不胜感激。
根据建议 Demitrian 我使用了 IDisposable 模式。所以我改变 GetView CountryListAdapter class 的函数
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;
}