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

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

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

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() чтобы получить контекст приложения статически.


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

предупреждение

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

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

удачи в кодировании!


Нет, я не думаю, что есть. К сожалению, вы застряли вызова 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'ы решение, если вы можете изменить код приложения.


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

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

и пояснил Диана Hackborn

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

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

Итак, я избавился от расширения приложения и передал контекст непосредственно 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


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

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


Если вы открыты для использования RoboGuice, вы можете ввести контекст в любой класс, который вы хотите. Вот небольшой пример того, как это сделать с RoboGuice 2.0 (beta 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();

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

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

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

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


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

public static Context getAppContext()
   return MyApplication.context; 

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

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

JavaDoc для ContextWrapper

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


Я использую вариант Шаблона дизайна 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 ); в своем активности.onCreate () и ApplicationContextSingleton.setContext( null ); на onDestroy();


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

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

$.act()

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

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

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

$.act(Activity);

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

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


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

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

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


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

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;
    }
}

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


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

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
}

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


Котлин способом:

Манифест:

<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.пример


Я проверил все здесь описанное предложение и могу сказать:

Если я использую следующий код:

public class App extends Application {
    private static Context context;
    public void onCreate() {
        super.onCreate();
        context = getApplicationContext();
    }

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

Я получаю в студии 3 сообщение об ошибке:

Do not place Android context classes in static fields; this is a memory leak (and also breaks Instant Run).

но если я использую следующий код:

public class App extends Application {
    private static List<Context> context = new ArrayList<>();

    @Override
    public void onCreate() {
        super.onCreate();
        context.add(0, getApplicationContext());
    }

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

он работает нормально без каких либо ошибок.


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


В Котлин

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

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

и получить контекст, как

App.mInstance

или

MyApp.getContext()