Through ACRA, I have received a small number of reports from an alpha build of software that show that an exception is occurring during a specific call to Canvas.restore()
. The exception is java.lang.IllegalStateException: Underflow in restore
.
I am well aware that this exception shall occur if one too many restore()
calls are made than save()
calls. However, from very careful code inspection, I am absolutely certain that all calls to canvas.save()
and canvas.restore()
are balanced. That is, all calls to canvas.save()
are most certainly balanced at a later stage by a call to canvas.restore()
. I can also confirm that there are no conditionals, exceptions or early returns from methods that could result in a missing canvas.save()
leading to the stack underflow.
Furthermore, this issue seems to be a rare edge-case that is resulting in the exception happening only a handful of times within graphics code that renders many times every second.
The sort of structure of the code in which this is happening is:
protected void onDraw(Canvas canvas) {
...
someMethod(canvas);
...
}
void someMethod(Canvas canvas)
{
....
canvas.save();
....
someOtherMethod(Canvas canvas);
....
canvas.restore();
....
}
void someOtherMethod(Canvas canvas)
{
....
canvas.save();
....
for ( ... ) {
....
canvas.save();
...
canvas.restore();
...
}
....
canvas.restore(); // *** exception here ***
....
}
There are places where save()
/ restore()
is used within a loop, but again the calls are balanced.
The reports are from devices running 4.3 and 4.4.2.
I have attempted to Google this issue and the only interesting QA I could find is from a person who clearly had unbalanced save() / restore() calls in his code; that is categorically not the issue in my case.
This is occurring within the call stack of a custom View subclass' onDraw(Canvas canvas)
method, all happening on the UI thread. There are no other threads I have touching that Canvas
object.
I could potentially check the stack with a call to getSaveCount()
before invoking the specific restore() that has been responsible for the exceptions, but that would really be just sticking an Elastoplast over the problem. I'd rather understand what on earth the edge case is that's causing the underflow, but it is baffling me.
Are there any known issues? Could it be possible for any kind of system configuration change to affect this Canvas
while the UI thread is within the context of a View
's onDraw()
call? Are there any known limitations on the Canvas
stack size? Could there be any OS graphics calls that are known to misuse the canvas stack?