Предупреждение: не размещайте классы контекста Android в статических полях; это утечка памяти (а также прерывает Instant Run)

Android Studio:

Не размещайте классы контекста Android в статических полях; это утечка памяти (а также прерывает Instant Run)

Итак, 2 вопроса:

# 1 Как вы вызываете startService из статического метода без статической переменной для контекста?
# 2 Как вы отправляете localBroadcast из статического метода (такого же)?

Примеры:

public static void log(int iLogLevel, String sRequest, String sData) {
    if(iLogLevel > 0) {

        Intent intent = new Intent(mContext, LogService.class);
        intent.putExtra("UPDATE_MAIN_ACTIVITY_VIEW", "UPDATE_MAIN_ACTIVITY_VIEW");
        mContext.startService(intent);
    }
}

или

        Intent intent = new Intent(MAIN_ACTIVITY_RECEIVER_INTENT);
        intent.putExtra(MAIN_ACTIVITY_REQUEST_FOR_UPDATE, sRequest));
        intent.putExtra(MAIN_ACTIVITY_DATA_FOR_VIEW, sData);
        intent.putExtra(MAIN_ACTIVITY_LOG_LEVEL, iLogLevel);
        LocalBroadcastManager.getInstance(mContext).sendBroadcast(intent);

Каким будет правильный способ сделать это, не используя mContext?

ПРИМЕЧАНИЕ. Я думаю, что мой главный вопрос может состоять в том, как передать контекст классу, из которого живет вызывающий метод.

Ответ 1

Просто передайте его как параметр вашему методу. Нет смысла создавать статический экземпляр Context исключительно для запуска Intent.

Вот как должен выглядеть ваш метод:

public static void log(int iLogLevel, String sRequest, String sData, Context ctx) {
    if(iLogLevel > 0) {

        Intent intent = new Intent(ctx, LogService.class);
        intent1.putExtra("UPDATE_MAIN_ACTIVITY_VIEW", "UPDATE_MAIN_ACTIVITY_VIEW");
        ctx.startService(intent);
    }
}

Обновление от комментариев к вопросу: каскадируйте контекст из инициирующей активности (через параметры конструктора или параметры метода) вплоть до нужной вам точки.

Ответ 2

Просто убедитесь, что вы передаете context.getApplicationContext() или вызываете getApplicationContext() в любом контексте, который передается через методы/конструктор в ваш синглтон, если вы решили сохранить его в любом поле участника.

Пример доказательства идиота (даже если кто-то передаст в действие, он захватит контекст приложения и использует его для создания экземпляра сингла):

public static synchronized RestClient getInstance(Context context) {
    if (mInstance == null) {
        mInstance = new RestClient(context.getApplicationContext());
    }
    return mInstance;
}

getApplicationContext() в соответствии с документами: "Вернуть контекст единого глобального объекта приложения текущего процесса".

Это означает, что контекст, возвращаемый функцией getApplicationContext(), будет проходить через весь процесс, и, следовательно, не имеет значения, храните ли вы статическую ссылку на него где угодно, поскольку она всегда будет присутствовать во время выполнения вашего приложения (и переживать любые объекты/синглэты, созданные им).

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

Ссылка на деятельность по ее контексту должна жить в том же жизненном цикле, что и сама деятельность, в противном случае она будет удерживать контекстную заложницу, вызывающую утечку памяти (что является причиной предупреждения линта).

РЕДАКТИРОВАТЬ: К парню, который избивает пример из вышеперечисленных документов, есть даже раздел комментария в коде о том, что я только что написал:

    // getApplicationContext() is key, it keeps you from leaking the
    // Activity or BroadcastReceiver if someone passes one in.

Ответ 3

Это просто предупреждение. Не волнуйся. Если вы хотите использовать контекст приложения, вы можете сохранить его в классе "singleton", который используется для сохранения всего одноэлементного класса в вашем проекте.

Ответ 4

В вашем случае не имеет смысла иметь это как статическое поле, но я не думаю, что это плохо во всех случаях. Если вы теперь делаете, вы можете иметь статическое поле, имеющее контекст, а затем - null. Я создаю статический экземпляр для моего основного класса модели, в котором есть контекст внутри, его контекст приложения, а не контекст активности, а также у меня есть статическое поле экземпляра класса, содержащего Activity, в котором я null in on destroy. Я не вижу, что у меня утечка памяти. Поэтому, если какой-нибудь умный парень думает, что я ошибаюсь, не стесняйтесь комментировать...

Также Instant Run работает здесь отлично...

Ответ 5

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

Теперь есть два сценария, в которых вы получите это предупреждение. Для экземпляра (наиболее очевидного):

public static Context ctx;

И тогда там бит более сложный, где контекст завернут в класс:

public class Example{
    public Context ctx;
    //Constructor omitted for brievety 
}

И этот класс определяется как статический где-то:

public static Example example;

И вы получите предупреждение.

Само решение довольно просто: Не размещать контекстные поля в статических экземплярах, будь то класс упаковки или объявлять его статическим.

И решение для предупреждения прост: не ставьте поле статически. В вашем случае передайте контекст как экземпляр метода. Для классов, в которых выполняется несколько вызовов контекста, используйте конструктор для передачи контексту (или активности по этому вопросу) в класс.

Обратите внимание, что это предупреждение, а не ошибка. Если вам по какой-то причине нужен статический контекст, вы можете это сделать. Хотя вы создаете утечку памяти, когда вы это делаете.Суб >

Ответ 6

Если вы убедитесь, что это контекст приложения. Это имеет значение. Добавьте это

@SuppressLint("StaticFieldLeak")