I'm trying to build a live wallpaper in Android, but it crashes the app when the orientation changes. It looks like it's crashing when trying to lockCanvas on the surface holder, but I'm not sure what I can do to prevent it.
Here's the class:
public class LiveWallpaperService extends WallpaperService
{
public void onCreate() {
super.onCreate();
}
public void onDestroy() {
super.onDestroy();
}
public Engine onCreateEngine() {
return new MyWallpaperEngine();
}
class MyWallpaperEngine extends Engine
{
private final Handler handler = new Handler();
private final Runnable drawRunner = new Runnable() {
@Override
public void run() {
draw();
}
};
private boolean visible = true;
Paint paint;
MyWallpaperEngine() {
paint = new Paint();
}
public void onCreate(SurfaceHolder surfaceHolder) {
super.onCreate(surfaceHolder);
}
@Override
public void onVisibilityChanged(boolean visible) {
this.visible = visible;
if (visible) {
handler.post(drawRunner);
}
else {
handler.removeCallbacks(drawRunner);
}
}
@Override
public void onSurfaceDestroyed(SurfaceHolder holder) {
super.onSurfaceDestroyed(holder);
this.visible = false;
handler.removeCallbacks(drawRunner);
}
public void onOffsetsChanged(float xOffset, float yOffset, float xStep, float yStep, int xPixels, int yPixels) {
draw();
}
void draw() {
final SurfaceHolder holder = getSurfaceHolder();
Canvas c = null;
try {
c = holder.lockCanvas();
if (c != null) {
// Paint stuff here.
}
}
finally {
if (c != null) {
holder.unlockCanvasAndPost(c);
}
}
handler.removeCallbacks(drawRunner);
if (visible) {
handler.postDelayed(drawRunner, 10);
}
}
}
}
And this is the exception that happens when the orientation changes:
E/StudioProfiler: JVMTI error: 15(JVMTI_ERROR_THREAD_NOT_ALIVE)
E/Surface: dequeueBuffer failed (No such device)
E/BaseSurfaceHolder: Exception locking surface
java.lang.IllegalArgumentException
at android.view.Surface.nativeLockCanvas(Native Method)
at android.view.Surface.lockCanvas(Surface.java:318)
at com.android.internal.view.BaseSurfaceHolder.internalLockCanvas(BaseSurfaceHolder.java:194)
at com.android.internal.view.BaseSurfaceHolder.lockCanvas(BaseSurfaceHolder.java:158)
at android.service.wallpaper.WallpaperService$Engine$1.lockCanvas(WallpaperService.java:262)
at greencell.bitpatternswallpaper.LiveWallpaperService$MyWallpaperEngine.draw(LiveWallpaperService.java:206)
at greencell.bitpatternswallpaper.LiveWallpaperService$MyWallpaperEngine$1.run(LiveWallpaperService.java:51)
at android.os.Handler.handleCallback(Handler.java:790)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6494)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
Update:
I've checked many other threads that seem to have the same issue but the only thing that I can do so far is to wrap unlockCanvasAndPost and lockCanvas in a try catch to ignore IllegalArgumentException.
In
draw(), I'd try movinghandler.removeCallbacks(drawRunner);just before thetryblock. It could be thatonOffsetsChanged()is getting called on orientation change, and that the previous thread on the handler might not have calledunlockCanvasAndPost(c)yet, which explains why you're getting an error withlockCanvas()at that point. However, this shouldn't be the case if the code you've posted here exactly matches what you're running locally, since you haven't overridenonOffsetsChanged().Another thing you could try is overriding
onSurfaceChanged()and clearing the handler queue like this:Ultimately, all of the examples regarding
WallpaperServicethat I've read online have atry-finallyblock with the lock/unlock canvas logic, so I wouldn't be worried.