среда, 7 декабря 2011 г.

Ошибка резидента MotionEvent & SurfaceView

После пары дней пыхтения и потения над отладкой тривиального графического редактора, обкусав ногти до локтей и выщипав все волосы в разных местах выяснил два момента. Может кому пригодится если сумеет найти данный текст.

Первая проблема известна в англоязычных энторнетах как SurfaceView flickering. Так вот если у вас картинка моргает, то почти сто процентов уверенности, что вы накосячили с синхронизацией потоков. Т.е. рисуете вы в одном потоке, а готовите картинку в другом и где-то лопухнулись. Вдумчиво изучите как у вас происходит синхронизация, прокол может быть совершенно неочевидным. Второй вариант - вынести все действия по подготовке картинки в один поток, тот же, что занимается рисованием. Тут нас может подстерегать другая проблема, названия ей я не нашел, но крови она мне попортила изрядно.

Итак, есть у нас обработчик кликов:

boolean onTouchEvent(MotionEvent e)

и есть желание эту обработку перенести в тот же поток, где у нас происходит формирование картинки. К слову сказать, SurfaceView был разработан в частности для того, чтобы эту обработку разнести по разным потокам :), но иногда это все таки может быть нужно. Так вот, делаем мы это элементарно, вариантов много, но все они сводятся к тому чтобы поместить событие в очередь, а в другом потоке эту очередь опросить и обработать. Так вот внимание, сюрприз:

 

Объект MotionEvent e, который получает наш обработчик передавать в другой поток для отложенной обработки НЕЛЬЗЯ. Потому что он существует в единственном экземпляре и в наш обработчик передается лишь ссылка на этот экземпляр. И как только вы выйдете из обработчика (положив эту ссылку в очередь), система с чистой совестью может поместить туда новое значение. В результате, если обработка у нас запаздывает, и мы успели  положить к себе в очередь три события MotionEvent, то все три будут указывать на одно и то же событие.

В результате мы одновременно а) теряем события и б) они у нас дублируются. Я плакаль...

Хрен вы это где найдете в документации. Это наглядный пример результатов телепатической связи между разработчиками Android SDK (которые полагают подобное поведение очевидным) и конечными его пользователями.

Вывод: верить можно только себе, и то, с осторожностью. Т.е. любой системный объект (в данном случае MotionEvent) должен быть обработан там, где он получен  и не следует рассчитывать что он сохранит своё состояние после выхода из обработчика. Если нам нужно сделать "ленивую обработку", то нужно получить "твердую копию", т.е. объект состояние которого полностью контролируется нашей программой, а не системой.

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

Отправить комментарий