I am using a TabActivity in which every tab is a view.
I wanted to create animations of sliding when changing the tabs.
Every time the animation starts the logcat gives a GC entry:
08-17 16:37:00.840: D/dalvikvm(2767): GC_CONCURRENT freed 1920K, 43% free 6852K/11975K, paused 12ms+14ms, total 72ms
08-17 16:37:01.235: D/dalvikvm(2767): GC_CONCURRENT freed 1480K, 40% free 7215K/11975K, paused 2ms+3ms, total 46ms
08-17 16:37:01.235: D/dalvikvm(2767): WAIT_FOR_CONCURRENT_GC blocked 18ms
08-17 16:37:05.715: D/dalvikvm(2767): GC_CONCURRENT freed 2092K, 43% free 6882K/11975K, paused 12ms+28ms, total 104ms
08-17 16:37:06.370: D/dalvikvm(2767): GC_CONCURRENT freed 779K, 34% free 7912K/11975K, paused 14ms+6ms, total 65ms
08-17 16:37:09.825: D/dalvikvm(2767): GC_FOR_ALLOC freed 1464K, 47% free 6455K/11975K, paused 19ms, total 19ms
08-17 16:37:09.865: D/dalvikvm(2767): GC_FOR_ALLOC freed 1K, 34% free 7988K/11975K, paused 19ms, total 19ms
08-17 16:37:09.865: I/dalvikvm-heap(2767): Grow heap (frag case) to 16.811MB for 944656-byte allocation
08-17 16:37:10.370: D/dalvikvm(2767): GC_CONCURRENT freed 1427K, 38% free 8706K/13959K, paused 2ms+5ms, total 41ms
Every time you click on a tab, there are two animations: one for current tab slide out and one for the new tab to slide in.
These are the Animation definitions:
public Animation inFromRightAnimation() {
Animation inFromRight = new TranslateAnimation(
Animation.RELATIVE_TO_PARENT, +1.0f,
Animation.RELATIVE_TO_PARENT, 0.0f,
Animation.RELATIVE_TO_PARENT, 0.0f,
Animation.RELATIVE_TO_PARENT, 0.0f);
inFromRight.setDuration(500);
return inFromRight;
}
public Animation outToLeftAnimation() {
Animation outtoLeft = new TranslateAnimation(
Animation.RELATIVE_TO_PARENT, 0.0f,
Animation.RELATIVE_TO_PARENT, -1.0f,
Animation.RELATIVE_TO_PARENT, 0.0f,
Animation.RELATIVE_TO_PARENT, 0.0f);
outtoLeft.setDuration(500);
return outtoLeft;
}
EDIT: this is the code of the tab changing: ( currentTab is a private int class member)
public void onTabChanged(String tabId) {
if (!initializatorFinished) {
getTabHost().setCurrentTab(currentTab);
return;
}
tab1 = (LinearLayout) findViewById(R.id.tabHome);
tab2 = (LinearLayout) findViewById(R.id.tabChanges);
tab3 = (LinearLayout) findViewById(R.id.tabTests);
tab4 = (ScrollView) findViewById(R.id.tabSettings);
tab5 = (LinearLayout) findViewById(R.id.tabInfo);
int currentTab = GetCurrentTab();
int newTab = Integer.parseInt(tabId);
if (currentTab == 1 || currentTab == 0) {
if (newTab == 2) {
tab1.setAnimation(outToLeftAnimation());
tab2.setAnimation(inFromRightAnimation());
}
else if (newTab == 4) {
tab1.setAnimation(outToLeftAnimation());
tab4.setAnimation(inFromRightAnimation());
}
else if (newTab == 5) {
tab1.setAnimation(outToLeftAnimation());
tab5.setAnimation(inFromRightAnimation());
}
}
else if (currentTab == 2) {
if (newTab == 1) {
tab2.setAnimation(outToLeftAnimation());
tab1.setAnimation(inFromRightAnimation());
}
else if (newTab == 4) {
tab2.setAnimation(outToLeftAnimation());
tab4.setAnimation(inFromRightAnimation());
}
else if (newTab == 5) {
tab2.setAnimation(outToLeftAnimation());
tab5.setAnimation(inFromRightAnimation());
}
}
else if (currentTab == 4) {
if (newTab == 1) {
tab4.setAnimation(outToLeftAnimation());
tab1.setAnimation(inFromRightAnimation());
}
else if (newTab == 2) {
tab4.setAnimation(outToLeftAnimation());
tab2.setAnimation(inFromRightAnimation());
}
else if (newTab == 5) {
tab4.setAnimation(outToLeftAnimation());
tab5.setAnimation(inFromRightAnimation());
}
}
else if (currentTab == 5) {
if (newTab == 1) {
tab5.setAnimation(outToLeftAnimation());
tab1.setAnimation(inFromRightAnimation());
}
else if (newTab == 2) {
tab5.setAnimation(outToLeftAnimation());
tab2.setAnimation(inFromRightAnimation());
}
else if (newTab == 4) {
tab5.setAnimation(outToLeftAnimation());
tab4.setAnimation(inFromRightAnimation());
}
}
else if (currentTab == 3) {
if (newTab == 1) {
tab3.setAnimation(outToLeftAnimation());
tab1.setAnimation(inFromRightAnimation());
}
else if (newTab == 2) {
tab3.setAnimation(outToLeftAnimation());
tab2.setAnimation(inFromRightAnimation());
}
else if (newTab == 4) {
tab3.setAnimation(outToLeftAnimation());
tab4.setAnimation(inFromRightAnimation());
}
else if (newTab == 5) {
tab3.setAnimation(outToLeftAnimation());
tab5.setAnimation(inFromRightAnimation());
}
}
SetCurrentTab(Integer.parseInt(tabId));
}
private void SetCurrentTab(int tab) {
this.currentTab = tab;
}
private int GetCurrentTab() {
return this.currentTab;
}
If you are trying to avoid excess allocations you could create these animations once in onCreate and then simply reference them later instead of creating a new animation every time.
Something like this:
Beyond this, if you are still getting GC when you call the animation it is beyond your control. Somewhere the android framework is allocating objects that are getting destroyed as part of the animation process.
GC is normal, necessary and expected to happen but it is good that you are keeping an eye on things. Most often, excessive GC is due to something stupid happening outside the framework like allocating new objects in a loop unnecessarily. But the framework needs GC too and in this case that seems to be what’s happening.