2017-06-24 35 views
0

向所有程序員致意。 雖然我在Visual Studio Xamarin中爲Android創建應用程序,但我發現內存有問題。在我的應用程序用戶需要選擇國家電話前綴。用於此的UI是帶有ListView的警報對話框。我用擴展適配器填充ListView。在AlertDialog中使用imageView和TextView的ListView泄漏內存並獲取Java.Lang.OutOfMemoryError

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。這裏的代碼,我創建適配器,並將其發送到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

你可能要考慮執行'IDisposable'模式的功能。對於像「位圖」這樣的大對象來說,這是特別需要的。你可以找到一個完整的指導,你需要考慮從內存中釋放哪些對象[這裏](https://developer.xamarin.com/guides/cross-platform/deployment,_testing,_and_metrics/memory_perf_best_practices/)。 – Demitrian

+0

對@Demitrian您建議的解決方案是有幫助的。謝謝。如果你同意,我會將它寫爲下面我的問題的答案。 –

+0

很高興知道它的工作,Adeptus – Demitrian

回答

0

如建議Demitrian我使用了IDisposable模式。所以我改變GetViewCountryListAdapter類這樣

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