25 November 2015

Как иногда плохой код и антипаттерн решают

JavaDevelopment for Android
Привет %username%!

Сегодня я хотел бы рассказать о том, как мне помог в проекте плохой код.

Кому интересно, прошу под кат.

Достался мне в наследство проект на сопровождение. Не знаю кем писался, но этому человеку икается и по сей день. Суть такая — есть приложение для сбора фотоотчетов в магазине и их отправке руководству на сервак.

Все вроде просто, но были постоянные жалобы сотрудников на падение приложения при фотографировании. Анализ кода показал, что при вызове intent приложения камеры, Android убивал приложение из-за нехватки ресурсов и по возвращению результата в активити, последняя пересоздавалась и не содержала нужных для работы данных. Пересоздание активити просто не обрабатывалось. Ну да, есть же configChanges *sarcasm*

Шаг 1. Обработка пересоздания


Помучавшись 2 дня, поправив код, выкинув повторяющиеся фрагменты я принялся тестировать. Закинул в эмулятор taskkiller, запустил камеру, убил процесс. Фоткаю — красота!!! Все красиво, фотка есть. Нажимаю сохранить и… бабах) Приложение упало.

Шаг 2. Singleton — злое зло


Причина падения — нет данных о текущем отчете. Оказалось, что в приложении был чудо-синглтон, который хранил в себе ВСЕ! Абсолютно все критические данные, необходимые для работы приложения и даже больше. Это и токен авторизации, и 40 полей состояния отчета и ссылки на классы, описывающие отчет и геоданные юзера и еще пару коллекций Bitmap. В одно мгновение мы лишились всего, чего только можно было лишиться.

Результат был описан заказчику. Рефакторинг оценен, но столько денег у них не оказалось. При этом слезно попросили что-то сделать и наставить костылей.

Шаг 3. Меняем Singleton


Все, что пришло в голову — исправить синглтон так, чтобы он стал надежным и при этом абсолютно не поменялся его интерфейс. Придумано было следующее — все что только можно запихнуть в SharedPreferenses сохранялось туда.
public String getAuthToken() {
        return getStingFromPref(AUTH_TOKEN);
}
.....
// и так далее.


Грубо, тупо, зато не нужно менять код во всем проекте и наш Singleton стал более устойчив (остались коллекции Bitmap, с которыми пока ничего не сделал). Все объекты, которые только можно сериализовать были сериализованы и тоже запихнулись в SharedPreferences.

Приложение ушло на тест, в прод и падения прекратились на 70% устройств. На остальных после отправки результата с камеры, пересоздавалась не текущая, а предыдущая активити в стеке. Для меня это пока загадка. Гуру — объясните в комментах.
Суть такая — Активити А стартует Активити Б с получением результата. Активити Б запускает intent камеры с получением результата.
Когда в процессе работы камеры процесс убивался, по созданию фотки пересоздавалась активити А. У активити Б не вызывался ни onCreate, ни onActivityResult. (P.S. Android 4.4)

goоgle и stackowerflow ответа не дали, пришлось идти другим путем и писать больше плохого кода.

Если пересоздать не получается, можно запретить убивать, подумал я и перешел к шагу 4.

Шаг 4. Финальный. Foreground-мусоровоз


Как же заставить Android не убивать процесс? Мои мысли были такими: «Если сказать операционке, что у нас есть важный foreground сервис, выполняющий важные нам вычисления ровно столько времени, сколько мы фоткам товар. Это определенно может сработать».

Так родился в проекте foreground-мусоровоз. Он гонял цикл от 0 до 60 и в теле засыпал на секунду. Как только приходил результат с камеры — сервис убивался. Приложение было собрано и отдано на тестирование.
Результат — KitKat оказался умнее и прибивал процесс. То-есть не изменилось ничего.

Я уже было отчаялся, когда мне в голову пришла еще одна мысль: «А если тупо сохранить ссылку на активити в синглтоне и вызвать startActivityForResult прямо из foreground-мусоровоза?». Я никогда так не делал и, надеюсь, больше не придется.

Приложение собрано, отдано в тест и… о чудо, процесс больше не закрывался.

Вместо заключения. Заказчик предупрежден, что у него плохое приложение и я сделал его еще хуже, но оно работает так как этого ждут сотрудники компании.

Напоследок. Расскажите про свой самый «плохой код» в карьере. Давайте поднимем друг другу настроение.
Tags:javaandroidкрасивый код
Hubs: Java Development for Android
+38
26.3k 53
Comments 70