Статический способ получить «Контекст» в Android? - PullRequest
899 голосов
/ 05 января 2010

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

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

Ответы [ 19 ]

1225 голосов
/ 25 февраля 2011

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

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

73 голосов
/ 19 января 2015

Большинство приложений, которым нужен удобный метод для получения контекста приложения, создают свой собственный класс, который расширяет 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.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);
} 

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

48 голосов
/ 16 августа 2016

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

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

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

Единственная причина, по которой Application существует как нечто, из которого вы можете извлечь информацию, заключается в том, что во время разработки до версии 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 blog

48 голосов
/ 05 января 2010

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

36 голосов
/ 19 сентября 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
}

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

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

32 голосов
/ 12 августа 2011

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

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

11 голосов
/ 29 февраля 2012

Если вы открыты для использования 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 голосов
/ 08 мая 2014

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

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

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

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

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

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

4 голосов
/ 21 сентября 2018

Котлин

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()
4 голосов
/ 23 мая 2018

Путь Котлина :

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...