欢迎所有程序员。 当我在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();
}
我真的不明白内存泄漏在哪里。所有帮助将不胜感激。
答案 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;
}