Pull to refresh

Отслеживание утечек памяти в Android приложениях

Reading time 3 min
Views 8.3K
На самом деле, данная статья является продолжением данного топика: Анализ памяти для Android приложений. Всем, кто хочет изучить вопрос детально, будут рады под катом.

Итак, поехали...


Анализировать состояние памяти с помощью инструментов, описанных в данной статье, вещь очень нужная и полезная, но не всегда это делать удобно и уместно.
Было бы неплохо, иметь возможность отслеживать memory leaks без каких-либо сторонних инструментов, чтобы даже неопытный тестер смог отыскать утечки памяти без доступа к компьютеру.

Как это сделать


На помощь нам придет особенность класса WeakReference.
Ниже приведен пример класса, который будет помогать отслеживать утечки памяти. На оригинальность данный класс не претендует, и, возможно, кто-то уже даже использует в своем проекте что-то подобное. Класс по понятным причинам был упрощен и слегка модифицирован, но свою основную функцию он выполняет. Скажу лишь, что для большего удобства, в результирующий список можно добавить как минимум количество «утекших» объектов.

    package com.company.product;

    import java.lang.ref.WeakReference;
    import java.util.Collections;
    import java.util.Vector;

    import android.content.Context;
    import android.content.Intent;
    import android.text.TextUtils;


    public class LeaksManager {

        private static LeaksManager mThis = null;
        private final Vector<WeakReference<Object>> mRefs = new Vector<WeakReference<Object>>();

        private LeaksManager() {
            super();
        }

        public static LeaksManager getThis() {
            if (mThis == null) {
                mThis = new LeaksManager();
            }
            return mThis;
        }
       
        public <T> T monitorObject(T obj) {
            if (obj == null) {
                return obj;
            }
           
            for (WeakReference<Object> ref : mRefs) {
                if (ref.get() == obj) {
                    return obj;
                }
            }
           
            mRefs.add(new WeakReference<Object>(obj));
           
            return obj;
        }
       
        public Vector<String> checkLeaks() {
            System.gc();
           
            Vector<String> names = new Vector<String>();
           
            for (int i = mRefs.size() - 1; i >= 0; i--) {
                WeakReference<Object> ref = mRefs.elementAt(i);
                Object obj = ref.get();
                if (obj != null) {
                    String className = obj.getClass().getSimpleName();
                    addUniqueClassName(names, TextUtils.isEmpty(className) ? "Unknown class name" : className);
                }
                else {
                    mRefs.remove(i);
                }
            }
           
            mRefs.trimToSize();
           
            return names;
        }
       
        private void addUniqueClassName(Vector<String> names, String className) {
            int index = -1;
            for (int j = 0; j < names.size(); j++) {
                if (names.elementAt(j).equals(className)) {
                    index = j;
                    break;
                }
            }
           
            if (index == -1) {
                names.add(names.getClass().getSimpleName());
            }
        }
    }
    


Как этим пользоваться


Для начала придется написать простую Activity, которая будет брать и отображать список строчек возвращаемый функцией LeaksManager.checkLeaks(). Данная Activity очень простая, поэтому не вижу большого смысла приводить ее код.

Далее, каждый объект, который необходимо отслеживать, мы добавляем в LeaksManager следующим образом:
Object obj = LeaksManager.getThis().monitorObject(new Object());


Далее, в удобном для вас месте, вы размещаете вызов Activity со списком memory leaks, и, если список пустой, то все хорошо, а если в нем что-то осталось даже после нескольких запусков этой самой Activity, то есть повод задуматься.

В общем-то, это все, что нужно сделать.

Комментарии


Отслеживать все создаваемые объекты нет никакого смысла — большинство объектов привязаны к контексту, и поэтому для них вполне достаточно вызывать monitorObject() в onCreate() каждой Activity, сервиса ну и возможно в конструкторе потока.

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

P.S.


Надеюсь эта статья будет кому-нибудь полезна.
Если данная статья будет интересна, то в следующей статье я поведаю о хитрой особенности класса AsyncTask
Tags:
Hubs:
+19
Comments 13
Comments Comments 13

Articles