Search code examples
androidsurfaceview

Android: Surface has been released


So this bit of code seems to be the problem:

public void surfaceCreated(final SurfaceHolder sh) {
    loop = new Loop();
    loop.setMethod(new LoopMethod() {
        public void callMethod() {
            Canvas canvas = sh.lockCanvas();
            synchronized(sh) {
                draw(canvas);
            }
            sh.unlockCanvasAndPost(canvas);
        }
    });
    loop.setRunning(true);
    loop.start();
}

This works fine up until the user backs out of the application, or opens up the Recent Items tray. Upon which I get the following error:

java.lang.IllegalStateException: Surface has already been released.

which points to this line:

 sh.unlockCanvasAndPost(canvas);

Why is this occurring and how do I fix it?

EDIT: ENTIRE PROBLEMATIC CLASS

public class SplashScreen extends SurfaceView implements SurfaceHolder.Callback {
    private SplashActivity splashActivity;
    private Loop loop;

    //Loading gif info
    private Movie loadingGif;
    private long gifStart;
    private int gifW, gifH;
    private boolean gifDone;

    public SplashScreen(Context context, SplashActivity splashActivity) {
        super(context);
        getHolder().addCallback(this);

        this.splashActivity = splashActivity;

        //Load gif in
        InputStream gifStream = context.getResources().openRawResource(+ R.drawable.load);
        loadingGif = Movie.decodeStream(gifStream);
        gifW = loadingGif.width();
        gifH = loadingGif.height();
        gifDone = false;
    }

    private void beginLoading() {

    }

    public void surfaceCreated(final SurfaceHolder sh) {
        loop = new Loop();
        loop.setMethod(new LoopMethod() {
            public void callMethod() {
                Canvas canvas = sh.lockCanvas();
                synchronized(sh) {
                    draw(canvas);
                }
                sh.unlockCanvasAndPost(canvas);
            }
        });
        loop.setRunning(true);
        loop.start();
    }

    public void draw(Canvas canvas) {
        if(canvas != null) {
            super.draw(canvas);
            canvas.save();

            canvas.drawColor(Color.rgb(69, 69, 69));
            Paint p = new Paint();
            p.setAntiAlias(true);

            long now = android.os.SystemClock.uptimeMillis();
            if(gifStart == 0)
                gifStart = now;
            if(loadingGif != null) {
                int dur = loadingGif.duration();
                if(!gifDone) {
                    int relTime = (int) ((now - gifStart) % dur);
                    loadingGif.setTime(relTime);
                }
                else
                    loadingGif.setTime(dur);

                if(now - gifStart > dur && !gifDone) {
                    gifDone = true;
                    loadingGif.setTime(dur);
                    beginLoading();
                }

                float scaleX = getWidth()/gifW*1f;
                float scaleY = getHeight()/gifH*1f;
                float centerX = getWidth()/2/scaleX - gifW/2;
                float centerY = getHeight()/2/scaleY - gifH/2;

                canvas.scale(scaleX, scaleY);
                loadingGif.draw(canvas, centerX, centerY, p);
                canvas.restore();

                p.setColor(Color.WHITE);
                p.setTypeface(Typeface.create("Segoe UI", Typeface.BOLD));
                p.setTextSize(UsefulMethods.spToPx(12));
                String s = "Version " + BuildConfig.VERSION_NAME;
                canvas.drawText(s, 10, getHeight() - 10, p);
            }
        }
        else
            System.out.println("not drawing :(");
    }

    public void surfaceDestroyed(SurfaceHolder sh) {
        loop.setRunning(false);
    }
    public void surfaceChanged(SurfaceHolder sh, int format, int width, int height) {
        System.out.println("call");
    }
}

Solution

  • Check if visibility has changed, if it has pause/resume loop:

    public void onVisibilityChanged(View view, int visibility) {
        super.onVisibilityChanged(view, visibility);
        if(loop != null)
            if(visibility == VISIBLE)
                if(!loop.isRunning())
                    loop.setRunning(true);
                else;
            else if(visibility == INVISIBLE)
                if(loop.isRunning())
                    loop.setRunning(false);
    }