article

Amelia@Amazon avatar image
Amelia@Amazon posted

Ensuring use of Canvas Restore does not cause an Exception to be thrown

Summary

Each canvas context maintains a stack of it's drawing states by using the following methods.

Canvas.save() pushes the current state onto the stack - http://developer.android.com/reference/android/graphics/Canvas.html#save%28%29 Canvas.restore() pops the top state from the stack - http://developer.android.com/reference/android/graphics/Canvas.html#restore%28%29 Canvas.restoreToCount(int restoreCount) pops any selected state from the stack - http://developer.android.com/reference/android/graphics/Canvas.html#restoreToCount%28int%29

Canvas class throws "java.lang.IllegalArgumentException: Underflow in restoreToCount" when there are no saves to restore when using restore() or restoreToCount(int). From Android 5, they have introduced an additional check to ensure that the restoreCount does not exceed the number of saves made on restoreToCount(int) method.

Issue

Although this change on Lollipop increased consistency with error handling, this introduced few apps to unexpectedly crash on Lollipop devices because restoreToCount(int) used to ignore when restoreCount exceeded the number of saves.

Before Android 5, Canvas.restoreToCount(int) had the following check where IllegalArgumentException was thrown when there are no saves to restore. Reference: Canvas.cpp(4.4) - http://androidxref.com/4.4_r1/xref/frameworks/base/core/jni/android/graphics/Canvas.cpp#restoreToCount

Sample

if (restoreCount < 1) {
        doThrowIAE(env, "Underflow in restoreToCount");
        return;
    } 

From Android 5, Canvas.restoreToCount(int) was modified with additional check where restoreCount doesn't exceed the number of saves made. Reference: android_graphics_Canvas.cpp(5.0) - http://androidxref.com/5.0.0_r2/xref/frameworks/base/core/jni/android_graphics_Canvas.cpp#restoreToCount

Sample

if (restoreCount < 1 || restoreCount > canvas->getSaveCount()) {
        doThrowIAE(env, "Underflow in restoreToCount");
        return;
    }

Resolution

save() and restore() calls on Canvas should be balanced and make sure not to call restore() more than # of Canvas saves. As for restoreToCount(int), make sure to pass the argument, restoreCount, that is less than equal to the Canvas saves and greater than 0.

However as we program, there could be rare occasions that we could loose count on # of saves. One method to help prevent this is by calling getSaveCount() before invoking restore()/restoreToCount method and making sure count matches from your expectation.

Finally to make sure your app doesn't crash, you should use try-catch to catch rare unexpected Canvas stack underflow events.

Sample

     try {
        // call restore() or restoreToCount()
    } catch (java.lang.IllegalStateException exception) {
        // 
    } 

Keywords: Canvas, Graphics, Stack Underflow

KB_0020

androidlollipop
10 |5000 characters needed characters left characters exceeded

Up to 2 attachments (including images) can be used with a maximum of 512.0 KiB each and 1.0 MiB total.

Article

Contributors

brizzlebrazzle contributed to this article