Quantcast
Channel: Найцікавіше на DOU
Viewing all articles
Browse latest Browse all 8115

Невидимые лики (Android, memory management)

$
0
0

Дано: Android апликейшн с аудиторией 10 млн человек. Crashlytics для трекинга крешей.

Топ 1% крешей выглядят так:

или так:

или еще десятком разных представлений, но все они — OutOfMemory креши.

Была проведена работа по анализу существующих memory leaks в приложении, и все они были устранены. Счастье наступило, но было недолгим. Крешей стало меньше, но они не ушли.

Кардиограмма студии:

Дамп хипа в Memory Analyzer tool (www.eclipse.org/mat/)

Монитор спокоен:

adb shell dumpsys activity activities com.app_name | grep "Running activities" -A 30 | head -30 

говорит: «Узбагойся»:

Task #4 — запущена всего одна активити. Но креши-то не ушли... Заставляет задуматься.

Хорошо, что есть умные люди в разных гуглах, которые пишут умные статьи вроде этой.

Посмотрим, что говорит:

adb shell dumpsys meminfo com.app_name

После минуты работы приложения при одной открытой активити в стеке:

Wow! 560 views и 8 activities... Wow, черт побери... что-то тут не так!

Начинаем бисектить код в живой активити.

Результат:

1. Найден код в стиле:

mHomeView.postDelayed(new Runnable() {
    @Override
    public void run() {
        bla-bla-bla
    }
}, 
время_в будущем_АКТУАЛЬНОЕ_после закрытия_активити);

Казалось бы, всё в порядке — если активити дестроится, его вью, которые не держатся чем-то извне, должны уничтожиться тоже, предварительно почистив колбеки. Но не тут-то было. Данный код приводит к утечке всей активити. Лечится либо выносом runnable в мембер класса с последующим удалением, либо переносом логики в

mHandler = new Handler();
...
    mHandler.postDelayed(new Runnable() {
    @Override
    public void run() {
...
    }
},

то_же_время);

с очисткой в виде

mHandler.removeCallbacksAndMessages(null);

в onDestroy или onStop. Хотя по сути postDelayed на вью и хендлер должны быть эквивалентны, если веритьуважаемым людям в мире Android.

2. Некоторое время назад один уважаемый человек индусской национальности вкрутил 3rd-party библиотеку для реализации shimmer эффекта (glow над текстом). Что-то вроде https://github.com/RomainPiel/Shimmer-android

Всё работает отлично. Видимых ликов нет. В коде библитеки найден следующий код:

mAnimator = ObjectAnimator.ofFloat(shimmerView, "gradientX", fromX, toX);
mAnimator.setRepeatCount(mRepeatCount);
mAnimator.setDuration(mDuration);
...
mAnimator.start();

Выглядит подозрительно:
— динамическая установка значений вью через рефлекшн каким-то делегатом;
— передаём вью куда-то в странного вида функцию.

В коде фреймворка в классе ObjectAnimatorработа с вью выглядит безопасно:

mTarget = target == null ? null : new WeakReference<Object>(target);
…
   @Nullable
    public Object getTarget() {
        return mTarget == null ? null : mTarget.get();
    }
...
    final Object oldTarget = getTarget();
    if (oldTarget != target) {
...
    }

Утечек быть не должно...

В процессе разрушения исследуемой активити вызывается mAnimator.cancel(), который должен остановить анимацию.

Шок: Анимация НИКОГДА не останавливается. Были опробованы разные методы вида:

mAnimator.cancel();
mAnimator.end();
shimmerView.clearAnimation();

и другие извращения. Не помогло ничего.

Не мы одни такие:
stackoverflow.com/...snt-always-work
stackoverflow.com/...l-does-not-work

Модифицируем код, чтобы избавиться от передачи вью в аниматор. Вуаля! Memory leaks gone!

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

Выводы:
— Не верь глазам своим. Что касается памяти — к сожалению, разные инструменты показывают разные вещи, и неизвестно, чему верить.
— манипулировать явно или неявно объектами UI фреймворка (в нашем случае views) — обычно очень плохая идея.
— При разработке приложений для Android проверяйте вывод:

watch -n 1 adb shell dumpsys meminfo com.app_name

Cудя по всему, это единственный объективный источник метрик памяти и объектов UI фреймворка, на который можно полагаться при разработке.

Удачи!


Viewing all articles
Browse latest Browse all 8115

Trending Articles