I wrote an Android game based on Google's Lunar Lander example (using the Android Canvas for 2D drawing). The game has worked fine on all Android devices since 2009, except users have recently reported that it fails to start on the LG G4. Sometimes the G4 completely freezes so users have to remove the battery to restart it.
I used LG's Developer program to borrow an LG G4, and reproduced the problem. Basically the app halts without throwing any exceptions. Usually it eventually gives an "application not responding" error. I also observed an occasional complete crash of the operating system, and, very rarely, a successful start of the game.
I found a workaround that seems to resolve the problem, so I'm answering my own question, below, in case others run into this. (Though the real solution is for LG to not introduce bugs into the Android OS.)
I performed a "binary search" by adding log statements to find the line where the program freezes. I discovered that the freeze occurs on the initial startup, when the rendering thread is trying to acquire a lock on the canvas.
The problem disappears if I insert a thread.sleep() immediately before my rendering thread tries to acquire the lock. I discovered this completely by accident: I inserted a log statement there, and the log statement itself caused enough of a delay that the game started working! Here's my (simplified) code structure and the fix:
public class theview extends SurfaceView implements SurfaceHolder.Callback {
private final SurfaceHolder mSurfaceHolder;
public theview(Context context, AttributeSet attrs) {
super(context, attrs);
mSurfaceHolder = getHolder();
//(other game-initialization code omitted)
thread = new LunarThread();
public void surfaceCreated(SurfaceHolder holder) {
//(other game-resuming code omitted)
mRun=True;
thread.start();
}
class LunarThread extends Thread {
@Override
public void run() {
Canvas c=null;
while (mRun) {
try {
LunarThread.sleep(0, 1); // <---the LG G4 needs this line or the game doesn't load! Sleep duration didn't matter.
}catch(InterruptedException ignored){}
if(c==null){
LunarThread.yield();//app is still loading; wait for it.
}
try {
c = mSurfaceHolder.lockCanvas(null); // <---- this is the line where the LG G4 freezes without the above sleep()
if (c != null) {
synchronized (mSurfaceHolder) {
if (mMode == STATE_RUNNING) doPhysics();
doDraw(c);
}
}
}
finally {
// do this in a finally so that if an exception is thrown
// during the above, we don't leave the Surface in an
// inconsistent state
if (c != null) {
mSurfaceHolder.unlockCanvasAndPost(c);
}
}
}
The 1ns delay is short enough that I just added it for all phones.
(It should go without saying, but obviously this is just a mitigation for a nasty underlying bug in the LG G4's customized version of the Android OS. Customers report that it doesn't happen with other LG phones. I am submitting the issue to LG separately.)