Есть ли способ получить текущий экземпляр Context
внутри статического метода?
Я ищу этот способ, потому что ненавижу сохранять экземпляр «Контекст» каждый раз, когда он изменяется.
Есть ли способ получить текущий экземпляр Context
внутри статического метода?
Я ищу этот способ, потому что ненавижу сохранять экземпляр «Контекст» каждый раз, когда он изменяется.
Сделай это:
В файле манифеста 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()
, чтобы получить статический контекст вашего приложения.
static context
переменную как volatile
?
- person Vladimir Sorokin; 31.03.2012
context
может быть доступна для нескольких потоков. А согласно Java Concurrency In Practice - 3.5.3. Безопасные идиомы публикации: для безопасной публикации ссылки вы должны использовать один из следующих подходов: статический инициализатор, изменчивое поле, AtomicReference, последнее поле, блокировка. В противном случае у вас могут возникнуть проблемы с параллелизмом. Но я не на 100% уверен в нашем конкретном случае, есть разные тонкие нюансы, поэтому я решил использовать volatile
на всякий случай, это все равно не влияет на производительность.
- person Vladimir Sorokin; 29.07.2012
ContentProvider
для получения сейфа Context
- person Louis CAD; 15.06.2018
myContext.getApplicationContext
и сохранять его в этом статическом глобальном. Вы знаете, есть ли какой-либо контекст до Application.onCreate?
- person ToolmakerSteve; 04.01.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);
}
Удачного кодирования!
Предполагая, что мы говорим о получении контекста приложения, я реализовал его, как это было предложено @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 а>
getInstance()
. :) И все же ответ убедил меня больше, чем принятый.
- person Gero; 07.04.2017
getInstance(ctx)
. У вас есть корень GC instance
типа MyHelper
, который имеет личное поле mContext
типа Context
, которое ссылается на контекст приложения, собранный через контекст, переданный в getInstance()
. instance
никогда не устанавливается во второй раз и не сбрасывается, поэтому сборщик мусора никогда не поймает контекст приложения, на который ссылается instance
. Вы не допускаете утечки информации о деятельности, так что это низкая стоимость ИМО.
- person Mark McKenna; 20.08.2018
this
в Application.onCreate()
, что сделает принятый ответ лучше.
- person Mark McKenna; 22.08.2018
null
при использовании метода подкласса приложений, значит, вы запускали код до app onCreate
: к какому Context
у вас был доступ в таком коде, перейти на помощника? В стороне, Марк МакКенна частично прав: не имеет значения, используете ли вы отдельный одноэлементный класс, как вы, или этот метод используется при создании подклассов Application - так что вы могли бы пропустить все это обсуждение; суть в том, что вы начинаете с некоторого контекста, чтобы избежать null. Хороший момент - не кэшировать контекст, не связанный с приложением.
- person ToolmakerSteve; 04.01.2020
myCtx
. Чтобы просто получить доступ к контексту приложения, этот синглтон не нужен - кодировщик должен просто сделать myCtx.getApplicationContext()...
.
- person ToolmakerSteve; 08.01.2020
Нет, не думаю. К сожалению, вы застряли при вызове getApplicationContext()
из Activity
или одного из других подклассов Context
. Кроме того, этот вопрос несколько связан.
Вот недокументированный способ получить приложение (который является контекстом) из любого места в потоке пользовательского интерфейса. Он полагается на скрытый статический метод 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, если вы можете изменить код приложения.
Это зависит от того, для чего вы используете контекст. Я могу вспомнить хотя бы один недостаток этого метода:
Если вы пытаетесь создать AlertDialog
с AlertDialog.Builder
, Application
контекст не будет работать. Я считаю, что вам нужен контекст для текущего _4 _...
Котлинский способ:
Манифест:
<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
Если вы готовы использовать 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) {
}
}
}
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()
Я когда-то использовал это:
ActivityThread at = ActivityThread.systemMain();
Context context = at.getSystemContext();
Это допустимый контекст, который я использовал при получении системных служб и работал.
Но я использовал его только в модификациях фреймворка / базы и не пробовал в приложениях Android.
Вы должны знать предупреждение: при регистрации широковещательных приемников в этом контексте это не сработает, и вы получите:
java.lang.SecurityException: данный пакет вызывающего абонента не работает в процессе ProcessRecord.
Если вы не хотите изменять файл манифеста, вы можете вручную сохранить контекст в статической переменной в своем начальном действии:
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
}
Примечание. Как и все другие ответы, это потенциальная утечка памяти.
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()
Надеюсь, это поможет.
Вы можете использовать следующее:
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();
Согласно этот источник, вы можете получить свой собственный контекст, расширив 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();
}
}
Проксирующая реализация контекста, которая просто делегирует все свои вызовы другому контексту. Может быть разделен на подклассы для изменения поведения без изменения исходного контекста.
Думаю нужно тело для метода getAppContext()
:
public static Context getAppContext()
return MyApplication.context;
Я только что выпустил основанный на jQuery фреймворк для Android под названием Vapor API, который призван упростить разработку приложений.
Центральный $
класс фасада поддерживает _ 2_ (ссылка на потрясающее сообщение в блоге Java об этом, написанное Итаном Николасом) в текущий Activity
контекст, который вы можете получить, позвонив :
$.act()
WeakReference
поддерживает ссылку, не препятствуя сборке мусора, возвращающей исходный объект, поэтому у вас не должно возникнуть проблем с утечками памяти.
Обратной стороной, конечно же, является то, что вы рискуете, что $.act()
может вернуть null. Я еще не сталкивался с этим сценарием, так что, возможно, это минимальный риск, о котором стоит упомянуть.
Вы также можете установить контекст вручную, если вы не используете VaporActivity
в качестве своего Activity
класса:
$.act(Activity);
Кроме того, большая часть инфраструктуры Vapor API изначально использует этот сохраненный контекст, что может означать, что вам вообще не нужно хранить его самостоятельно. если вы решите использовать фреймворк. Посетите сайт для получения дополнительной информации и примеров.
Надеюсь, это поможет :)
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()
Однако я не рекомендую этот подход ни для чего, кроме контекста приложения. Поскольку это может вызвать утечку памяти.
Я использую вариант шаблона проектирования 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 ();
Ответ Рохита кажется правильным. Однако имейте в виду, что, насколько мне известно, «Мгновенный запуск» AndroidStudio зависит от отсутствия атрибутов static Context
в вашем коде.
Сегодня правильный способ получить 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
может получить доступ к контексту правильным образом без утечек памяти.
Context
, то может быть лучший способ разработать код. - person Anonsage   schedule 20.02.2015Context
иApplicationContext
одинаковы, но один из них тематический. Когдаstatic
код требуетContext
, лучше игнорируйте эти так называемые решения и спросите себя, как правильно их передать. - person Martin Zeitler   schedule 12.01.2021