TextureView продолжает получать SIGSEGV 11

Я нашел это решение, но к сожалению, это не сработало для меня. Когда я показываю только небольшой движущийся прямоугольник, поэтому анимация не облагается налогом, он работает нормально, но я хочу отображать некоторые кадры анимации, которые я загружаю с помощью .png, и всякий раз, когда я сворачиваю свое приложение или нажимаю кнопку «Назад», я немедленно получите ошибку SIGSEV.

Для одного экрана, где я рисую график с большим количеством точек, я нашел решение, в котором я просто останавливаю поток после того, как закончу рисовать линии, но, поскольку мне нужно отобразить движущуюся анимацию, я не могу сделать это в этом конкретный фрагмент.

Мой код для потока рендеринга выглядит так:

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

Очевидно, что это основано на примере Ромена Гая, который можно найти здесь

Помощь очень ценится!

Редактировать: аварийный дамп вот такой:

********** 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@[email protected]

person Sami    schedule 17.06.2015    source источник
comment
Пожалуйста, покажите собственный аварийный дамп в вашем вопросе.   -  person fadden    schedule 17.06.2015
comment
Сбой в блит-функции Skia, используемой drawBitmap(). SIGSEGV с SEGV_MAPERR указывает, что он пытается получить доступ к странице, которая не сопоставлена ​​с процессом. Я предполагаю, что хранилище пикселей Bitmap отбрасывается, когда действие приостанавливается, но каким-то образом приложение все еще пытается получить доступ к данным через этот указатель.   -  person fadden    schedule 18.06.2015
comment
Да, я тоже так думал, но не знаю, как этого избежать. Я прерываю поток в методах жизненного цикла, поэтому он должен остановиться до того, как это произойдет...   -  person Sami    schedule 19.06.2015
comment
Вам нужен Thread.join() звонок в stopRendering()? Вызов interrupt() не ожидает остановки другого потока, поэтому, если вы или платформа приложения выполняете очистку, пока ваш поток все еще работает, возможен сбой.   -  person fadden    schedule 19.06.2015
comment
Я попробую это и отчитаюсь!   -  person Sami    schedule 21.06.2015
comment
Пока вроде работает!   -  person Sami    schedule 22.06.2015
comment
Хорошо, у меня больше нет сбоев, так что thread.join() работает! Если вы опубликуете это как отдельный ответ, я приму его! :)   -  person Sami    schedule 25.06.2015
comment
Готово. Я также немного рассказал об игровых циклах.   -  person fadden    schedule 25.06.2015


Ответы (2)


Важно остановить рендеринг до того, как onPause() вернется, так как фреймворк начнет все разрушать. Простой и эффективный способ сделать это — попросить поток рендерера остановиться и подождать, пока он это сделает, с помощью Thread#join().

Альтернативой методу «поспать немного» является использование Choreographer. (API 16+), который вызывает обратный вызов для VSYNC. Вы все еще можете выполнять рендеринг в отдельном потоке для повышения производительности на многоядерных устройствах. Действие "запись приложения GL" в Grafika использует Choreographer для подачи сигнала потоку рендеринга, который использует стандартный механизм Android Looper/Handler. Он постоянно рендерится со скоростью 60 кадров в секунду и демонстрирует грубый механизм пропуска кадров, когда система замедляется.

(Обратите внимание, что в примерах Grafika используется SurfaceView, а не TextureView, для которого правила немного отличаются — жизненный цикл SurfaceView Surface не привязан к onPause(), поэтому остановка потока и присоединение фактически происходят в обратном вызове surfaceDestroyed().)

См. также раздел "игровые циклы" в документе по графической архитектуре.

person fadden    schedule 25.06.2015
comment
Я не думаю, что вызов Thread#join() в UIThread - хорошая идея, может быть, кто-то нашел лучшее решение? - person AndroidCoolestRulest; 02.02.2016
comment
@AndroidCoolestRulest: альтернатива — не рисовать на TextureView из отдельного потока. Например, выполните рендеринг в растровое изображение за пределами экрана из другого потока, а затем перенесите его в TextureView из потока пользовательского интерфейса. - person fadden; 02.02.2016

Я бы переключил строки в функции stopRendering из:

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

to:

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

причина в том, что возможно, что interrupt() прервет sleep() в вашем потоке, тогда поток рендеринга продолжит выполнение и обнаружит, что mRunning все еще верен. Я не уверен, что это проблема, вызывающая ваши сбои.

[редактировать]

подсказки, чтобы сделать код более надежным:

  1. Как написал Фадден в комментариях, хорошей идеей будет дождаться завершения потока рендеринга в stopRendering().
  2. Вы можете проверить mSimulationAnimationView.getSurface().isValid() перед его блокировкой, если оно ложно, то continue зациклит поток рендеринга.
  3. После того, как вы вызвали lockCanvas(null), проверьте, не является ли результат нулевым, прежде чем использовать его. В документах говорится:

Если null не возвращается, эта функция внутренне удерживает блокировку до соответствующего вызова unlockCanvasAndPost(Canvas), не позволяя SurfaceView создавать, уничтожать или изменять поверхность во время ее рисования.

так что, как только у вас есть замок, вы должны быть в безопасности

person marcinj    schedule 17.06.2015
comment
Хотя идея хорошая, и я ее поменял на эту, но к сожалению все равно вылетает. Я думаю о том, чтобы просто использовать анимацию просмотра, так как я меняю ее только каждые 250 мс и не делаю никаких больших вычислений, а только отключаю отображаемое изображение png... - person Sami; 17.06.2015
comment
Не имеет значения — условие while также проверяет наличие Thread.interrupted(). interrupt() просто устанавливает флаг, фактически не прерывая другой поток. - person fadden; 19.06.2015
comment
Уточнение: это выведет другой поток из режима ожидания/сна. Это не повлияет на него, если он активно работает. Флаг прерывания будет оставаться поднятым до тех пор, пока он не будет очищен Thread.interrupted(), который проверяется одновременно с mRunning. Вообще говоря, есть лучшие способы сделать цикл рисования - я подозреваю, что исходный пример несколько устарел. - person fadden; 19.06.2015
comment
Что было бы лучше для цикла рисования? - person Sami; 22.06.2015
comment
@Sami: Хореограф рекомендуется для API 16+. См. source.android.com/devices/graphics/architecture.html#loops для обсуждения темы. - person fadden; 22.06.2015