Какие инструменты и методы Android лучше всего подходят для поиска утечек памяти/ресурсов?

У меня появилось приложение для Android, и я нахожусь в разработке приложений для телефона, где все, кажется, работает хорошо, и вы хотите объявить победу и отправить, но вы знаете, что просто нужно быть какой-то памятью и утечки ресурсов там; и там всего 16 мб кучи на Android и, по-видимому, удивительно легко утечка в приложении для Android.

Я смотрел вокруг и до сих пор мог только выкопать информацию о 'hprof' и 'traceview', и не получил много положительных отзывов.

Какие инструменты или методы вы столкнулись или разработали и можете поделить, возможно, в проекте ОС?

Ответ 1

Одной из наиболее распространенных ошибок, которые я обнаружил при разработке приложений для Android, является ошибка "java.lang.OutOfMemoryError: Bitmap Size Exceeds VM Budget". Я быстро обнаружил эту ошибку при работе с большим количеством растровых изображений после изменения ориентации: активность уничтожена, снова создана, а макеты "завышены" из XML, потребляющего память VM, доступную для растровых изображений.

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

Сначала установите атрибут "id" в родительском представлении вашего XML-макета:

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="fill_parent"
     android:layout_height="fill_parent"
     android:id="@+id/RootView"
     >
     ...

Затем в методе onDestroy() вашей операции вызовите метод unbindDrawables(), передающий подтверждение родительскому представлению, а затем выполните команду System.gc()

    @Override
    protected void onDestroy() {
    super.onDestroy();

    unbindDrawables(findViewById(R.id.RootView));
    System.gc();
    }

    private void unbindDrawables(View view) {
        if (view.getBackground() != null) {
        view.getBackground().setCallback(null);
        }
        if (view instanceof ViewGroup) {
            for (int i = 0; i < ((ViewGroup) view).getChildCount(); i++) {
            unbindDrawables(((ViewGroup) view).getChildAt(i));
            }
        ((ViewGroup) view).removeAllViews();
        }
    }

Этот метод unbindDrawables() исследует дерево представления рекурсивно и:

  • Удаляет обратные вызовы во всех фоновых рисунках
  • Удаляет дочерние элементы во всех группах просмотра

Ответ 3

В основном для путешественников Google в будущем:

Большинство java-инструментов, к сожалению, не подходят для этой задачи, потому что они анализируют только JVM-кучу. Однако у каждого приложения Android также есть своя куча, которая также должна соответствовать пределу ~ 16 МБ. Например, он обычно используется для растровых данных. Таким образом, вы можете легко запускать ошибки Out Of Memory, даже если ваша JVM-куча - chillin около 3 МБ, если вы используете множество чертежей.

Ответ 4

Ответ от @hp.android работает хорошо, если вы просто работаете с растровыми фонами, но в моем случае у меня был BaseAdapter набор ImageView для GridView. Я изменил метод unbindDrawables(), как указано, так что условие:

if (view instanceof ViewGroup && !(view instanceof AdapterView)) {
  ...
}

но проблема в том, что рекурсивный метод никогда не обрабатывает дочерние элементы AdapterView. Чтобы решить эту проблему, я сделал следующее:

if (view instanceof ViewGroup) {
  ViewGroup viewGroup = (ViewGroup) view;
  for (int i = 0; i < viewGroup.getChildCount(); i++)
    unbindDrawables(viewGroup.getChildAt(i));

  if (!(view instanceof AdapterView))
    viewGroup.removeAllViews();
}

так что дочерние элементы AdapterView все еще обрабатываются - метод просто не пытается удалить всех дочерних элементов (который не поддерживается).

Это не совсем исправляет проблему, поскольку ImageView управляет растровым изображением, которое не является их фоном. Поэтому я добавил следующее. Он не идеален, но он работает:

if (view instanceof ImageView) {
  ImageView imageView = (ImageView) view;
  imageView.setImageBitmap(null);
}

В целом метод unbindDrawables():

private void unbindDrawables(View view) {
  if (view.getBackground() != null)
    view.getBackground().setCallback(null);

  if (view instanceof ImageView) {
    ImageView imageView = (ImageView) view;
    imageView.setImageBitmap(null);
  } else if (view instanceof ViewGroup) {
    ViewGroup viewGroup = (ViewGroup) view;
    for (int i = 0; i < viewGroup.getChildCount(); i++)
    unbindDrawables(viewGroup.getChildAt(i));

    if (!(view instanceof AdapterView))
      viewGroup.removeAllViews();
  }
}

Я надеюсь, что существует более принципиальный подход к освобождению таких ресурсов.

Ответ 5

Хорошие разговоры о вводе/выводе Google (2011) по управлению памятью в Android, а также подробные сведения о инструментах + методы для профилирования памяти:
http://www.youtube.com/watch?v=_CruQY55HOk

Ответ 7

Ну, это те инструменты, которые связаны с уникальными форматами, которые использует Android. Я думаю, что вы можете быть неудовлетворены тем, что используется базовая структура кода тестирования.

Вы пробовали тестировать области кода с помощью Android Mock Framework?