3

I have found this Solution, but unfortunately, it didn't work for me. When I'm only displaying a small moving rectangle, so no taxing animations, it works fine, but I want to display some animation frames that I load in with a .png, and whenever I minimize my app or press the back button, I immediately get a SIGSEV error.

For one screen where I draw a graph with a lot of points I found a solution in which I just stop the thread after I'm done drawing the lines, but since I need to display a moving animation I can't do it in this particular fragment.

My code for for the render thread looks like this:

private class RenderThread extends Thread {
    private volatile boolean mRunning = true;
    int framecount = 1;

    @Override
    public void run() {


        while (mRunning && !Thread.interrupted()) {

            final Canvas canvas = mSimulationAnimationView.lockCanvas(null);

            try {
                canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
                drawCar(canvas);
            } finally {
                mSimulationAnimationView.unlockCanvasAndPost(canvas);
            }

            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                // Sleep if the Thread is interrupted
            }
        }
    }

    public void stopRendering() {
        interrupt();
        mRunning = false;
    }


    private void drawCar(Canvas canvas){

        if(framecount==1){
            canvas.drawBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.piston_frame_one), 10, 10, null);
            framecount++;
        }
        else{
            canvas.drawBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.piston_frame_two), 10, 10, null);
            framecount--;

        }



    }

}//RenderThread

This is obviously based on Romain Guy's example which can be found here

Help is very much appreciated!

Edit: the crash dump is this one:

********** Crash dump: **********
Build fingerprint: 'google/hammerhead/hammerhead:5.1.1/LMY48B/1863243:user/release-keys'
pid: 16130, tid: 16343, name: Thread-19966  >>> package.package.package <<<
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x9ee3ad10
Stack frame #00 pc 001b474a  /system/lib/libskia.so (S32A_Opaque_BlitRow32_neon_src_alpha(unsigned int*, unsigned int const*, int, unsigned int)+109)
Stack frame #01 pc 001072fb  /system/lib/libskia.so
Stack frame #02 pc 00103793  /system/lib/libskia.so
Stack frame #03 pc 0010385f  /system/lib/libskia.so (SkScan::FillIRect(SkIRect const&, SkRegion const*, SkBlitter*)+198)
Stack frame #04 pc 0010395f  /system/lib/libskia.so (SkScan::FillIRect(SkIRect const&, SkRasterClip const&, SkBlitter*)+36)
Stack frame #05 pc 000e0e27  /system/lib/libskia.so (SkDraw::drawBitmap(SkBitmap const&, SkMatrix const&, SkPaint const&) const+464)
Stack frame #06 pc 000d90c9  /system/lib/libskia.so
Stack frame #07 pc 000d91b1  /system/lib/libskia.so (SkCanvas::drawBitmap(SkBitmap const&, float, float, SkPaint const*)+116)
Stack frame #08 pc 000947d1  /system/lib/libandroid_runtime.so (android::SkiaCanvas::drawBitmap(SkBitmap const&, float, float, SkPaint const*)+12)
Stack frame #09 pc 0008a7b7  /system/lib/libandroid_runtime.so
Stack frame #10 pc 007eff33  /data/dalvik-cache/arm/system@framework@boot.oat
Community
  • 1
  • 1
Sami
  • 129
  • 2
  • 15
  • Please show the native crash dump in your question. – fadden Jun 17 '15 at 18:26
  • It's crashing in a Skia blit function, used by `drawBitmap()`. SIGSEGV with SEGV_MAPERR indicates that it's attempting to access a page that is not mapped into the process. My guess would be that the Bitmap's pixel storage is being discarded when the Activity pauses, but somehow the app is still trying to access data through that pointer. – fadden Jun 18 '15 at 18:04
  • Yes, that's what I thought as well, but I don't know how to prevent that. I interrupt the thread in the lifecycle methods, so it should stop before that happens... – Sami Jun 19 '15 at 09:37
  • 1
    Do you need a `Thread.join()` call in `stopRendering()`? The `interrupt()` call doesn't wait for the other thread to stop, so if you or the app framework is doing clean-up work while your thread is still running, a crash is possible. – fadden Jun 19 '15 at 16:16
  • I will try that out and report back! – Sami Jun 21 '15 at 10:09
  • So far it seems to work! – Sami Jun 22 '15 at 12:25
  • Alright, I don't have any more crashes, so the thread.join() seems to work! If you post this as a separate answer, I will accept it! :) – Sami Jun 25 '15 at 07:41
  • Done. I also expounded a bit on game loops. – fadden Jun 25 '15 at 15:37

2 Answers2

3

I would switch lines in stopRendering function, from:

public void stopRendering() {
    interrupt();
    mRunning = false;
}

to:

public void stopRendering() {
    mRunning = false;
    interrupt();
}

the reason is that it is possible that interrupt() will break sleep() in your thread, then render thread will continue execution and will find out that mRunning is still true. I am not sure if this is the problem causing your crashes .

[edit]

hints to make code more reliable:

  1. As fadden has written in comments, its good idea to wait in stopRendering() for rendering thread to end.
  2. You might check mSimulationAnimationView.getSurface().isValid() before locking it, if its false then continue looping your rendering thread.
  3. Once you have called lockCanvas(null) check if result is non null before using it. In docs it says:

If null is not returned, this function internally holds a lock until the corresponding unlockCanvasAndPost(Canvas) call, preventing SurfaceView from creating, destroying, or modifying the surface while it is being drawn.

so once you have a lock, you should be safe

marcinj
  • 48,511
  • 9
  • 79
  • 100
  • While a good idea, and I changed it to that, but unfortunately it is still crashing. I'm thinking about just using a view animation since I only change it about every 250ms and don't do any big calculations but only switch out the png image to be displayed... – Sami Jun 17 '15 at 16:37
  • Shouldn't matter -- the `while` condition also checks for `Thread.interrupted()`. `interrupt()` just sets a flag, it doesn't actually disrupt the other thread. – fadden Jun 19 '15 at 16:09
  • Clarification: it will kick the other thread out of wait/sleep. It won't affect it if it's actively running. The interrupted flag will remain raised until it's cleared by `Thread.interrupted()`, which is checked at the same time as `mRunning`. Generally speaking there are better ways to do a draw loop -- I suspect the source example is somewhat old. – fadden Jun 19 '15 at 16:19
  • What would be a better way for a draw loop then? – Sami Jun 22 '15 at 12:26
  • 1
    @Sami: Choreographer is recommended for API 16+. See https://source.android.com/devices/graphics/architecture.html#loops for a discussion of the subject. – fadden Jun 22 '15 at 15:41
2

It's important to stop rendering before onPause() returns, as the framework will start tearing things down. A simple and effective way to do this is to ask the renderer thread to stop, and wait for it to do so with Thread#join().

An alternative to the "sleep for a while" approach is to use Choreographer (API 16+), which invokes a callback on VSYNC. You may still want to do the rendering in a separate thread for better performance on multi-core devices. The "record GL app" activity in Grafika uses Choreographer to signal a renderer thread that uses the standard Android Looper/Handler mechanism. It consistently renders at 60fps, and demonstrates a crude mechanism for dropping frames when the system slows down.

(Note the Grafika examples uses SurfaceView, not TextureView, for which the rules are slightly different -- the SurfaceView Surface's lifecycle is not tied to onPause(), so the thread stop and join actually happen in the surfaceDestroyed() callback.)

See also the "game loops" section in the graphics architecture doc.

fadden
  • 51,356
  • 5
  • 116
  • 166
  • I don't think that calling Thread#join() on UIThread is a good idea, may be someone found a better solution ? – AndroidCoolestRulest Feb 02 '16 at 14:58
  • 1
    @AndroidCoolestRulest: the alternative is to not draw on a TextureView from a separate thread. For example, render to an offscreen Bitmap from the other thread, then blit it to the TextureView from the UI thread. – fadden Feb 02 '16 at 16:38