My code has a massive memory leak on orientation change. The heap is full after rotating the phone once or twice. I tried to track down the leak for two days now without success.
The application uses SDK 15 (min 14).
MainActivity
shows an ActionBar with list navigation (NAVIGATION_MODE_LIST
). Navigating the spinner replaces the fragment shown.
The code works.
ListNavigationItem
which contains the information needed to instantiate Fragments:
public class ListNavigationItem
{
public final String fragmentClass;
public final String title; // Used as fragment tag and as title
public ListNavigationItem(String fragmentClass, String title)
{
this.fragmentClass = fragmentClass;
this.title = title;
}
@Override
public String toString()
{
return title;
}
}
And finally my (simplified) MainActivity
:
public class MainActivity extends Activity implements OnNavigationListener
{
private MyApplication app;
private ArrayAdapter<ListNavigationItem> adapter;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
app = (MyApplication) getApplicationContext();
ActionBar actionBar = getActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
actionBar.setDisplayShowTitleEnabled(false);
actionBar.setDisplayShowHomeEnabled(false);
adapter = new ArrayAdapter<ListNavigationItem>(this, android.R.layout.simple_spinner_dropdown_item);
adapter.add(new ListNavigationItem(FirstFragment.class.getName(), "First"));
adapter.add(new ListNavigationItem(SecondFragment.class.getName(), "Second"));
actionBar.setListNavigationCallbacks(adapter, this);
}
public boolean onNavigationItemSelected(int itemPosition, long itemId)
{
ListNavigationItem item = adapter.getItem(itemPosition);
if (item == null)
return false;
Fragment f = getFragmentManager().findFragmentByTag(item.title);
if (f == null)
f = Fragment.instantiate(this, item.fragmentClass);
else if (f.isAdded())
return true;
FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
fragmentTransaction.replace(R.id.main_content, f, item.title);
fragmentTransaction.commit();
return true;
}
@Override
protected void onDestroy()
{
adapter = null; // Do I need this?
app = null;
super.onDestroy();
}
}
Manifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="at.company.app"
android:versionCode="1"
android:versionName="1.0">
<uses-sdk android:minSdkVersion="14" />
<uses-sdk android:targetSdkVersion="15" />
<application
android:name=".Application"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name">
<activity
android:name=".MainActivity"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
A MAT heap analysis says that the class android.content.res.Resources
occupies 57% of the available memory, and android.graphics.Bitmap
occupies 12%. My fragments, however, contain just a simple TextView
.
The MAT histogram shows that there are 4 Objects (Instances, I assume?) of MainActivity
. Also, logcat shows that the heap is frequently being emptied after orientation change (GC_CONCURRENT).
The official Dev Guide discourages from using android:configChanges="orientation"
. (Source)
I am relatively new to Java and the Android platform, and I would appreciate any help.