Skip to content

Commit

Permalink
Android: Fixed more memory leaks related to bitmaps. There might be o…
Browse files Browse the repository at this point in the history
…ther leaks but the new code seems to release most of the memory. Once again, I had to reimplement the way the ViewPager in the MainActivity handles the MobileLibraryBrowser fragments. This time the fragments are never actually replaced; the fragment is refreshed through the presenter instead. This fixes also the empty fragments often shown when changing fragments (clicking on an item or using the Back button). The ViewPager is one of the most buggiest control I've ever seen. Android never ceases to amaze me, every time! The iOS version will keep spawning new views as needed.

Related to issue #406.
  • Loading branch information
ycastonguay committed Jul 24, 2013
1 parent 591c564 commit 4fd8d4a
Show file tree
Hide file tree
Showing 10 changed files with 245 additions and 125 deletions.
6 changes: 3 additions & 3 deletions MPfm/MPfm.Android/Classes/Activities/MainActivity.cs
Expand Up @@ -279,11 +279,11 @@ protected override void OnDestroy()

public override void OnBackPressed()
{
// Check if the history has another tab
if (_navigationManager.CanRemoveMobileLibraryBrowserFragmentFromBackstack(_tabPagerAdapter.GetCurrentTab()))
var tabType = _tabPagerAdapter.GetCurrentTab();
if (_navigationManager.CanGoBackInMobileLibraryBrowserBackstack(tabType))
{
Console.WriteLine("MainActivity - OnBackPressed - CanRemoveFragment");
_navigationManager.RecreateMobileLibraryBrowserFragment(_tabPagerAdapter.GetCurrentTab());
_navigationManager.PopMobileLibraryBrowserBackstack(tabType);
}
else
{
Expand Down
35 changes: 33 additions & 2 deletions MPfm/MPfm.Android/Classes/Adapters/MainTabStatePagerAdapter.cs
Expand Up @@ -30,6 +30,7 @@ public class MainTabStatePagerAdapter : FragmentStatePagerAdapter, ActionBar.ITa
{
readonly List<Tuple<MobileNavigationTabType, Fragment>> _fragments;
readonly ViewPager _viewPager;
readonly FragmentManager _fragmentManager;

public MainTabStatePagerAdapter(IntPtr javaReference, JniHandleOwnership transfer)
: base(javaReference, transfer)
Expand All @@ -39,19 +40,29 @@ public MainTabStatePagerAdapter(IntPtr javaReference, JniHandleOwnership transfe
public MainTabStatePagerAdapter(FragmentManager fm, ViewPager viewPager)
: base(fm)
{
_fragmentManager = fm;
_fragments = new List<Tuple<MobileNavigationTabType, Fragment>>();
_viewPager = viewPager;
}

public void SetFragment(MobileNavigationTabType tabType, Fragment fragment)
{
//var transaction = _fragmentManager.BeginTransaction();
int index = _fragments.FindIndex(x => x.Item1 == tabType);
if (index == -1)
{
// New fragment
_fragments.Add(new Tuple<MobileNavigationTabType, Fragment>(tabType, fragment));
//transaction.Add(_viewPager.Id, fragment);
}
index = _fragments.FindIndex(x => x.Item1 == tabType);
_fragments[index] = new Tuple<MobileNavigationTabType, Fragment>(tabType, fragment);
else
{
// Replace fragment
//transaction.Remove(_fragments[index].Item2);
_fragments[index] = new Tuple<MobileNavigationTabType, Fragment>(tabType, fragment);
//transaction.Add(_viewPager.Id, fragment);
}

NotifyDataSetChanged();
}

Expand Down Expand Up @@ -102,6 +113,26 @@ public override int Count
}
}

//public override Java.Lang.Object InstantiateItem(global::Android.Views.View view, int position)
//{
// return base.InstantiateItem(view, position);
//}

//public override Java.Lang.Object InstantiateItem(global::Android.Views.ViewGroup container, int position)
//{
// return base.InstantiateItem(container, position);
//}

//public override void DestroyItem(global::Android.Views.View view, int position, Java.Lang.Object obj)
//{
// base.DestroyItem(view, position, obj);
//}

//public override void DestroyItem(global::Android.Views.ViewGroup container, int position, Java.Lang.Object obj)
//{
// base.DestroyItem(container, position, obj);
//}

public void OnPageScrollStateChanged(int p0)
{
}
Expand Down
Expand Up @@ -30,10 +30,10 @@

namespace MPfm.Android.Classes.Adapters
{
public class MobileLibraryBrowserGridAdapter : BaseAdapter<LibraryBrowserEntity>
public class MobileLibraryBrowserGridAdapter : BaseAdapter<LibraryBrowserEntity>, AbsListView.IRecyclerListener
{
readonly Activity _context;
MobileLibraryBrowserFragment _fragment;
readonly MobileLibraryBrowserFragment _fragment;
GridView _gridView;
List<LibraryBrowserEntity> _items;

Expand All @@ -43,6 +43,7 @@ public MobileLibraryBrowserGridAdapter(Activity context, MobileLibraryBrowserFra
_fragment = fragment;
_gridView = gridView;
_items = items;
gridView.SetRecyclerListener(this);
}

public void SetData(IEnumerable<LibraryBrowserEntity> items)
Expand All @@ -69,7 +70,7 @@ public override int Count
public override View GetView(int position, View convertView, ViewGroup parent)
{
//Console.WriteLine(">>>>>>>>> MobileLibraryBrowserGridAdapter - GetView - position: {0}", position);
var mainActivity = (MainActivity)_context;
//var mainActivity = (MainActivity)_context;
var item = _items[position];
string bitmapKey = item.Query.ArtistName + "_" + item.Query.AlbumTitle;
View view = convertView;
Expand All @@ -89,40 +90,42 @@ public override View GetView(int position, View convertView, ViewGroup parent)
return view;
}

if (imageView.Drawable != null)
{
// Check if the bitmap is still in memory cache
string currentBitmapKey = imageView.Tag != null ? imageView.Tag.ToString() : "";
if (!mainActivity.BitmapCache.KeyExists(bitmapKey))
{
Console.WriteLine("##############>> MobileLibraryBrowserGridAdapter - OH NO!!! Bitmap key {0} isn't in cache", currentBitmapKey);
// Recycle the bitmap ONLY when the bitmap has been cleared from the memory cache, or the application will crash!
imageView.Drawable.Callback = null;
Bitmap bitmap = ((BitmapDrawable)imageView.Drawable).Bitmap;
if (bitmap != null)
{
Console.WriteLine("##############>> MobileLibraryBrowserGridAdapter - Recycling bitmap key {0}", currentBitmapKey);
//bitmap.Recycle();
bitmap.Dispose();
bitmap = null;
}
}
else
{
Console.WriteLine("##############>> MobileLibraryBrowserGridAdapter - Bitmap key {0} *IS IN CACHE*", currentBitmapKey);
}
}
//if (imageView.Drawable != null)
//{
// // Check if the bitmap is still in memory cache
// string currentBitmapKey = imageView.Tag != null ? imageView.Tag.ToString() : "";
// if (!mainActivity.BitmapCache.KeyExists(bitmapKey))
// {
// Console.WriteLine("##############>> MobileLibraryBrowserGridAdapter - OH NO!!! Bitmap key {0} isn't in cache", currentBitmapKey);
// // Recycle the bitmap ONLY when the bitmap has been cleared from the memory cache, or the application will crash!
// imageView.Drawable.Callback = null;
// Bitmap bitmap = ((BitmapDrawable)imageView.Drawable).Bitmap;
// if (bitmap != null)
// {
// Console.WriteLine("##############>> MobileLibraryBrowserGridAdapter - Recycling bitmap key {0}", currentBitmapKey);
// //bitmap.Recycle();
// bitmap.Dispose();
// bitmap = null;
// }
// }
// else
// {
// Console.WriteLine("##############>> MobileLibraryBrowserGridAdapter - Bitmap key {0} *IS IN CACHE*", currentBitmapKey);
// }
//}
imageView.SetImageBitmap(null);


// Check if bitmap is in cache before requesting album art (Android likes to request GetView extremely often for no good reason)
//Console.WriteLine(">>>>>>>>> MobileLibraryBrowserGridAdapter - Loading album art - position: {0} artistName: {1} albumTitle: {2}", position, _items[position].Query.ArtistName, _items[position].Query.AlbumTitle);
if(mainActivity.BitmapCache.KeyExists(bitmapKey))
//if(mainActivity.BitmapCache.KeyExists(bitmapKey))
if(_fragment.BitmapCache.KeyExists(bitmapKey))
{
//Console.WriteLine(">>>>>>>>> MobileLibraryBrowserGridAdapter - Getting album art from cache - position: {0} artistName: {1} albumTitle: {2}", position, _items[position].Query.ArtistName, _items[position].Query.AlbumTitle);
Task.Factory.StartNew(() => {
imageView.Tag = bitmapKey;
imageView.SetImageBitmap(mainActivity.BitmapCache.GetBitmapFromMemoryCache(bitmapKey));
//imageView.SetImageBitmap(mainActivity.BitmapCache.GetBitmapFromMemoryCache(bitmapKey));
imageView.SetImageBitmap(_fragment.BitmapCache.GetBitmapFromMemoryCache(bitmapKey));
});
}
else
Expand All @@ -140,7 +143,7 @@ public void RefreshAlbumArtCell(string artistName, string albumTitle, byte[] alb
{
try
{
var mainActivity = (MainActivity)_context;
//var mainActivity = (MainActivity)_context;
int index = _items.FindIndex(x => x.Query.ArtistName == artistName && x.Query.AlbumTitle == albumTitle);
//Console.WriteLine(">>>>>>>>>MobileLibraryBrowserGridAdapter - *RECEIVED* album art for {0}/{1} - index: {2}", artistName, albumTitle, index);
if (index >= 0)
Expand All @@ -155,7 +158,8 @@ public void RefreshAlbumArtCell(string artistName, string albumTitle, byte[] alb
//Console.WriteLine(">>>>>>>>>MobileLibraryBrowserGridAdapter - *LOADING BITMAP* from byte array for {0}/{1} - Index found: {2}", artistName, albumTitle, index);
var image = view.FindViewById<ImageView>(Resource.Id.albumCell_image);
image.Tag = artistName + "_" + albumTitle;
mainActivity.BitmapCache.LoadBitmapFromByteArray(albumArtData, artistName + "_" + albumTitle, image);
//mainActivity.BitmapCache.LoadBitmapFromByteArray(albumArtData, artistName + "_" + albumTitle, image);
_fragment.BitmapCache.LoadBitmapFromByteArray(albumArtData, artistName + "_" + albumTitle, image);
});
}
else
Expand All @@ -169,5 +173,11 @@ public void RefreshAlbumArtCell(string artistName, string albumTitle, byte[] alb
Console.WriteLine("MobileLibraryBrowserGridAdapter - Failed to load album art: {0}", ex);
}
}

public void OnMovedToScrapHeap(View view)
{
//ImageView imageView = view.FindViewById<ImageView>(Resource.Id.albumCell_image);
//Console.WriteLine(">>>>%%%%%%%%%$$$$$$$$$###########^^^^^^^^^^:: OnMovedtoScrapHeap view.Tag: {0} view type: {1} imageView.Tag: {2}", view.Tag != null ? view.Tag.ToString() : "null", view.GetType().FullName, imageView != null && imageView.Tag != null ? imageView.Tag : "null");
}
}
}
41 changes: 21 additions & 20 deletions MPfm/MPfm.Android/Classes/Cache/BitmapCache.cs
Expand Up @@ -83,31 +83,32 @@ public bool KeyExists(string key)

public void LoadBitmapFromByteArray(byte[] bytes, string key, ImageView imageView)
{
Console.WriteLine("!!!!!!!!!!!!!!!!!!!!!!!!!!!!! BitmapCache - LoadBitmapFromByteArray - key: {0} size: {1} maxSize: {2}", key, memoryCache.Size(), memoryCache.MaxSize());
Bitmap bitmap = GetBitmapFromMemoryCache(key);
if (bitmap != null)
lock (memoryCache)
{
Console.WriteLine("BitmapCache - LoadBitmapFromByteArray - Loaded bitmap from cache! key: {0}", key);
imageView.SetImageBitmap(bitmap);
}
else
{
Console.WriteLine("BitmapCache - LoadBitmapFromByteArray - Decoding album art and adding to cache... key: {0}", key);
Task.Factory.StartNew(() => {
bitmap = BitmapHelper.DecodeFromByteArray(bytes, MaxWidth, MaxHeight);
AddBitmapToMemoryCache(key, bitmap);
activity.RunOnUiThread(() => {
Console.WriteLine("BitmapCache - Setting album art on image view... key: {0}", key);
imageView.SetImageBitmap(bitmap);
Animation animation = AnimationUtils.LoadAnimation(activity, Resource.Animation.fade_in);
imageView.StartAnimation(animation);
//Console.WriteLine("!!!!!!!!!!!!!!!!!!!!!!!!!!!!! BitmapCache - LoadBitmapFromByteArray - key: {0} size: {1} maxSize: {2}", key, memoryCache.Size(), memoryCache.MaxSize());
Bitmap bitmap = GetBitmapFromMemoryCache(key);
if (bitmap != null)
{
//Console.WriteLine("BitmapCache - LoadBitmapFromByteArray - Loaded bitmap from cache! key: {0} size: {1} maxSize: {2}", key, memoryCache.Size(), memoryCache.MaxSize());
imageView.SetImageBitmap(bitmap);
}
else
{
//Console.WriteLine("BitmapCache - LoadBitmapFromByteArray - Decoding album art and adding to cache... key: {0} size: {1} maxSize: {2}", key, memoryCache.Size(), memoryCache.MaxSize());
Task.Factory.StartNew(() => {
bitmap = BitmapHelper.DecodeFromByteArray(bytes, MaxWidth, MaxHeight);
AddBitmapToMemoryCache(key, bitmap);
activity.RunOnUiThread(() => {
//Console.WriteLine("BitmapCache - Setting album art on image view... key: {0} size: {1} maxSize: {2}", key, memoryCache.Size(), memoryCache.MaxSize());
imageView.SetImageBitmap(bitmap);
Animation animation = AnimationUtils.LoadAnimation(activity, Resource.Animation.fade_in);
imageView.StartAnimation(animation);
});
});
});
}
}
}



//public void LoadBitmapFromResource(int resId, ImageView imageView)
//{
// string imageKey = resId.ToString();
Expand Down

0 comments on commit 4fd8d4a

Please sign in to comment.