3

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.

Green Cell
  • 4,677
  • 2
  • 18
  • 49
  • Check it https://stackoverflow.com/questions/47707707/app-crashing-after-theme-update?rq=1&utm_medium=organic&utm_source=google_rich_qa&utm_campaign=google_rich_qa – Ninja Mar 30 '18 at 14:05
  • 1
    I don't follow. The question in that link is completely different to what's happening here. – Green Cell Mar 30 '18 at 14:09
  • I tried to reproduce the issue with the code you provided, but I couldn't. Does it happen for any specific Android version/device? – Marcin Jedynak Apr 08 '18 at 09:07
  • Right now I'm testing on a Pixel 2 with android version 8.1.0 – Green Cell Apr 09 '18 at 03:45

2 Answers2

2

In draw(), I'd try moving handler.removeCallbacks(drawRunner); just before the try block. It could be that onOffsetsChanged() is getting called on orientation change, and that the previous thread on the handler might not have called unlockCanvasAndPost(c) yet, which explains why you're getting an error with lockCanvas() 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 overriden onOffsetsChanged().

Another thing you could try is overriding onSurfaceChanged() and clearing the handler queue like this:

@Override
public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
    handler.removeCallbacks(drawRunner);
    super.onSurfaceChanged(holder, format, width, height);
}

Ultimately, all of the examples regarding WallpaperService that I've read online have a try-finally block with the lock/unlock canvas logic, so I wouldn't be worried.

Cvarier
  • 161
  • 13
  • The error still triggers once in a while but it does feel more stable. Yes I noticed all the examples ended up with a try catch, just feels dirty, but I guess I'll need to live with it :). Thank you! – Green Cell Apr 08 '18 at 13:54
  • It is pretty weird, but I'm glad I could help! :) – Cvarier Apr 08 '18 at 17:28
2

try android:configChanges="orientation" on your <service android:name=".LiveWallpaperService" /> in Manifest

example: <service android:name=".LiveWallpaperService" android:configChanges="orientation" />

shb
  • 5,957
  • 2
  • 15
  • 32