Есть ли способ получить текущий Context
экземпляр внутри статического метода?
Я ищу этот путь, потому что ненавижу сохранять экземпляр Context каждый раз, когда он меняется.
Context
, тогда может быть лучший способ разработки кода.
Есть ли способ получить текущий Context
экземпляр внутри статического метода?
Я ищу этот путь, потому что ненавижу сохранять экземпляр 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
?
Большинство приложений, которым нужен удобный метод для получения контекста приложения, создают свой собственный класс, который расширяется android.app.Application
.
РУКОВОДСТВО
Вы можете сделать это, сначала создав класс в своем проекте, как показано ниже:
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.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 () в качестве аргумента Context при вызове метода getInstance () вашего синглтона.
и объяснил Дайан Хэкборн
Единственная причина, по которой Application существует как нечто, из которого вы можете извлечь информацию, заключается в том, что во время разработки до версии 1.0 один из наших разработчиков приложений постоянно задавал мне вопрос о необходимости иметь объект приложения верхнего уровня, из которого они могли бы извлекать, чтобы они могли иметь более "нормальный" «им модель приложения, и я в конце концов сдался. Я всегда буду сожалеть о том, что уступил в этом. :)
Она также предлагает решение этой проблемы:
Если вам нужно глобальное состояние, которое можно использовать в разных частях вашего приложения, используйте одиночный код. [...] И это более естественно приводит к тому, как вы должны управлять этими вещами - инициализировать их по требованию.
Итак, я избавился от расширения Application и передал контекст напрямую в getInstance () хелпера singleton, сохранив ссылку на контекст приложения в приватном конструкторе:
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(ctx)
. У вас есть корень instance
типа GC MyHelper
, у которого есть закрытое поле mContext
типа Context
, которое ссылается на контекст приложения, собранный через передаваемый контекст getInstance()
. instance
никогда не устанавливается и не очищается, поэтому GC никогда не будет перехватывать appcontext, на который ссылается instance
. Вы не пропускаете никакие действия, таким образом это - низкая стоимость IMO
this
in Application.onCreate()
, что сделает принятый ответ лучше.
Нет, я не думаю, что есть. К сожалению, вы застряли, звоня 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
контекст не будет работать. Я считаю, что вам нужен контекст для текущего Activity
...
Котлин путь :
Manifest:
<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) {
}
}
}
Я использовал это в какой-то момент:
ActivityThread at = ActivityThread.systemMain();
Context context = at.getSystemContext();
Это правильный контекст, который я использовал при получении системных сервисов и работал.
Но я использовал его только в модификациях фреймворка / базы и не пробовал в приложениях для Android.
Предупреждение , что вы должны знать: При регистрации для радиовещательных приемников с этим контекстом, он не будет работать , и вы получите:
java.lang.SecurityException: данный пакет вызывающей программы Android не запущен в процессе ProcessRecord
open class MyApp : Application() {
override fun onCreate() {
super.onCreate()
mInstance = this
}
companion object {
lateinit var mInstance: MyApp
fun getContext(): Context? {
return mInstance.applicationContext
}
}
}
и получить контекст как
MyApp.mInstance
или
MyApp.getContext()
Вы можете использовать следующее:
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();
Если вы не хотите изменять файл манифеста, вы можете вручную сохранить контекст в статической переменной в вашей начальной активности:
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
}
Примечание: как и все другие ответы, это потенциальная утечка памяти.
Согласно этому источнику вы можете получить свой собственный контекст, расширив 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();
}
}
Прокси-реализация контекста, которая просто делегирует все свои вызовы другому контексту. Может быть разделено на подклассы для изменения поведения без изменения исходного контекста.
Если по какой-то причине вам нужен контекст приложения в любом классе, а не только в тех, которые расширяют приложение / активность, возможно, для некоторых фабричных или вспомогательных классов. Вы можете добавить следующий синглтон в ваше приложение.
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 () ;
Я только что выпустил JQuery-фреймворк для Android под названием Vapor API, который призван упростить разработку приложений.
Класс центрального $
фасада поддерживает WeakReference
(ссылка на потрясающую статью в блоге Java об этом Итана Николаса) в текущий Activity
контекст, который вы можете получить, вызвав:
$.act()
A WeakReference
поддерживает ссылку, не предотвращая сборку мусора, возвращая исходный объект, поэтому у вас не должно быть проблем с утечками памяти.
Недостатком, конечно, является то, что вы рискуете, что $.act()
может вернуть ноль. Я еще не сталкивался с этим сценарием, так что, возможно, это просто минимальный риск, о котором стоит упомянуть
Вы также можете установить контекст вручную, если вы не используете его в VaporActivity
качестве Activity
класса:
$.act(Activity);
Кроме того, большая часть инфраструктуры Vapor API использует этот хранимый контекст по своей сути, что может означать, что вам вообще не нужно хранить его самостоятельно, если вы решите использовать среду. Проверьте сайт для получения дополнительной информации и образцов.
Надеюсь, это поможет :)
Ответ Рохита кажется правильным. Тем не менее, имейте в виду, что «Instant Run» в AndroidStudio зависит от отсутствия static Context
атрибутов в вашем коде, насколько я знаю.
в Kotlin, помещая Context / App Context в объект-компаньон, все еще выдают предупреждение 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
}
Это просто дурак, чтобы не обнаружить утечку памяти, экземпляр App все еще может вызвать утечку памяти, так как класс Application и его потомок являются Context.
Кроме того, вы можете использовать функциональный интерфейс или функциональные свойства, чтобы помочь вам получить контекст вашего приложения.
Просто создайте объектный класс:
object CoreHelper {
lateinit var contextGetter: () -> Context
}
или вы можете использовать его более безопасно, используя обнуляемый тип:
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()
Надеюсь, это поможет.
Попробуйте что-то вроде этого
import androidx.appcompat.app.AppCompatActivity; import android.content.Context; import android.os.Bundle; public class MainActivity extends AppCompatActivity { private static Context context; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); context = getApplicationContext(); } public static void getContext(View view){ Toast.makeText(context, "Got my context!", Toast.LENGTH_LONG).show(); } }