Рисование на поверхность из JNI при перемещении SurfaceView по кругу

#android #android-ndk #surfaceview

#Android #android-ndk #surfaceview

Вопрос:

В моем приложении у меня есть SurfaceView макет активности, поэтому на его поверхность не влияют жизненные циклы фрагментов. Я использую поверхность для рисования на ней из JNI, используя следующий код (некоторые переменные являются глобальными):

 void onDraw() {
    int height=0, width=0;
    ANativeWindow_Buffer nativeBuffer;
    ARect redrawRect;

    if (nativeWindow != NULL) {
        height = ANativeWindow_getHeight(nativeWindow);
        width = ANativeWindow_getWidth(nativeWindow);

        redrawRect.left = 0;
        redrawRect.top = 0;
        redrawRect.right = width;
        redrawRect.bottom = height;
    
        if (ANativeWindow_lock(nativeWindow, amp;nativeBuffer, amp;redrawRect) == 0) {
            height = (redrawRect.bottom - redrawRect.top);
            width = (redrawRect.right - redrawRect.left);
    
            bufferSize= height * width * 4;
            memcpy(nativeBuffer.bits, pbuf, bufferSize);
            ANativeWindow_unlockAndPost(nativeWindow);
        }
    }
}
 

Пока я не изменяю размер и положение SurfaceView , это отлично работает. Однако, когда я начинаю рисовать, корректирую SurfaceView размер и положение (содержимое настраивается нормально), останавливаю рисование, а затем запускаю его снова, я получаю следующую ошибку:

E / BufferQueue: [SurfaceView] подключение: уже подключено (cur = 2, req = 2)

Что я понимаю из документации до сих пор, так это то, что my SurfaceView является потребителем и ANativeWindow производителем. SurfaceView содержит a BufferQueue , который отправляет данные отображения (скопированные memcpy) от производителя на сторону потребителя. Итак, после изменения размера и местоположения один из них (я не уверен, какой) снова пытается подключиться к этому BufferQueue. Поскольку в моем коде я не вызываю queue/acquire и т. Д., В частности, я не уверен, как это предотвратить.

Чего я не знаю: вызывается ли connect со ANativeWindow_lock стороны NDK или ANativeWindow_fromSurface на стороне NDK? Может ли буфер, полученный методом блокировки, каким-то образом протекать, чтобы предотвратить подключение в следующий раз? Я также добавил сообщения журнала в результаты блокировки и разблокировки и обнаружил, что после сбоя изменения размера и местоположения ANativeWindow_lock . Мне это кажется странным, потому что моя разблокировка выполняется только тогда, когда блокировка прошла успешно, и я вообще не блокирую surface со стороны Java. Если я попытаюсь разблокировать его, то из JNI я получу:

Сбой Surface :: unlockAndPost, нет заблокированного буфера

Я также подумал о других утечках памяти и проанализировал их с помощью LeakCanary. Это не обнаруживает никаких утечек, даже когда я делаю дамп кучи вручную, хотя я не уверен, насколько хорошо это работает с машинным кодом.

Если я вообще не рисую, эта ошибка не появляется. У вас есть какие-либо идеи для этого?

Если это имеет значение, я разрабатываю на Android KitKat (4.4.2). Любая помощь приветствуется.

Комментарии:

1. Изменение размера поверхности выполняется с помощью SurfaceFlinger, и вы не можете рисовать, пока выполняется изменение размера / перемещение. Вы должны определить, когда окно перемещается / изменяет размер, и прекратить попытки заблокировать его для рисования.

2. Я изменяю размер своего SurfaceView при переключении между двумя фрагментами. Итак, вы говорите, что я должен заблокировать surface во время этой транзакции и освободить его, когда транзакция завершена? Есть lockCanvas и unlockCanvasAndPost правильные методы для этого?

3. Блокировка / разблокировка используются, когда вы хотите ИЗМЕНИТЬ пиксели: чтобы помешать другим (другим потокам) вносить изменения, когда вы в данный момент работаете на той же поверхности. Вы должны повторно ЗАБЛОКИРОВАТЬ все блокировки (количество lockCanvas() должно быть таким же, как у unlock()), и подождать, пока Поверхность не станет свободной для блокировки или доступной. Если я хорошо помню, есть способ проверить доступность SurfaceView, прежде чем пытаться заблокировать холст.

4. Пока SurfaceView удерживается SurfaceFlinger (или другими), вы должны остановиться, чтобы рисовать на нем. Затем вы должны быть уверены, что вызывали unlockCanvas() столько же времени, сколько и метод блокировки.

5. Спасибо за объяснение, я попробую это завтра! Но почему это работает в первый раз (пока я не останавливаю рисование, содержимое перемещается и масштабируется нормально)?