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

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

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

Ответ 1

Сделайте это:

В файле манифеста 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(), чтобы ваш контекст приложения статически.

Ответ 2

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

GUIDE

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

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.xmls:

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

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

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

Внимание

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

Как правило, нет необходимости в подклассе Application. В большинстве случаев, статические синглеты могут обеспечивать такую ​​же функциональность в более модульной путь. Если ваш singleton нуждается в глобальном контексте (например, для регистрации широковещательные приемники), функция для ее получения может быть предоставлена Контекст, который внутренне использует 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);
} 

Счастливое кодирование!

Ответ 3

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

Ответ 4

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

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

и объясняется Dianne Hackborn

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

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

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

поэтому я избавился от расширения приложения и передал контекст непосредственно в singleton helper 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();

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


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

Ответ 5

Вот недокументированный способ получить приложение (которое является контекстом) из любой точки потока пользовательского интерфейса. Он опирается на скрытый статический метод 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, если вы можете изменить код приложения.

Ответ 6

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

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

Ответ 7

Если вы открыты для использования 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) {
        }
    }
}

Ответ 8

Я использовал это в какой-то момент:

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

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

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

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

java.lang.SecurityException: данный пакет андеррауэра не работает в процессе ProcessRecord

Ответ 9

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

public static Context getAppContext()
   return MyApplication.context; 

Ответ 10

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

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();

Ответ 11

В соответствии с этот источник вы можете получить свой собственный контекст, расширив 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 own Context just like an Activity or Service
        startActivity(this, SomeRealActivity.class);

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

JavaDoc для ContextWrapper

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

Ответ 12

Котлин путь:

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.

Ответ 13

Котлин

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()

Ответ 14

Я использую вариант шаблона проектирования 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();

Ответ 15

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

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
}

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

Ответ 16

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

Центральный $ класс фасада поддерживает WeakReference (ссылка на замечательный пост в блоге Java от Ethan Nicholas) в текущий контекст Activity, который вы можете получить, вызвав:

$.act()

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

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

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

$.act(Activity);

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

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

Ответ 17

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

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()

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

Ответ 18

Поэтому я изменил принятый ответ, потому что он вызывает утечку памяти, это то, что я придумал...

AndroidManifest.xml

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

    </application>

MyApplication.java

public class MyBakingAppContext extends Application {
    private static Object mContext;

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

    public static Context getAppContext() {
        return (Context)mContext;
    }
}

Что я на самом деле сделал, так это назначил контекст объекту и возвратил объект как контекст (приведение его к контексту). Надеюсь, это поможет.

Ответ 19

Rohit ответ кажется правильным. Однако помните, что AndroidStudio "Instant Run" зависит от отсутствия static Context атрибутов static Context в вашем коде, насколько я знаю.

Ответ 20

это будет внутри файла AndroidManifest.xml

<application
        android:name=".MyApplication">

</application>

и это файл MyApplication.java

public class MyApplication extends Application {
    private static MyApplication application;
    @Override
    public void onCreate() {
        super.onCreate();
        application = this;
    }
    public static MyApplication getContext(){
        return application;
    }
}