Have you ever had a mysterious memory leak on Android when your code seemed clean as a whistle? I sure have, and I finally figured out why: ViewFlipper.
It turns out that when you call ViewFlipper.startFlipping(), it sets up a loop via messages which flips between its child Views. This is all well and good, but there's no code which detects that the ViewFlipper itself is no longer being displayed and should be canned. So the ViewFlipper keeps a hold on itself and its children, who inevitably keep a hold on their Context, and bam - gigantic memory leak. Depending on how much memory your Activity takes up, all you need to do is start changing the orientation of the ViewFlipper's containing Activity a few times and you could crash your application.
The solution to this is relatively simple: just call ViewFlipper.stopFlipping() in your Activity's onPause(), and then have it start flipping again in onResume().
There is, however, one small catch. Since ViewFlipper uses delayed messages, it's possible that a user can pause your activity then resume within the amount of time between flips (if they hit power then menu in quick succession, for example). The way ViewFlipper is setup, this means you can end up with multiple flip messages being passed around, resulting in faster flipping than intended. Unfortunately, the only solution I can think of to this is a real hassle (you would have to intentionally delay some time before resuming flipping in onResume()), so I'm just ignoring it for now.
"So the ViewFlipper keeps a hold on itself and its children".
ReplyDeleteI don't quite understand this sentence. If an Activity is destroyed, there will be no reference to the ViewFlipper object, so the ViewFlipper object in the Activity will turn garbage collectible. How can ViewFlipper keeps a hold on itself?
It keeps a hold on itself by using a handler to message itself at a later date.
ReplyDelete