XenoAmp - Free Joomla! 3.0 Template

The joy of threading

Google has an article on "Painless threading", which I suggest you read. But since I prefer pleasure to "no pain" I needed more than that. Due to the well known fact that Android main (=GUI) thread should be used only for short atomic tasks, using additional threads is in practice a must. When I start programming XenoAmp I began to throw

new Thread(){
  run() {
     // something
  }
}.start();

completely carelesly here and there just at any place that required a background thread. It worked! Then I started adding ThreadPools! Executors! Handlers! Loopers! AsyncTasks! And very soon a pack of lone threads began to swarm like food worms I've raised once on my school sandwich which I forgot to remove from my backpack when school ended. Seriously I was not sure how many threads XenoAmp runs and when, but I was sure I am doing something very wrong.

If your code is similar you should take a moment and find all pieces of code that run in their own threads. Write them down. Now that you see the big picture think if there's some way to group these tasks. For me it was pretty easy, I've observed that:

1) There are tasks that should run in the background but I really don't care how long they take and don't need to know when they finish (writing tags to audio file, clearing old files from disk cache, deleting files, telling 8Tracks about played track, searching for cover on the Internet)

2) There are long tasks that need to run in the background to prepare some important data to be used by main process (audio calibrators, looking for new microphone profiles, getting tags from MusicBrainz, lising ShoutCast, 8Tracks or Subsonic tracks)

3) There are short lasting tasks that have to be run in the background (fetching cover from cache, saving a playlist, receiving play/pause commands)

So when all thready parts were assigned to proper groups I looked to check if these tasks can be scheduled to be executed sequentially in each group. Can cache cleaning wait until some cover is found on the Internet? Sure, doesn't hurt. Group 1) was really easy, so was group 3) as it was assumed it supports only "short, atomic tasks". If they're short, then they're almost simultanious, I'm not going to worry that XenoAmp will react to pause event only after it dumps the playlist to the disk, which is pretty fast. Last step was to think if threads from group 2) should stay there or rather be moved to 1) or 3).

Having that I've created a wrapper around HandlerThread / Looper and added three global instances of it in my Application object:

public class XenoLooper {

	private Handler bkHandler;
	private HandlerThread bkThread;

	public void post(Runnable r) {
		getHandler().post(r);
	}
	
	private Handler getHandler() {
		if (bkThread == null || (bkThread!=null && !bkThread.isAlive()))
		{
			bkThread = new HandlerThread("Xeno background worker"); bkThread.start();
		}
		
		if (bkHandler == null) {	
			bkHandler = new Handler(bkThread.getLooper());
		}

		return bkHandler;
	}

	public void dispose() {
		if(bkHandler!=null)
			bkHandler.removeCallbacksAndMessages(null);
		bkHandler=null;
		
		if(bkThread!=null)
		{
			try{
				bkThread.quit();
				bkThread.interrupt();
				
			} catch (Exception ex) {}
			bkThread=null;
		}
	}
	
	public void flushQueue() {
		if(bkHandler!=null)
			bkHandler.removeCallbacksAndMessages(null);
	}
}

Since I wanted to exit cleanly with no zombie threads, I've added dispose() function. There are also some rare cases when I want to discard queued tasks, thus the flushQueue() function. Now it was enough to obtain a reference to such XenoLopper and post() a Runnable to queue it for running on one of three available and specialized threads. So - thanks to this only "three to rule them all and in the darkness bind 'em". 99% of XenoAmp threads run on these three loopers.

© Meet Gavern - Free Joomla! 3.0 Template 2019