Когда последний раз проходил продакшен собеседование

Ответ

Последний раз проходил продакшен собеседование 3 месяца назад. Готовился по системному дизайну, алгоритмам и глубоким знаниям Android. Разбирал кейсы по оптимизации приложений, работе с памятью, многопоточности.

Пример вопроса и ответа:
Как избежать утечек памяти в Activity?

class MyActivity : AppCompatActivity() {
    private val heavyObject = HeavyObject()
    private val handler = Handler(Looper.getMainLooper())

    override fun onDestroy() {
        handler.removeCallbacksAndMessages(null) // очищаем Handler
        heavyObject.cleanup() // освобождаем ресурсы
        super.onDestroy()
    }
}

Использовал WeakReference для колбэков, избегал статических ссылок на контекст.

Ответ 18+ 🔞

А, ну это классика, ёпта! Три месяца назад — это как вчера было, если честно. Я тогда так накрутил себя по системному дизайну и алгоритмам, что, кажется, мог во сне объяснить, чем отличается O(n log n) от O(n^2). А про Андроид так вообще — каждый чих знал, от утечек памяти до того, как правильно потоки синхронизировать, чтобы не было пиздеца.

Вот, например, спрашивают классику: «Как избежать утечек памяти в Activity?». Ну, тут, блядь, целая наука, но если коротко — надо быть не распиздяем.

Смотри, самый простой косяк — это забыть почистить за собой всякие Handler'ы или колбэки. Представь: твоя Activity уже на том свете, а какой-то долбаный Handler всё ещё держит на неё ссылку и ждёт, когда ему сообщение пришлют. И она не может собраться мусорщиком, висит в памяти как призрак, ядрёна вошь! И приложение через час работы начинает лагать и падать, а ты сидишь и думаешь: «Какого хуя?».

Вот смотри на код, тут всё правильно сделано:

class MyActivity : AppCompatActivity() {
    private val heavyObject = HeavyObject()
    private val handler = Handler(Looper.getMainLooper())

    override fun onDestroy() {
        handler.removeCallbacksAndMessages(null) // очищаем Handler
        heavyObject.cleanup() // освобождаем ресурсы
        super.onDestroy()
    }
}

В onDestroy() мы говорим хендлеру: «Братан, всё, отбой, никаких сообщений больше не жди». И тяжёлый объект чистим. Это база. Но это только верхушка айсберга, чувак.

Главная ловушка — это статические ссылки. Вот представим, что у тебя есть какой-нибудь утилитный класс-синглтон, и ты в него для удобства засунул контекст своей активити. Активити закрылась, а синглтон живёт вечно и держит ссылку на мёртвый контекст. Всё, пиши пропало — утечка. Поэтому либо ApplicationContext используй, либо WeakReference. WeakReference — это такая хитрая жопа, которая говорит: «Я на объект ссылаюсь, но если на него больше никто не ссылается, то и я его отпущу, пусть сборщик мусора забирает». Красота!

Ещё одна популярная засада — колбэки. Передал ты в какую-нибудь долгую операцию колбэк, который является внутренним классом и неявно держит ссылку на внешнюю активити. Операция выполняется, а активити уже не нужна, но её не отпускают. Тут тоже либо WeakReference в помощь, либо явно обнулять ссылку на колбэк, когда операция завершилась.

Короче, суть в том, чтобы всегда представлять, кто на кого ссылается. Как только активити должна умереть, все ниточки, которые к ней ведут, должны быть обрезаны. Иначе будет тебе овердохуища проблем, и пользователи начнут писать гневные отзывы, что приложение говно и память жрёт. А ты потом будешь сидеть и дебажить, а там один такой Handler виноват, который ты забыл почистить. Сам от себя охуеешь.