Designing a game is different from designing a consumer application in a very basic way. Consumer applications tend to be event-driven with long periods of inactivity while waiting for input, while games tend to be constantly redrawing and recalculating in real time. The Android framework is designed to allow for easy creation of event-driven applications.
This leaves the game developer with an interesting quandary. To make a game, one must simulate a loop within an event driven environment. Many developers stuff their game logic inside of onDraw() and then invalidate themselves as quickly as possible. This accomplishes the job, but it prevents them from invalidating only the areas of the screen that need to be repainted. For games where much of the screen does not change often, invalidating only changed areas of the screen can provide a large performance increase.
Luckily, the Android framework proves a way to hook into the message loop which drives the application. The Handler class provides the ability for an application to send itself custom messages. This means that the game developer can separate game logic from rendering code for a cleaner structure.
When we first implemented a game in this fashion, it would continue to use 100% CPU after it had finished. I was confused when I verified that the game was receiving the onDestroy event, which indicates that the application's resources are about to be released.
It turns out that in order to speed up subsequent launches of an application, Android caches processes from terminated activities. When cached, the process' message loop can still receive messages, presumably so that the OS can wake it up when the user chooses to launch that application again. Since we were sending a new custom event every time we received one, the process would continue to receive and act on our custom messages after the activity had been destroyed.
A simple change that prevented the application from sending new messages after it had received a termination event fixed the issue.