This article explores the possibility of race conditions when using Asynchronous Messages in Android. The software developer will learn that some API calls will not work in a synchronous manner, and the Android documentation may not specify that the calls are asynchronous.
Android has a fondness for asynchronous message passing (sending a message and not waiting for a response). Many of Android’s services use this technique to allow methods in their public interface, running in a user process, to talk to the system process that actually performs the work. The problem is that most programmers will naturally assume that the C functions or Java methods defined in a public interface will behave synchronously (perform the requested task before returning), unless there is documentation stating otherwise.
An example
Let’s look at some actual code where asynchronous message passing causes a race condition and occasional failure.
LocationManager has a method called setTestProviderLocation, which is used by the CTS. There is nothing in the documentation that hints at the fact that calling this method does not immediately set the location.
The code below is from the CTS, and is used by several of its tests:
// update location to outside proximity range<span>updateLocation(30, 30);registerProximityListener(0, 0, 1000, expiration);// update location to inside proximity rangeupdateLocation(0, 0);waitForReceiveBroadcast();</span>
The updateLocation method calls setTestProviderLocation, which eventually calls LocationManagerService.reportLocation, which sends an asynchronous message. Usually this works just fine, but sometimes the message is not handled quickly enough, and the code effectively behaves like this:
registerProximityListener(0, 0, 1000, expiration);// update location to outside proximity rangeupdateLocation(30, 30);// update location to inside proximity rangeupdateLocation(0, 0);waitForReceiveBroadcast();
When this happens, a failure in the CTS will occur. Why? Because once a proximity alert is set, only the first location change is guaranteed to have an effect. Any subsequent location changes will be ignored if they happen within one second of the previous change. But like the asynchronous message passing, this “feature” is not documented anywhere, except in the source code.
The moral of the story
Android is filled with asynchronous message passing and potential race conditions, so don’t assume that any method in the Android API is synchronous. If you have some code that works most of the time, but not always, there’s a good chance that Android’s reliance on asynchronous message passing is the cause.
Dig through the source code for the API method you are using, and look for calls to Handler.sendMessage and the like. If you see them, then the API method is probably not synchronous, and you’ll need to take that into account. Often, you can simply work around the problem by adding a short delay after the asynchronous call:
try { Thread.sleep(200); } catch (Exception e) { }
Commentaires