Распутываем легаси-код на Android проекте

от автора

Попадая на новый проект с долгой историей, вас неизбежно ждёт легаси-код. Возможно, проект прошёл через несколько команд, и теперь он в ваших руках. Бывает, что на проекте уже нет контекст-овнера, а на любой вопрос тимлид отвечает: «Так исторически сложилось.»

Приложение может тормозить, состояние определяться десятками мутабельных переменных. Фризы, утечки памяти, файлы на сотни, а то и тысячи строк кода. Год-обжекты. Знакомо?

Я хочу дать несколько советов, которые помогут разобраться в происходящем и распутать спагетти-код.


1. Логирование изменений в базе данных

Все ORM поддерживают возможность логирования запросов. В Room это можно сделать так:

Room.databaseBuilder(context, AppDatabase::class.java, "mydb.db")             .setQueryCallback(                 { sqlQuery, bindArgs ->                     Log.wtf("my_tag", "query: $sqlQuery args: $bindArgs")                 },                 Executors.newSingleThreadExecutor()             )             .build()

Что это даёт? Вы увидите, кто, когда и с какой частотой пишет в базу. На одном из проектов я столкнулся с ситуацией, когда при запуске приложения заново заполнялись десятки таблиц — совершенно ненужная операция, которая «исторически сложилась» и заметно тормозила старт. Логи помогли это обнаружить и устранить.

2. Логирование изменений в SharedPreferences

Встречался код, где в SharedPreferences на главном потоке сохранялся список геоточек. По мере роста списка UI начинал фризить. А в другом случае SharedPreferences использовался как шина событий.

Логирование изменений поможет выявить подобные проблемы:

val listener = SharedPreferences.OnSharedPreferenceChangeListener { _, key ->      Log.wtf("my_tag", "key: $key value: ${prefs.all[key]}")        } prefs.appPreferences.registerOnSharedPreferenceChangeListener(listener)

Не забывайте отписываться от изменений, если они больше не нужны:

prefs.appPreferences.unregisterOnSharedPreferenceChangeListener(listener)

3. Логирование стектрейса вызова

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

object AppLogger {      private const val DEFAULT_TAG = "AppLog"      fun logStack(message: String? = null, tag: String = DEFAULT_TAG) {         val threadName = "Call on Thread:  ${Thread.currentThread().name}\n"         message?.run { Log.wtf(tag, this) }         val stack = Thread.currentThread().stackTrace             .filter { it.className.contains(this::class.java.name).not() }             .filter { it.className.contains(LoggingProperty::class.java.name).not() }             .filter { it.className !in listOf("dalvik.system.VMStack", "java.lang.Thread") }             .joinToString(prefix = threadName, separator = "\n") { element ->                 "at ${element.className}.${element.methodName} (${element.fileName}:${element.lineNumber})"             }         Log.wtf(tag, stack)     } }

Эффект усиливается, если добавить логирование в геттер и сеттер мутабельной переменной:

var currentLocation: GeoPoint? = null         get() {             AppLogger.logStack("Get currentLocation $field")             return field         }         set(value) {             AppLogger.logStack("Set currentLocation value: $value field: $field")             field = value         }

Можно сделать ещё элегантнее с помощью делегата:

class LoggingProperty<T>(private var value: T) {     operator fun getValue(thisRef: Any?, property: KProperty<*>): T {         AppLogger.logStack()         return value     }      operator fun setValue(thisRef: Any?, property: KProperty<*>, newValue: T) {         AppLogger.logStack()         value = newValue     } }

Тогда переменная будет выглядеть так:

var currentLocation: GeoPoint? by LoggingProperty(null)

Такой подход сильно упрощает работу с кодом, где переменные меняются хаотично, а логика запутана. Эти три совета — мой проверенный арсенал для погружения в легаси-код. Они помогают быстро найти узкие места и начать приводить проект в порядок. Но я уверен, у вас есть свои практики! Делитесь ими в комментариях — будет интересно обсудить и, возможно, дополнить этот список.


ссылка на оригинал статьи https://habr.com/ru/articles/895236/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *