Невозможно вызвать removeOnDrawListener внутри onDraw - PullRequest
1 голос
/ 28 октября 2019

Я пишу код для сохранения растрового изображения с экрана приложения в определенный момент.

Я делаю это, слушая onDraw() события для некоторого компонента и проверяя, есть ли набор условийtrue

     @Override
     public void onDraw() {



        if (checkConditions()) {
                            Canvas canv = new Canvas(tmpBitmap);
                            canvasView.draw(canv);
                            saveBitmapToImage(tmpBitmap, Bitmap.CompressFormat.JPEG);
                            this.lastDraw = System.currentTimeMillis();

                            AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
                            viewKonfetti.getViewTreeObserver().removeOnDrawListener(listener);

                        });                   
    }

                    }

Но иногда я получаю исключение при удалении слушателя

    Fatal Exception: java.lang.IllegalStateException: Cannot call removeOnDrawListener inside of onDraw
           at android.view.ViewTreeObserver.removeOnDrawListener(ViewTreeObserver.java:736)
           at com.tomatedigital.lottogram.dialogs.ShuffleWinnerDialog$Shuffler$2.lambda$onParticleSystemEnded$1(ShuffleWinnerDialog.java:235)
           at com.tomatedigital.lottogram.dialogs.-$$Lambda$ShuffleWinnerDialog$Shuffler$2$vCYJiRVhO65xXIsicqZHHpw_34A.run(-.java:4)
           at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
           at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
           at java.lang.Thread.run(Thread.java:764)

Это происходит не всегда, иногда и в производстве (на многих разных устройствах Android). устройства). НО ПОЧЕМУ?

Я даже использую новый поток для удаления слушателя ...

Я тестировал, и даже удаление нового потока не решает проблему: эта ошибка непроисходят в 100% случаев, просто иногда.

Какое объяснение? как это решить?

1 Ответ

1 голос
/ 01 ноября 2019

«Слушатель OnDrawListener нельзя добавить или удалить из этого метода». Это в документации , и, исходя из вашего опыта, причиной может быть какое-то состояние гонки , но я не знаю подробностей.

Обходной путьЯ обнаружил следующее: создайте GlobalLayoutListener и удалите там onDrawListener. Будьте осторожны, потому что GlobalLayoutListner будет вызываться до и после onDraw много раз. Вот мой код (который не идеален, потому что GlobalLayoutListener будет вызываться пару раз после того, как для isDescriptionListenerAdded установлено значение false из-за разных потоков, но это не критично. Не стесняйтесь улучшать его.)

    private var isDescriptionListenerAdded = false

fun setText(title: String, description: String, note: String) {
    titleText.text = title
    descriptionText.text = description

    var descriptionDrawListener = ViewTreeObserver.OnDrawListener {
        val maxLines = descriptionText.height / descriptionText.lineHeight
        descriptionText.maxLines = maxLines
        isDescriptionListenerAdded = true
    }

    descriptionText.viewTreeObserver.addOnDrawListener(descriptionDrawListener)
    descriptionText.viewTreeObserver.addOnGlobalLayoutListener {
        if (isDescriptionListenerAdded) {
            isDescriptionListenerAdded = false
            descriptionText.viewTreeObserver.removeOnDrawListener(descriptionDrawListener)
        }
    }
}

Как видите, после того, как текст задан, он вычисляет, сколько строк можно отобразить целиком, и устанавливает для maxLines это значение. После того, как это будет сделано, isDescriptionListenerAdded имеет значение true, поэтому в GlobalLayoutListener он будет удален, а для него установлено значение false, поэтому он больше никогда не запустится.

...