I recently tracked down a thread leak in Net Ping.
It was the ScheduledThreadPoolExecutor.
To execute pings, the app uses these thread resources: AsyncTask, Process, and a ScheduledThreadPoolExecutor.
The AsyncTask is used to run the ping on a non-UI thread. Pinging can often take too long to complete on a UI thread, and anything that blocks the UI thread for five seconds will display the ANR (Application Not Responsive) error to the user.
The Process is how the ping is executed. Since Android restricts the network driver's capabilities to prevent normal, non-rooted apps from opening a socket for pinging, we need to use the native OS's ping command, which has the sticky bit set. The OS's ping command runs as the administrator, and is granted unrestricted access to the network device.
The ScheduledThreadPoolExecutor is used to time the responses coming back from the ping execution. Sometimes, ping may block for a very long time, particularly if there are intermittent network errors. The thread executor is used to spawn Runnables that will kill a hung, unresponsive ping process.
In the case of Net Ping, the AsyncTask starts up to five threads, and resuses those threads on subsequent ping operations. No leaks observed with AsyncTask.
The Process doesn't actually start a new thread in the context of the app. When using a Process object, it's important to call destroy() on it to ensure that the external process is terminated, and any related streams are closed.
Finally, we get to where the leak occurred: after the ScheduledThreadPoolExecutor object no longer had any references to it, it would not terminate the threads that were idle and waiting for work that could never possibly come. The fix was to call shutdownNow() followed by awaitTermination() just before the executor object goes out of scope, and we lose the last reference to it.
Net Ping version 1.7 has this fix, and is available in the Android Market. Enjoy!