Статический способ получить «Контекст» в Android?

Есть ли способ получить текущий экземпляр Context внутри статического метода?

Я ищу этот способ, потому что ненавижу сохранять экземпляр «Контекст» каждый раз, когда он изменяется.


person Andrea Baccega    schedule 04.01.2010    source источник
comment
Отказ от сохранения контекста - хорошая идея не только потому, что это неудобно, но еще и потому, что это может привести к огромным утечкам памяти!   -  person Vikram Bodicherla    schedule 30.04.2012
comment
@VikramBodicherla Да, но ответы ниже предполагают, что мы говорим о контексте приложения. Таким образом, утечки памяти не являются проблемой, но пользователь должен использовать эти решения только в том случае, если это правильный контекст для использования.   -  person Tom    schedule 31.03.2013
comment
Если вам нужно использовать статический способ получения Context, то может быть лучший способ разработать код.   -  person Anonsage    schedule 20.02.2015
comment
В документации Android рекомендуется передавать контекст получателям синглетонов. developer.android.com/reference/android/app/Application.html   -  person Marco Luglio    schedule 14.07.2015
comment
Если вы предпочитаете синглтоны и контекст, передаваемый с помощью getInstance (), вместо статического контекста, пожалуйста, посмотрите, я попытался объяснить мои рассуждения здесь, поддерживаемые рабочим кодом: stackoverflow.com/a/38967293/4469112   -  person Alessio    schedule 28.02.2017
comment
Если вам нужен статический контекст, вам следует подумать, что, возможно, у вас есть проблема с дизайном. Проверьте шаблоны проектирования Factory и, как сказал @VikramBodicherla, огромные утечки памяти!   -  person tricknology    schedule 19.06.2017
comment
@Tom Какие еще типы контекста могут быть здесь проблемой? Кажется, контекст приложения не проблема. Какие бывают тогда типы?   -  person DADi590    schedule 16.12.2020
comment
Context и ApplicationContext одинаковы, но один из них тематический. Когда static код требует Context, лучше игнорируйте эти так называемые решения и спросите себя, как правильно их передать.   -  person Martin Zeitler    schedule 12.01.2021


Ответы (20)


Сделай это:

В файле манифеста Android заявите следующее.

<application android:name="com.xyz.MyApplication">

</application>

Затем напишите класс:

public class MyApplication extends Application {

    private static Context context;

    public void onCreate() {
        super.onCreate();
        MyApplication.context = getApplicationContext();
    }

    public static Context getAppContext() {
        return MyApplication.context;
    }
}

Теперь везде вызывайте MyApplication.getAppContext(), чтобы получить статический контекст вашего приложения.

person Rohit Ghatol    schedule 25.02.2011
comment
Есть ли у этого метода недостатки? Это похоже на обман. (Взлом?) - person jjnguy; 07.07.2011
comment
Обратной стороной является то, что нет гарантии, что нестатический onCreate () будет вызван до того, как какой-либо статический код инициализации попытается получить ваш объект Context. Это означает, что ваш вызывающий код должен быть готов иметь дело с нулевыми значениями, которые как бы нарушают всю суть этого вопроса. - person Melinda Green; 19.10.2011
comment
Также может быть .. следует ли нам объявить эту static context переменную как volatile? - person Vladimir Sorokin; 31.03.2012
comment
@VladimirSorokin: ключевое слово volatile не требуется, поскольку контекст всегда относится к одному и тому же объекту. Значение указателя можно сделать окончательным, поскольку оно никогда не меняется после назначения. - person RaphMclee; 26.07.2012
comment
@RaphMclee: Вы не можете сделать его окончательным, потому что он инициализирован не в c-tor. Тем не менее, переменная context может быть доступна для нескольких потоков. А согласно Java Concurrency In Practice - 3.5.3. Безопасные идиомы публикации: для безопасной публикации ссылки вы должны использовать один из следующих подходов: статический инициализатор, изменчивое поле, AtomicReference, последнее поле, блокировка. В противном случае у вас могут возникнуть проблемы с параллелизмом. Но я не на 100% уверен в нашем конкретном случае, есть разные тонкие нюансы, поэтому я решил использовать volatile на всякий случай, это все равно не влияет на производительность. - person Vladimir Sorokin; 29.07.2012
comment
Кто-нибудь знает причину установки переменной контекста в onCreate () вместо конструктора? - person Kiirani; 29.11.2012
comment
@Kiirani, наверное, потому что раньше его не было? Вы можете получить нулло. - person GorillaApe; 25.03.2013
comment
@MelindaGreen Я не согласен. Если у вас есть статические атрибуты, которые инициализируются статикой, вы уже знаете, что делаете что-то, что зависит от порядка инициализации, и вам нужно быть осторожным. В большинстве случаев вы просто не будете использовать это таким образом. - person Tom; 31.03.2013
comment
@nerith Создайте «суррогатный» класс в коде общей библиотеки, а затем установите Surrogate.context из App.onCreate (). Я также делаю это для флага BuildConfig.DEBUG, поскольку он недоступен из кода библиотеки. - person Tom; 31.03.2013
comment
@Tom Это не тот случай, когда статический член данных изначально статичен. В данном коде статический член инициализируется нестатически в onCreate (). Даже статически инициализированные данные в этом случае недостаточно хороши, потому что ничто не гарантирует, что статическая инициализация данного класса произойдет до того, как к нему будет осуществлен доступ во время статической инициализации какого-либо другого класса. - person Melinda Green; 01.04.2013
comment
@MelindaGreen Я согласен с только что сделанным вами заявлением. По той причине, которую вы указали, я просто не буду ссылаться на getApplicationContext в статическом инициализаторе. OP хочет получить доступ к контексту через статический метод, но я не интерпретирую это как то, что он хочет иметь возможность использовать этот статический метод в статическом инициализаторе. - person Tom; 01.04.2013
comment
@Tom Опять же, речь идет не о статической инициализации, а о порядке инициализации в целом. Предлагаемое решение просто устанавливает состояние гонки. Если это в целом работает, это похоже на удачу (то есть невезение). И это обратная сторона, которую я описал в моем первом комментарии, а именно то, что в целях безопасности вызывающие абоненты должны быть готовы к получению нулевых значений. К сожалению, это похоже на проблему, которую OP пытался решить. В зависимости от ситуации, возможно, OP не осознавал, что Activity и Services являются контекстами, и, возможно, не имел никаких проблем с самого начала. - person Melinda Green; 30.04.2013
comment
@MelindaGreen Согласно документации для Application, onCreate () вызывается до создания любого действия, службы или получателя (за исключением поставщиков контента). Разве это решение не будет безопасным, если вы не пытаетесь получить доступ к getAppContext () от поставщика контента? - person Magnus W; 10.02.2014
comment
@BadCash Я думаю, что в каком-то смысле это безопасно, потому что фреймворк гарантирует, что onCreate будет вызываться для каждого Activity, и к тому времени контекст приложения также будет создан. Однако с точки зрения обслуживания есть несколько предостережений, о которых вы должны знать, а именно: вы должны быть готовы получить нулевые значения в зависимости от того, откуда вы вызываете эту функцию. (т. е. если вы вызываете ее из конструктора, возможно, статическая переменная контекста еще не инициализирована.) Итак, вы всегда должны проверять значение null, чтобы быть уверенным. Лично я бы так не поступил. - person hopia; 10.02.2014
comment
@BadCash, спасибо, что указали на документацию. В нем заранее сказано, что обычно нет необходимости создавать подклассы от Application, потому что синглтоны, вероятно, лучше. Затем он говорит, что для реализации такого синглтона ваша функция для получения экземпляра должна пройти в его контексте, в котором инициализация синглтона вызывает getApplicationContext (). Таким образом, при первом вызове будет установлен синглтон, и вызывающим абонентам не придется беспокоиться о получении нулей. К сожалению, это означает, что у них должен быть контекст, что и было первоначальной проблемой. Ответ Эриха, приведенный ниже, является правильным. - person Melinda Green; 15.02.2014
comment
Будет ли вызываться метод onCreate при вызове приложения с трансляцией android.intent.action.BOOT_COMPLETED? похоже, это было корнем моих проблем. Я ожидал, что вызовет onCreate, но по какой-то причине это не так. При использовании вашего раствора в некоторых случаях следует соблюдать осторожность. - person Rouz; 16.10.2014
comment
@TeeTracker В этом случае утечки не будет, потому что объект Application жив до тех пор, пока живо само приложение, то есть он не собирает мусор в течение всего времени существования приложения. - person npace; 11.08.2015
comment
Существуют явные случаи, когда у приложения есть поставщик содержимого, в котором поставщик содержимого инициализируется до объекта приложения, и поэтому вы не можете рассчитывать на инициализацию статического поля в этом случае. - person Jerry Brady; 24.09.2015
comment
Пока вы не инициализируете классы, которые будут статически пытаться ссылаться на статически сохраненный контекст, делать это в подклассе Application (который определяется как приложение в AndroidManifest.xml, безопасно, потому что система Android гарантирует, что ваше приложение instance .onCreate будет первым, что вызывается перед чем-либо еще. Таким образом, хотя это и небезопасный способ сделать это в обычном java, это было бы похоже на установку статического поля в обычном основном java ... Безопасно, если вы не делают ужасных статических вещей с другими статическими инициализированными полями. - person Ajax; 15.07.2016
comment
Это решение вызывает предупреждение Android Lint Performance, поскольку контексты Android, помещенные в статические поля, представляют собой утечку памяти ... Вы можете проверить это сами в Android Studio: Анализировать ›Проверить код ... - person MahNas92; 11.01.2018
comment
Прискорбно видеть, сколько голосов получил этот ответ. Никогда не следует привязать статический экземпляр к контексту - для доказательства попробуйте использовать Leak Canary (github.com/square/leakcanary) и найдите вызванную этим утечку памяти. @ people-with-enouhg-rep-and-Android-knowledge, еще раз просмотрите этот ответ и действуйте соответственно. Это гарантированно будет использоваться новичками, и это просто неправильно. - person Crearo Rotar; 01.05.2018
comment
@cyrilchampier Я сделал библиотеку, которая работает с библиотеками: github.com/LouisCAD /Splitties/blob/master/appctx/README.md Он использует пустышку ContentProvider для получения сейфа Context - person Louis CAD; 15.06.2018
comment
В современном духе вещи, которые должны быть простыми, должны быть легкими, действительно должен быть статический метод Context.getApplicationContext () или Application.getApplicationContext (). Этот объект является синглтоном и должен быть доступен как таковой, не прыгая через обручи. Пока это не будет решено, за пределами статического инициализатора или поставщика контента, то есть в 99% моего кода, этот ответ обеспечивает разумный обходной путь. Спасибо комментаторам, указавшим на те конкретные случаи, когда эта техника небезопасна. - person Ian Lovejoy; 06.07.2018
comment
@CrearoRotar На самом деле это не так, хранение статической ссылки на контекст приложения, на мой взгляд, вовсе не плохая практика, утечек памяти не будет, потому что система Android уже убила бы ваш процесс, хотя я согласен, что это следует использовать с осторожностью. - person Sarthak Mittal; 20.08.2018
comment
@MelindaGreen - re К сожалению, это означает, что у них должен быть контекст, что и было исходной проблемой. Да и нет. Чтобы избежать null, необходимо сначала запустить some код, который имеет some Context - так что по-настоящему универсального решения, как говорит Эрих, и вы упомянули, не существует. OTOH, возможно можно упорядочить свой код так, чтобы некоторый контекст существовал достаточно рано. Если это так, то какой бы ни был контекст, он должен вызывать myContext.getApplicationContext и сохранять его в этом статическом глобальном. Вы знаете, есть ли какой-либо контекст до Application.onCreate? - person ToolmakerSteve; 04.01.2020
comment
@ToolmakerSteve, нет, я не знаю, но я предлагаю вам не рассчитывать на это, если в документации явно не указано, что вы можете. - person Melinda Green; 04.01.2020
comment
Внимание: в некоторых редких случаях этот блок кода не будет вызываться, что может вызвать сбой NPE: когда данные приложения восстанавливаются из облака, Application.onCreate () не вызывается при следующем запуске. stackoverflow.com/a/60597718/1333448 - person Yves Delerm; 09.03.2020

Большинство приложений, которым нужен удобный метод получения контекста приложения, создают свой собственный класс, расширяющий _1 _.

РУКОВОДСТВО

Вы можете добиться этого, сначала создав класс в своем проекте, как показано ниже:

import android.app.Application;
import android.content.Context;

public class App extends Application {

    private static Application sApplication;

    public static Application getApplication() {
        return sApplication;
    }

    public static Context getContext() {
        return getApplication().getApplicationContext();
    }

    @Override
    public void onCreate() {
        super.onCreate();
        sApplication = this;
    }
}

Затем в вашем AndroidManifest вы должны указать имя своего класса в теге AndroidManifest.xml:

<application 
    ...
    android:name="com.example.App" >
    ...
</application>

Затем вы можете получить контекст приложения любым статическим методом, используя следующее:

public static void someMethod() {
    Context context = App.getContext();
}

ВНИМАНИЕ

Прежде чем добавлять что-то подобное в свой проект, вы должны подумать о том, что говорится в документации:

Обычно нет необходимости создавать подклассы Application. В большинстве случаев статические синглтоны могут предоставлять ту же функциональность более модульным способом. Если вашему синглтону нужен глобальный контекст (например, для регистрации широковещательных приемников), функции для его получения может быть задан Context, который внутренне использует Context.getApplicationContext () при первом построении синглтона.


ОТРАЖЕНИЕ

Есть еще один способ получить контекст приложения с помощью отражения. На отражение в Android часто смотрят свысока, и я лично считаю, что его не следует использовать в продакшене.

Чтобы получить контекст приложения, мы должны вызвать метод скрытого класса (ActivityThread), доступный с API 1:

public static Application getApplicationUsingReflection() throws Exception {
    return (Application) Class.forName("android.app.ActivityThread")
            .getMethod("currentApplication").invoke(null, (Object[]) null);
}

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

public static Application getApplicationUsingReflection() throws Exception {
    return (Application) Class.forName("android.app.AppGlobals")
            .getMethod("getInitialApplication").invoke(null, (Object[]) null);
} 

Удачного кодирования!

person Jared Rummler    schedule 19.01.2015
comment
Ага! Очень нравится последний подход! Тем более, что у меня внутренние / скрытые API-интерфейсы отображаются в Android Studio, поэтому мне даже не нужно использовать Reflection, что кажется более безопасным (если методы исчезнут, Android Studio предупредит об этом). Интересно, почему этого нет в SDK? Думаю, облегчает жизнь. - person DADi590; 29.05.2021

Предполагая, что мы говорим о получении контекста приложения, я реализовал его, как это было предложено @Rohit Ghatol, расширяющим приложение. Затем произошло то, что нет гарантии, что контекст, полученный таким образом, всегда будет отличным от нуля. В то время, когда вам это нужно, это обычно из-за того, что вы хотите инициализировать помощник или получить ресурс, который вы не можете отложить во времени; обработка нулевого случая вам не поможет. Итак, я понял, что в основном борюсь против архитектуры Android, как указано в документации

Примечание. Обычно нет необходимости создавать подклассы Application. В большинстве случаев статические синглтоны могут предоставлять ту же функциональность более модульным способом. Если вашему синглтону нужен глобальный контекст (например, для регистрации широковещательных приемников), включите Context.getApplicationContext () в качестве аргумента контекста при вызове метода getInstance () вашего синглтона.

и объяснил Дайанн Хакборн

Единственная причина, по которой приложение существует как нечто, из чего вы можете извлечь, заключается в том, что во время разработки до версии 1.0 один из наших разработчиков приложений постоянно вызывал у меня проблемы с необходимостью иметь объект приложения верхнего уровня, из которого они могли бы производиться, чтобы они могли иметь более "нормальный" "для них модель приложения, и я в конце концов сдался. Я всегда буду сожалеть о том, что уступил ей. :)

Она также предлагает решение этой проблемы:

Если вам нужно какое-то глобальное состояние, которое можно использовать в разных частях приложения, используйте синглтон. [...] И это более естественно приводит к тому, как вы должны управлять этими вещами - инициализировать их по запросу.

так что я избавился от расширения Application и передал контекст непосредственно getInstance () одноэлементного помощника, сохранив ссылку на контекст приложения в частном конструкторе:

private static MyHelper instance;
private final Context mContext;    

private MyHelper(@NonNull Context context) {
    mContext = context.getApplicationContext();
}

public static MyHelper getInstance(@NonNull Context context) {
    synchronized(MyHelper.class) {
        if (instance == null) {
            instance = new MyHelper(context);
        }
        return instance;
    }
}

вызывающий затем передает помощнику локальный контекст:

Helper.getInstance(myCtx).doSomething();

Итак, чтобы правильно ответить на этот вопрос: существуют способы статического доступа к контексту приложения, но все они не должны поощряться, и вам следует предпочесть передачу локального контекста в getInstance () синглтона.


Для всех, кто заинтересован, вы можете прочитать более подробную версию в блоге fwd

person Alessio    schedule 16.08.2016
comment
Обратите внимание, что в Android Studio (по крайней мере, 2.3.1) вы предупреждаете, что не следует сохранять контекст в статическом поле, когда вы это делаете (утечка памяти). Это правда в данном случае? Я избавился от него, получив все, что мне нужно, из контекста и сохранил это (пути к файлам и т. Д.) В членах, так что я в порядке, просто уточняю. Также этот подход сначала кажется немного странным, потому что вы ничего не делаете с контекстом при последующих вызовах getInstance(). :) И все же ответ убедил меня больше, чем принятый. - person Gero; 07.04.2017
comment
@Gero Я сохраняю ссылку на контекст приложения, поскольку предполагаю, что она понадобится другому методу MyHelper. Если он вам никогда не понадобится, вам не следует хранить его ссылку (для чего?). Кроме того, ваш подход не позволяет изменять ресурсы (например, когда пользователь меняет язык), поэтому я бы не рекомендовал его. И последнее, но не менее важное: предупреждение о линтах является правильным, потому что то, что видит линт, является контекстом, но в этом случае я беру контекст приложения (контекст единственного глобального объекта Application текущего процесса) в синглтоне, который после ленивой инициализации доступен до краха - person Alessio; 10.04.2017
comment
@Alessio Ага, в моем случае я не приспосабливаюсь к изменениям ресурсов, но это нормально для меня в контексте моего приложения. Я также, в конце концов, действительно удалил ссылку на контекст (после того, как понял, что могу просто жить с данными, которые я получил от него при инициализации). Итак, я хотел сказать «Таким образом» в моем случае немного странно продолжать передавать контекст при последующих вызовах getInstance (), извините, если это было неясно. Кроме того, спасибо за пояснение, что игнорировать предупреждение о ворсинах можно, если вы просто сохраняете контекст приложения. Я уже догадался, но подтверждение всегда хорошо. - person Gero; 10.04.2017
comment
@Gero в вашем конкретном случае, тогда я буду еще более защищенным: вместо передачи контекста конструктору или getInstance () просто передайте необходимые параметры инициализации напрямую и позвольте вызывающей стороне использовать контекст для их получения. Тогда ваш интерфейс / API еще более понятен, потому что он указывает на то, что вашему классу или помощнику вообще не нужен контекст. Пожалуйста, я рад уточнить, а также я планирую написать расширенную запись для этого ответа, чтобы у меня было больше места для комментариев и ответов на любые сомнения. - person Alessio; 11.04.2017
comment
@Alessio Да, это, наверное, более полезно. Я все равно еще не закончил с классом, так что это моя задача по рефакторингу. Я также подумываю о том, чтобы иметь специальный метод setupInstance () или что-то для этого, и чтобы singleton использовал какое-то значимое поведение по умолчанию, если оно еще не было вызвано. Обратной стороной этого может быть то, что ленивая инициализация не очень хороша с этим, поскольку это не тогда, когда вам нужно все время передавать бесполезные параметры (которые использовались только при первом вызове), независимо от того, завернут ли он в контекстные или индивидуальные. Обидно, что экземпляр не может получить их сам по себе. - person Gero; 11.04.2017
comment
@Alessio Отличная статья! Это именно то, что я наконец сделал (не считая концепции многопоточности, которая мне сейчас не нужна). Большое спасибо за то, что вы приложили дополнительные усилия, а также собрали различные источники по проблеме (например, заявление Дайанны). - person Gero; 07.06.2017
comment
@Alessio Разве этот метод не приводит к утечке памяти - person Phillip Kigenyi; 15.09.2017
comment
@codephillip нет. Почему ты так думаешь? Пожалуйста, расскажите мне больше, также прочтите мою запись в блоге, чтобы узнать больше об этом - person Alessio; 15.09.2017
comment
@Alessio - это синглтон, взятый в контексте из компонента (активности). Что происходит, когда компонент прекращает работу системы. Что происходит при вызове onResume (), onStart (). Вы должны протестировать приложение, открыв несколько других приложений на вашем телефоне, а затем вернувшись примерно через 5-10 минут, вы можете обнаружить, что ваше приложение вылетело из-за того, что singleton ссылается на контекст из убитого компонента (activity ) - person Phillip Kigenyi; 15.09.2017
comment
@codephillip Я не понимаю, о чем вы говорите. Синглтон ссылается на контекст приложения, полученный из переданного действия, а не на действие узла. Это нормально и не вызовет утечки памяти. Это основная мысль моего блога, который я написал. Если вы действительно думаете, что правы, пришлите мне образец кода, в котором я могу воспроизвести утечку памяти, о которой вы говорите, потому что это не так. - person Alessio; 15.09.2017
comment
Я думаю, что @KigenyiPhillip верен, и это все еще представляет собой утечку ресурсов. Представьте себе справочную таблицу после вашего первого звонка в getInstance(ctx). У вас есть корень GC instance типа MyHelper, который имеет личное поле mContext типа Context, которое ссылается на контекст приложения, собранный через контекст, переданный в getInstance(). instance никогда не устанавливается во второй раз и не сбрасывается, поэтому сборщик мусора никогда не поймает контекст приложения, на который ссылается instance. Вы не допускаете утечки информации о деятельности, так что это низкая стоимость ИМО. - person Mark McKenna; 20.08.2018
comment
@MarkMcKenna, как вы указываете, у которого есть частное поле mContext типа Context, которое ссылается на контекст приложения, поэтому вам ясно, что mContext является ссылкой на контекст приложения, а не на какой-либо контекст. В getApplicationContext () документах вы читаете: Контекст чей жизненный цикл отделен от текущего контекста, который привязан к времени жизни процесса, а не текущего компонента. Как это может вызвать утечку памяти? Контекст приложения GC'd только тогда, когда процесс завершается. - person Alessio; 21.08.2018
comment
@Alessio, если вы согласны с тем, что ссылка на контекст приложения не квалифицируется как утечка ресурсов, вы можете упростить это, разместив статическую ссылку на this в Application.onCreate(), что сделает принятый ответ лучше. - person Mark McKenna; 22.08.2018
comment
@MarkMcKenna сначала вы говорите, что это утечка, а теперь говорите, что нет. Ссылка на контекст приложения не является утечкой. Но проблема принятого ответа (для расширения приложения и получения контекста статически) заключается не в утечке ресурсов, а в извлечении статического контекста, который может быть «нулевым», и в этот момент вы мало что можете с этим поделать. Я объяснил это более подробно в нашем блоге, пожалуйста прочтите внимательно. После этого, если у вас возникнут какие-либо вопросы, дайте мне знать. Также внимательно читайте слова Дайанны, если не мои. - person Alessio; 23.08.2018
comment
@Alessio Я просмотрел ваш блог, но я не понимаю одного: у вас null при использовании метода подкласса приложений, значит, вы запускали код до app onCreate: к какому Context у вас был доступ в таком коде, перейти на помощника? В стороне, Марк МакКенна частично прав: не имеет значения, используете ли вы отдельный одноэлементный класс, как вы, или этот метод используется при создании подклассов Application - так что вы могли бы пропустить все это обсуждение; суть в том, что вы начинаете с некоторого контекста, чтобы избежать null. Хороший момент - не кэшировать контекст, не связанный с приложением. - person ToolmakerSteve; 04.01.2020
comment
@ToolmakerSteve Я не понимаю вашего вопроса, но в основном я испытал то, что некоторые статически загруженные классы могут извлекать из метода подкласса приложения ссылку, содержащую null, что очень просто. Моя основная идея заключается не в том, чтобы не создавать подклассы приложения, а в том, чтобы не извлекать контекст статически. Его следует извлекать по запросу, каждый раз передавая его от родителей к детям. Потерпи меня: каждый раз. И это не я объясняю это, даже если мне кажется, что это еще не понято, это сама Дайан Хакборн, и в ее словах нет никаких сомнений. - person Alessio; 08.01.2020
comment
нп. Суть вашего ответа - начать с местного контекста - вот важный совет (как сказал в ответе Эриха Дугласа много лет назад). РАЗЪЯСНЕНИЕ ДЛЯ ДРУГИХ: синглтон нужен только при доступе к вашим собственным глобальным объектам. Первоначальный вопрос касался доступа к контексту приложения где угодно. Если кто-то пытается это сделать, это ему не помогает - поскольку вам нужен параметр контекста myCtx. Чтобы просто получить доступ к контексту приложения, этот синглтон не нужен - кодировщик должен просто сделать myCtx.getApplicationContext().... - person ToolmakerSteve; 08.01.2020
comment
@ToolmakerSteve Я показал пример как синглтон, потому что именно так он объясняется в документации по Android, на которую я ссылаюсь: Примечание. Обычно нет необходимости создавать подклассы Application. В большинстве случаев статические синглтоны могут предоставлять ту же функциональность более модульным способом. Если вашему синглтону нужен глобальный контекст (например, для регистрации широковещательных приемников), включите Context.getApplicationContext () в качестве аргумента контекста при вызове метода getInstance () вашего синглтона. Итак, я показал, как именно вы это сделаете, чтобы передать его в getInstance () и получить из него контекст приложения. - person Alessio; 09.01.2020
comment
@ToolmakerSteve, поэтому я не согласен с тем, что вы говорите. Если кто-то пытается это сделать, это не помогает им в этом - поскольку вам требуется параметр Context. Да. Это просто: вы не должны получать доступ к своему контексту повсюду, но вы должны передавать его от родителей к детям повсюду, а дети хранят от него ссылки на контекст приложения. Это правильный способ, все остальное - неправильно. Я удивлен, что спустя столько лет это все еще не понято, и не из-за меня, а из-за слов Hackborn и дизайна системы Android. - person Alessio; 09.01.2020
comment
@ToolmakerSteve, если вы посмотрите на Flutter, который был разработан 2 года назад, он использует тот же шаблон, передавая контекст по запросу. Итак, вы не можете бороться с системой, не ошибаясь (иначе говоря, потенциальный НП). И это для доступа к глобальному контексту: если вы исключите классы действий и служб, какие у вас есть другие классы, экземпляры которых не созданы из действия? У меня были только синглтоны или классы с чистыми статическими методами, больше ничего не могу придумать. Ты? - person Alessio; 09.01.2020
comment
@ToolmakerSteve также позволил мне заявить об очевидном и убедиться, что мы находимся на одной странице: я не предлагаю получить доступ к контексту приложения через синглтон MyHelper. MyHelper - это класс, которому необходим доступ к контексту, например, для извлечения некоторых ресурсов или регистрации получателя или чего-то еще в методе doSomething (). Если другому вспомогательному классу AnotherHelper потребуется нечто подобное, вам также потребуется передать контекст в его getInstance (). И так далее, для всех классов, которые не расширяют контекст или сами имеют доступ к контексту. - person Alessio; 09.01.2020
comment
@Alessio - Я прокомментировал еще раз, чтобы прояснить всем, кто придет сюда, что ваш ответ не является решением исходного вопроса, который искал статический способ доступа к контексту . Это то, что я имел в виду, когда сказал, что если кто-то пытается это сделать ... это им не помогает. Ваш ответ полезен для (другой) ситуации, когда один имеет доступный контекст и желает использовать контекст приложения в статических методах. Я просто хочу, чтобы читатели четко понимали, для каких целей ваш ответ полезен, а для каких - нет. - person ToolmakerSteve; 10.01.2020
comment
@ToolmakerSteve: Я прекрасно понял, и я с вами очень не согласен. Я цитирую себя несколько лет назад: Итак, чтобы правильно ответить на этот вопрос: есть способы получить доступ к контексту приложения статически, но все они не должны поощряться, и вам следует предпочесть передачу локального контекста в одноэлементный getInstance (). Мой ответ отвечает на вопрос таким образом, что не должно быть никакого способа получить статический доступ к контексту в чьем-то коде, и вместо этого она должна обращаться к контексту по запросу. Я думаю, вы игнорируете то, что я пишу, пожалуйста, прочтите внимательно, там все есть - person Alessio; 13.01.2020

Нет, не думаю. К сожалению, вы застряли при вызове getApplicationContext() из Activity или одного из других подклассов Context. Кроме того, этот вопрос несколько связан.

person Erich Douglass    schedule 05.01.2010
comment
Правая ссылка на статью: android-developers.blogspot .co.il / 2009/01 / - person Tal Weiss; 04.07.2012

Вот недокументированный способ получить приложение (который является контекстом) из любого места в потоке пользовательского интерфейса. Он полагается на скрытый статический метод ActivityThread.currentApplication(). Он должен работать как минимум на Android 4.x.

try {
    final Class<?> activityThreadClass =
            Class.forName("android.app.ActivityThread");
    final Method method = activityThreadClass.getMethod("currentApplication");
    return (Application) method.invoke(null, (Object[]) null);
} catch (final ClassNotFoundException e) {
    // handle exception
} catch (final NoSuchMethodException e) {
    // handle exception
} catch (final IllegalArgumentException e) {
    // handle exception
} catch (final IllegalAccessException e) {
    // handle exception
} catch (final InvocationTargetException e) {
    // handle exception
}

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

По-прежнему лучше использовать решение @RohitGhatol, если вы можете изменить код приложения.

person kennytm    schedule 19.09.2012
comment
Я использовал вышеупомянутый метод KennyTM, но иногда метод возвращает значение null. Есть ли другая альтернатива этому? Например, если мы получаем здесь ноль, мы можем получить контекст из другого места. В моем случае onCreate () приложения не вызывается. Но перед этим вызывается вышеуказанный метод. Пожалуйста, помогите - person AndroidGuy; 25.03.2013
comment
Это не всегда сработает в том случае, если сборщик мусора очистил все, что связано с деятельностью. - person AlexVPerl; 07.05.2015

Это зависит от того, для чего вы используете контекст. Я могу вспомнить хотя бы один недостаток этого метода:

Если вы пытаетесь создать AlertDialog с AlertDialog.Builder, Application контекст не будет работать. Я считаю, что вам нужен контекст для текущего _4 _...

person gulchrider    schedule 12.08.2011
comment
Верно. Если вы используете для этого контекст приложения, вы можете увидеть свой диалог, скрытый под действиями переднего плана. - person Nate; 23.08.2011
comment
+1 в первую очередь. И возможная ошибка: невозможно запустить действие ComponentInfo {com.samples / com.MyActivity}: android.view.WindowManager $ BadTokenException: невозможно добавить окно - нулевой токен не предназначен для приложения - person Govind; 25.09.2014

Котлинский способ:

Манифест:

<application android:name="MyApplication">

</application>

MyApplication.kt

class MyApplication: Application() {

    override fun onCreate() {
        super.onCreate()
        instance = this
    }

    companion object {
        lateinit var instance: MyApplication
            private set
    }
}

Затем вы можете получить доступ к собственности через MyApplication.instance

person phnmnn    schedule 23.05.2018

Если вы готовы использовать RoboGuice, вы можете внедрить контекст в любой класс, который хотите . Вот небольшой пример того, как это сделать с помощью RoboGuice 2.0 (бета 4 на момент написания)

import android.content.Context;
import android.os.Build;
import roboguice.inject.ContextSingleton;

import javax.inject.Inject;

@ContextSingleton
public class DataManager {
    @Inject
    public DataManager(Context context) {
            Properties properties = new Properties();
            properties.load(context.getResources().getAssets().open("data.properties"));
        } catch (IOException e) {
        }
    }
}
person user605331    schedule 29.02.2012

Котлин

open class MyApp : Application() {
    override fun onCreate() {
        super.onCreate()
        mInstance = this
    }

    companion object {
        lateinit var mInstance: MyApp
        fun getContext(): Context? {
            return mInstance.applicationContext
        }
    }
}

и получить Context как

MyApp.mInstance

or

MyApp.getContext()
person Khemraj Sharma    schedule 21.09.2018

Я когда-то использовал это:

ActivityThread at = ActivityThread.systemMain();
Context context = at.getSystemContext();

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

Но я использовал его только в модификациях фреймворка / базы и не пробовал в приложениях Android.

Вы должны знать предупреждение: при регистрации широковещательных приемников в этом контексте это не сработает, и вы получите:

java.lang.SecurityException: данный пакет вызывающего абонента не работает в процессе ProcessRecord.

person ungalcrys    schedule 08.05.2014

Если вы не хотите изменять файл манифеста, вы можете вручную сохранить контекст в статической переменной в своем начальном действии:

public class App {
    private static Context context;

    public static void setContext(Context cntxt) {
        context = cntxt;
    }

    public static Context getContext() {
        return context;
    }
}

И просто установите контекст, когда ваша деятельность (или действия) начинается:

// MainActivity

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    // Set Context
    App.setContext(getApplicationContext());

    // Other stuff
}

Примечание. Как и все другие ответы, это потенциальная утечка памяти.

person Sheharyar    schedule 14.03.2018
comment
Что именно будет происходить, если контекст в этом случае привязан к приложению? Если приложение умирает, умирает и все остальное. - person TheRealChx101; 12.10.2019
comment
Есть ли способ предотвратить эту утечку статических контекстных ссылок? - person David Kariuki; 10.07.2020
comment
Нет, поскольку вы устанавливаете контекст с помощью getApplicationContext(), утечка контекстов активности не происходит. Однако он может возвращать значение null в потоке, не относящемся к пользовательскому интерфейсу, который выходит за рамки действия. - person Eugene Kartoyev; 18.09.2020

в Kotlin при помещении контекста / контекста приложения в сопутствующий объект по-прежнему появляется предупреждение Do not place Android context classes in static fields; this is a memory leak (and also breaks Instant Run)

или если вы используете что-то вроде этого:

    companion object {
        lateinit var instance: MyApp
    }

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

В качестве альтернативы вы можете использовать функциональный интерфейс или функциональные свойства, чтобы помочь вам получить контекст вашего приложения.

Просто создайте класс объекта:

object CoreHelper {
    lateinit var contextGetter: () -> Context
}

или вы можете использовать его более безопасно, используя тип, допускающий значение NULL:

object CoreHelper {
    var contextGetter: (() -> Context)? = null
}

и в своем классе приложения добавьте эту строку:


class MyApp: Application() {

    override fun onCreate() {
        super.onCreate()
        CoreHelper.contextGetter = {
            this
        }
    }
}

и в своем манифесте объявите имя приложения . MyApp


    <application
            android:name=".MyApp"

Если вы хотите получить контекст, просто позвоните:

CoreHelper.contextGetter()

// or if you use the nullable version
CoreHelper.contextGetter?.invoke()

Надеюсь, это поможет.

person Hayi Nukman    schedule 03.08.2019
comment
Этот объектный класс corehelper будет инициализирован и может использоваться в наших действиях на более позднем этапе? Извини я новичок в котлине - person Dr. aNdRO; 10.02.2020

Вы можете использовать следующее:

MainActivity.this.getApplicationContext();

MainActivity.java:

...
public class MainActivity ... {
    static MainActivity ma;
...
    public void onCreate(Bundle b) {
         super...
         ma=this;
         ...

Любой другой класс:

public ...
    public ANY_METHOD... {
         Context c = MainActivity.ma.getApplicationContext();
person barwnikk    schedule 06.04.2013
comment
Это работает, только если вы находитесь внутри внутреннего класса, что вряд ли имеет место в OP. - person Richard J. Ross III; 02.05.2013
comment
Это будет работать до тех пор, пока ANY_METHOD вызывается после создания MainActivity, но сохранение статических ссылок на действия почти неизбежно приводит к утечкам памяти (как уже упоминалось в других ответах на вопрос OP), поэтому, если вы действительно должны сохранить статическую ссылку, используйте приложение только контекст. - person handtwerk; 31.05.2013
comment
Внутренние классы злы. Хуже всего то, что многие люди делают это для AsyncTasks и тому подобного, потому что многие учебники делают это именно так ... - person Reinherd; 10.12.2013

Согласно этот источник, вы можете получить свой собственный контекст, расширив ContextWrapper

public class SomeClass extends ContextWrapper {

    public SomeClass(Context base) {
      super(base);
    }

    public void someMethod() {
        // notice how I can use "this" for Context
        // this works because this class has it's own Context just like an Activity or Service
        startActivity(this, SomeRealActivity.class);

        //would require context too
        File cacheDir = getCacheDir();
    }
}

JavaDoc для ContextWrapper

Проксирующая реализация контекста, которая просто делегирует все свои вызовы другому контексту. Может быть разделен на подклассы для изменения поведения без изменения исходного контекста.

person BlueWizard    schedule 13.12.2016
comment
Это интересно. Приятно узнать о ContextWrapper. Однако, если вам нужно передать контекст приложения этому конструктору, вам все равно нужно получить его откуда-то. - person jk7; 28.02.2017

Думаю нужно тело для метода getAppContext():

public static Context getAppContext()
   return MyApplication.context; 
person Kognos    schedule 18.08.2011

Я только что выпустил основанный на jQuery фреймворк для Android под названием Vapor API, который призван упростить разработку приложений.

Центральный $ класс фасада поддерживает _ 2_ (ссылка на потрясающее сообщение в блоге Java об этом, написанное Итаном Николасом) в текущий Activity контекст, который вы можете получить, позвонив :

$.act()

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

Обратной стороной, конечно же, является то, что вы рискуете, что $.act() может вернуть null. Я еще не сталкивался с этим сценарием, так что, возможно, это минимальный риск, о котором стоит упомянуть.

Вы также можете установить контекст вручную, если вы не используете VaporActivity в качестве своего Activity класса:

$.act(Activity);

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

Надеюсь, это поможет :)

person Darius    schedule 26.02.2013
comment
Видимо, за это только что проголосовали против ... объяснение было бы неплохо !? - person Darius; 02.10.2013
comment
Я не отрицал это, но Javascript не имеет никакого отношения к рассматриваемому вопросу, он объяснил бы любые отрицательные голоса, которые у вас могли быть! Ваше здоровье. - person Ernani Joppert; 10.09.2014
comment
Это было бы довольно бессмысленно, учитывая, что он вдохновлен некоторыми аспектами jQuery, такими как свободный интерфейс и его абстракции ... это принципы, не зависящие от основного языка! - person Darius; 10.09.2014
comment
Я понимаю вашу идею и ваше объяснение, у нее есть контекст, но поймите, что думают другие пользователи. В любом случае я с тобой! - person Ernani Joppert; 12.09.2014
comment
Итак, вы голосуете против него, потому что он был вдохновлен семантикой API фреймворка, который не находится на той же платформе ?! Я думаю, вы упускаете из виду применение принципов, не зависящих от платформы ..................................... - person Darius; 30.03.2016
comment
этот ответ совершенно не имеет отношения к JavaScript. Прочтите ответ перед тем, как проголосовать против: / - person BlueWizard; 13.12.2016
comment
Фактически, этот ответ вдохновил меня на использование удобного именования классов JQuery. Но это не имеет отношения к JQuery. Люди должны лучше читать! Идея использования WeakReference прекрасна! - person Eugene Kartoyev; 18.09.2020
comment
@EugeneKartoyev WeakReference не рекомендуется. - person Martin Zeitler; 12.01.2021

Если вам по какой-то причине нужен контекст приложения в любом классе, а не только в тех, которые расширяют приложение / действие, возможно, для некоторых фабричных или вспомогательных классов. Вы можете добавить в свое приложение следующий синглтон.

public class GlobalAppContextSingleton {
    private static GlobalAppContextSingleton mInstance;
    private Context context;

    public static GlobalAppContextSingleton getInstance() {
        if (mInstance == null) mInstance = getSync();
        return mInstance;
    }

    private static synchronized GlobalAppContextSingleton getSync() {
        if (mInstance == null) mInstance = 
                new GlobalAppContextSingleton();
        return mInstance;
    }

    public void initialize(Context context) {
        this.context = context;
    }

    public Context getApplicationContext() {
        return context;
    }
}

затем инициализируйте его в классе приложения onCreate с помощью

GlobalAppContextSingleton.getInstance().initialize(this);

используйте его где угодно, позвонив

GlobalAppContextSingleton.getInstance().getApplicationContext()

Однако я не рекомендую этот подход ни для чего, кроме контекста приложения. Поскольку это может вызвать утечку памяти.

person Versa    schedule 23.02.2016
comment
Это не похоже на то, что имена классов / методов высечены в камне, они были длинными и (надеюсь) описательными для вопросов и ответов, сокращены для моего собственного использования. - person Versa; 08.09.2016

Я использую вариант шаблона проектирования Singleton, чтобы помочь мне в этом.

import android.app.Activity;
import android.content.Context;

public class ApplicationContextSingleton {
    private static Activity gContext;

    public static void setContext( Activity activity) {
        gContext = activity;
    }

    public static Activity getActivity() {
        return gContext;
    }

    public static Context getContext() {
        return gContext;
    }
}

Затем я вызываю ApplicationContextSingleton.setContext( this ); в своем activity.onCreate () и ApplicationContextSingleton.setContext( null ); в onDestroy ();

person Bamaco    schedule 30.12.2014
comment
Если вам нужен только контекст, вы можете вызвать activity.getApplicationContext (); Это можно удерживать статически, не беспокоясь об утечках. - person MinceMan; 02.06.2015
comment
это приведет к утечке памяти - person BlueWizard; 13.12.2016

Ответ Рохита кажется правильным. Однако имейте в виду, что, насколько мне известно, «Мгновенный запуск» AndroidStudio зависит от отсутствия атрибутов static Context в вашем коде.

person payne    schedule 12.07.2018
comment
Ты прав. И это также приведет к утечке памяти! - person user1506104; 24.07.2018

Сегодня правильный способ получить context - это использовать внедрение зависимостей. Например, можно использовать Hilt для внедрения контекста в любом месте, где это необходимо. Допустим, нужно context в каком-то менеджере баз данных, тогда это можно решить следующим образом:

Добавьте рукоять в Gradle:

implementation "com.google.dagger:hilt-android:2.35"
kapt "com.google.dagger:hilt-android-compiler:2.35"

Определите класс Application с аннотацией @HiltAndroidApp (пусть он, например, внедрит менеджер баз данных):

@HiltAndroidApp
class MyApplication : Application() {

    @Inject
    lateinit var dbManager: DBManager

    override fun onCreate() {
        super.onCreate()
        dbManager.initDB()
    }
}

Определите диспетчер баз данных (пусть это тоже будет @Singleton):

@Singleton
class DBManager @Inject constructor(
    @ApplicationContext private val context: Context
) {

    fun initDB() {
        // context is avaiable
        databaseInit(context)
    }
}

Вот и все. DBManager может получить доступ к контексту правильным образом без утечек памяти.

person Sergey Trukhachev    schedule 26.05.2021