What I wish I’d knew about Android from start


Android docs suck. There are a few rare exceptions. But overall, it sucks.
This makes dealing with Android a lot harder than it should be. Many examples cover useless cases while real-world cases are nowhere to be mentioned. Once in a while you get lucky on Stack Overflow, but most of the time you end up just experimenting.

Unless your app is a calculator, sooner or later you will encounter a problem referred in this post. If you’re a low level developer, you probably already hit them from the beginning.

First Step

DOWNLOAD THE SOURCE CODE. The documentation sucks, so you’re best analyzing the source. Fortunately, the source code is quite clean. Setup a Linux Box, and use repo. It’s >8GB but it is worth it.
If you don’t want to, you can still go to Android GoogleSource and download the repos one by one. You really, really want to download platforms/frameworks/base. It’s one of the most important ones.

Main loop and threading

You may have read to the death the Handler documentation. It is a means of multithreading communication. But it fails to mention how this is done or how thread-safe it is.
Well, if you analyze Handler.java most functions are just proxies to boolean enqueueMessage.
This redirects us to the MessageQueue.java file in which its internal access is synchronized (aka Thread safe).
We can do the same with all the functions and we’ll soon found out there’s a global lock for everything. So yep, it’s thread safe. Arguable, not efficient either. But this is Java, not C (sarcasm).

But how does the Handler get consumed? How does the other thread get notified of this message that was just added to the Queue?
Here is where Looper.java enters. If you’ve opened that last link, you’re looking at the Holy Grail of Android. Look at “void loop” definition. It’s THE main loop.
Read again. THE MAIN LOOP.
Every time you post a Runnable, send a message, or your Activity gets scheduled for update, this loop is in charge of reading the next event and run it.
The UI thread has a looper. And your worker thread won’t have one unless you explicitly create a Looper for it. Handlers to communicate to your worker threads won’t work until you setup the looper in each one of them.
Creating a looper with a handler is easy, all you have to do is:

public class MyThread extends Thread
{

Handler handler = null;

public void run() {
try {
// preparing a looper on current thread
// the current thread is being detected implicitly
Looper.prepare();

// now, the handler will automatically bind to the
// Looper that is attached to the current thread
// You don't need to specify the Looper explicitly
handler = new Handler();

// After the following line the thread will start
// running the message loop and will not normally
// exit the loop unless a problem happens or you
// quit() the looper (see below)
Looper.loop();
} catch (Throwable t) {
Log.e(TAG, "halted due to an error", t);
}
}

Note that “Looper” is a static function and there are static global locks inside. Yes, more architectural awesomeness from the developers of Android. It’s pretty evident Android was done in a rush to compete with the iPhone and under tight deadlines. Kudos for them though, most locks hold the lock for as little as possible, at least the devs actually really knew what they were doing.

There is a caveat though: Looper.loop doesn’t return. So if you want to receive Handle messages, you have to cede control of your thread to the Looper class. You can still schedule yourself through the Handler using a Runnable to run on each iteration of the Looper’s loop, instead of doing your own “while( !bStop ) { doTightLoop(); }”

 

So, we’ve covered the thread safety of Handler class, and how it will be processed by the receiving thread. One black box unveiled.

 

MotionEvents aren’t thread safe

This is another example of Android’s great documentation. Responding to Touch Events teaches an almost useless way of handling touch events with GLSuraceView, which purposedly uses battery-friendly techniques (update the screen on demand) to hide the fact that you can’t do that when updating every frame; regardless of whether you care about the battery.

This code will not work:

@Override
public boolean onTouchEvent( final android.view.MotionEvent event )
{
renderThread.queueEvent( new Runnable()
{
@Override
public void run()
{
renderThread.handleTouchEventInOtherThread( event );
}
});

return super.onTouchEvent( event );
}

Why won’t it work? If we analyze the View.java and ViewRootImpl.java files, after onTouchEvent, event.recycle() will be called, which puts the event back into a pool to be reused. In other words, by the time the other thread reads the event, it will see garbage.

Luckily, there is a solution not mentioned in the documentation: MotionEvent.obtain() can clone the event.

We’ve seen the key parts of MotionEvent thread unsafety, and how to solve it. We now know many events are recycled by the OS and thus cannot be used outside listener’s function. Another black box unveiled.

The following code works:

@Override
public boolean onTouchEvent( final android.view.MotionEvent event )
{
final android.view.MotionEvent safeEvent = android.view.MotionEvent.obtain( event );
renderThread.queueEvent( new Runnable()
{
@Override
public void run()
{
renderThread.handleTouchEventInOtherThread( safeEvent );
safeEvent.recycle(); //This call can be skipped
}
});

return super.onTouchEvent( event );
}

But can we call safeEvent.recycle? Is it thread safe? YES! there is a global lock enclosing the recycle call.
It’s not necessary to call recycle though. In theory the Java machine should eventually see the pointer is no longer used and garbage collect it. The recycling system is there to prevent garbage collection and reuse the pointer.

This recycle pattern protected by a global lock to prevent garbage collection is a recurring pattern across Android’s source code, you better get used to it and know how to recognize it.

SensorEventListener can be delivered to other threads

Unlike onTouchEvent, SensorEventListener works very similar (their input arguments get recycled, don’t use them outside the function) but with a major advantage: SensorManager.registerListener accepts a 4th parameter which is a Handler. By passing the handler of the thread you want to receive the events at, that thread will safely receive the motion sensor events (provided there is a Looper running… now you see why Loopers are so important?).
If you don’t provide a handler (or pass a null reference) the Main Looper will be used (I don’t know what is the “Main Looper”, but I suspect it’s the one from the UI thread). So, stay alert.

Final words

Deducing concurrency behaviors or potential race conditions from Android’s documentation is like watching paint getting dry to understand earthquakes.
I wish the documentation explained these things (among many other problems!) and provided useful code, but it isn’t the case.
DOWNLOAD THE SOURCE CODE. I can’t stress it enough. It will save you hours (or days!) of bumping your head against the wall because of awkward bugs or because you don’t know how to write your program correctly.

Leave a comment

Your email address will not be published. Required fields are marked *