Statyczny sposób na "kontekst" w Androidzie?

Czy istnieje sposób na uzyskanie aktualnej instancji Context wewnątrz statycznej metody?

Szukam tego sposobu, ponieważ nienawidzę zapisywania instancji "kontekstu" za każdym razem, gdy się zmienia.

Author: Sheharyar, 2010-01-04

19 answers

Zrób to:

W pliku Manifest Androida zadeklaruj, co następuje.

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

</application>

Następnie napisz klasę:

public class MyApplication extends Application {

    private static Context context;

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

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

Teraz wszędzie wywołaj MyApplication.getAppContext(), aby statycznie uzyskać kontekst aplikacji.

 1339
Author: Rohit Ghatol,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-15 07:28:25

Większość aplikacji, które chcą wygodnej metody uzyskania kontekstu aplikacji, tworzy własną klasę, która rozszerza android.app.Application.

Przewodnik

Możesz to osiągnąć, najpierw tworząc klasę w swoim projekcie, jak poniżej:

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

Następnie w AndroidManifest powinieneś podać nazwę swojej klasy w AndroidManifest.znacznik xml:

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

Można następnie pobrać kontekst aplikacji w dowolnej statycznej metodzie za pomocą po:

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

WARNING

Przed dodaniem czegoś podobnego do powyższego do swojego projektu powinieneś rozważyć, co mówi dokumentacja:

Zwykle nie ma potrzeby podklasyfikować aplikacji. W większości sytuacji, statyczne singletony mogą zapewnić tę samą funkcjonalność w bardziej modułowym sposób. Jeśli twój singleton potrzebuje globalnego kontekstu (np. aby zarejestrować odbiorników nadawczych), funkcja jej pobierania może być Kontekst, który wewnętrznie używa kontekstu.getApplicationContext () gdy pierwsze zbudowanie singletonu.


Odbicie

Istnieje również inny sposób uzyskania kontekstu aplikacji za pomocą reflection. Odbicie jest często postrzegane z góry w Androidzie i osobiście uważam, że nie powinno być używane w produkcji.

Aby odzyskać kontekst aplikacji musimy wywołać metodę na ukrytej klasie (ActivityThread), która jest dostępna od API 1:

public static Application getApplicationUsingReflection() throws Exception {
    return (Application) Class.forName("android.app.ActivityThread")
            .getMethod("currentApplication").invoke(null, (Object[]) null);
}

Jest jeszcze jedna ukryta Klasa ( AppGlobals ), która zapewnia sposób na uzyskanie kontekstu aplikacji w sposób statyczny. Pobiera kontekst za pomocą ActivityThread, więc naprawdę nie ma różnicy między następującą metodą a tą napisaną powyżej:

public static Application getApplicationUsingReflection() throws Exception {
    return (Application) Class.forName("android.app.AppGlobals")
            .getMethod("getInitialApplication").invoke(null, (Object[]) null);
} 

Szczęśliwego kodowania!
 98
Author: Jared Rummler,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-06-29 22:06:21

Zakładając, że mówimy o pobraniu kontekstu aplikacji, zaimplementowałem go zgodnie z sugestią @ Rohit Ghatol extending Application. Co się wtedy stało, to to, że nie ma gwarancji, że kontekst pobrany w taki sposób zawsze będzie non-null. W momencie, gdy go potrzebujesz, zwykle dlatego, że chcesz zainicjować helpera lub uzyskać zasób, nie możesz opóźnić w czasie; Obsługa przypadku null nie pomoże Ci. Więc zrozumiałem, że w zasadzie walczę z Androidem. Architektura, jak podano w docs

Uwaga: zwykle nie ma potrzeby podklasowania aplikacji. W większości sytuacji statyczne singletony mogą zapewnić tę samą funkcjonalność w bardziej modułowy sposób. Jeśli singleton potrzebuje globalnego kontekstu (na przykład do rejestracji odbiorników nadawczych), Dołącz kontekst.getApplicationContext() jako argument kontekstowy podczas wywoływania metody getinstance() Singletona.

I wyjaśnione przez Dianne Hackborn

Jedynym powodem, dla którego aplikacja istnieje jako coś, z czego możesz czerpać, jest to, że podczas rozwoju przed 1.0 jeden z naszych programistów ciągle mi przeszkadzał, że potrzebuje najwyższego poziomu obiektu aplikacji, z którego mogą czerpać, aby mogli mieć bardziej "normalny" dla nich model aplikacji, I w końcu się poddałem. Na zawsze będę żałował poddania się temu. :)

Ona również sugeruje rozwiązanie tego problemu:

If what you want is niektóre stany globalne, które mogą być współdzielone w różnych częściach aplikacji, użyj singleton. [...] I to prowadzi bardziej naturalnie do tego, jak powinieneś zarządzać tymi rzeczami-inicjować je na żądanie.

Więc to, co zrobiłem, to pozbycie się rozszerzenia aplikacji i przekazanie kontekstu bezpośrednio do getinstance () helpera singleton, zachowując odniesienie do kontekstu aplikacji w prywatnym konstruktorze:

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

Rozmówca następnie przekaże lokalny kontekst do "helper": {]}

Helper.getInstance(myCtx).doSomething();

Aby odpowiedzieć poprawnie na to pytanie: istnieją sposoby statycznego dostępu do kontekstu aplikacji, ale wszystkie powinny być zniechęcane i powinieneś preferować przekazywanie lokalnego kontekstu do getinstance Singletona ().


Dla wszystkich zainteresowanych, można przeczytać bardziej szczegółową wersję na FWD blog

 62
Author: Alessio,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-29 08:37:53

Nie, Nie wydaje mi się. Niestety, utknąłeś w wywołaniu getApplicationContext() z Activity lub jednej z innych podklas Context. Ponadto, to pytanie jest nieco powiązane.

 49
Author: Erich Douglass,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:18:23

Oto nieudokumentowany sposób na uzyskanie aplikacji (która jest kontekstem) z dowolnego miejsca w wątku UI. Opiera się na ukrytej metodzie statycznej ActivityThread.currentApplication(). Powinno działać przynajmniej na Androidzie 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
}

Zauważ, że ta metoda może zwrócić null, np. gdy wywołasz metodę poza wątkiem interfejsu użytkownika, lub aplikacja nie jest powiązana z wątkiem.

Jest jeszcze lepiej użyć @ rohitghatol'S rozwiązanie, jeśli można zmienić aplikację kod.

 39
Author: kennytm,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:18:23

To zależy od tego, do czego używasz kontekstu. Mogę sobie wyobrazić przynajmniej jedną wadę tej metody:

Jeśli próbujesz utworzyć AlertDialog z AlertDialog.Builder, kontekst Application nie zadziała. Myślę, że potrzebujesz kontekstu dla obecnego Activity...

 32
Author: gulchrider,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-15 07:32:32

Droga Kotlina :

Manifest:

<application android:name="MyApplication">

</application>

Kt

class MyApplication: Application() {

    override fun onCreate() {
        super.onCreate()
        instance = this
    }

    companion object {
        lateinit var instance: MyApplication
            private set
    }
}

Możesz następnie uzyskać dostęp do nieruchomości przez MyApplication.instance

 24
Author: phnmnn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-12-04 15:36:23

Jeśli jesteś otwarty na używanie RoboGuice , możesz wprowadzić kontekst do dowolnej klasy. Oto mała próbka Jak to zrobić z RoboGuice 2.0 (beta 4 w momencie pisania tego tekstu)

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) {
        }
    }
}
 11
Author: user605331,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-02-29 14:46:28

Kotlin

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

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

I get Context like

MyApp.mInstance

Lub

MyApp.getContext()
 9
Author: Khemraj,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-06-20 09:12:55

Kiedyś tego użyłem:

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

Jest to poprawny kontekst, którego użyłem podczas uzyskiwania usług systemowych i zadziałałem.

Ale używałem go tylko w modyfikacjach framework/base i nie próbowałem go w aplikacjach na Androida.

A Ostrzeżenie że musisz wiedzieć: podczas rejestracji odbiorników nadawczych z tym kontekstem, to nie będzie działać i otrzymasz:

Java.lang.SecurityException: podany pakiet rozmówcy android nie jest uruchomiony w trakcie ProcessRecord

 8
Author: ungalcrys,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-15 07:47:04

Jeśli nie chcesz modyfikować pliku manifestu, możesz ręcznie zapisać kontekst w zmiennej statycznej w początkowej aktywności:

public class App {
    private static Context context;

    public static void setContext(Context cntxt) {
        context = cntxt;
    }

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

I po prostu ustaw kontekst, gdy rozpocznie się Twoja aktywność (lub działania):

// MainActivity

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    // Set Context
    App.setContext(getApplicationContext());

    // Other stuff
}

Uwaga: podobnie jak wszystkie inne odpowiedzi, jest to potencjalny wyciek pamięci.

 6
Author: Sheharyar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-14 21:02:05

Możesz użyć:

MainActivity.this.getApplicationContext();

Główna aktywność.java:

...
public class MainActivity ... {
    static MainActivity ma;
...
    public void onCreate(Bundle b) {
         super...
         ma=this;
         ...

Każda inna klasa:

public ...
    public ANY_METHOD... {
         Context c = MainActivity.ma.getApplicationContext();
 4
Author: barwnikk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-15 07:45:51

Zgodnie z to źródło możesz uzyskać własny kontekst, rozszerzając 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 for ContextWrapper

Proxy implementacja kontekstu, która po prostu deleguje wszystkie swoje wywołania do innego kontekstu. Może być podklasowany, aby modyfikować zachowanie bez zmiany pierwotnego kontekstu.

 4
Author: BlueWizard,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-13 08:29:11

W Kotlinie, umieszczenie kontekstu / kontekstu aplikacji w obiekcie towarzyszącym nadal powoduje Ostrzeżenie Do not place Android context classes in static fields; this is a memory leak (and also breaks Instant Run)

Lub jeśli używasz czegoś takiego:

    companion object {
        lateinit var instance: MyApp
    }

To po prostu oszukać lint, aby nie odkryć wycieku pamięci, instancja aplikacji nadal może wytworzyć wyciek pamięci, ponieważ Klasa aplikacji i jej potomek są kontekstem.

Alternatywnie możesz użyć funkcjonalnego interfejsu lub właściwości funkcjonalnych, aby pomóc ci uzyskać kontekst aplikacji.

Po prostu utwórz obiekt Klasa:

object CoreHelper {
    lateinit var contextGetter: () -> Context
}

Lub można go używać bezpieczniej używając typu nullable:

object CoreHelper {
    var contextGetter: (() -> Context)? = null
}

I w klasie aplikacji Dodaj ten wiersz:


class MyApp: Application() {

    override fun onCreate() {
        super.onCreate()
        CoreHelper.contextGetter = {
            this
        }
    }
}

I w manifeście zadeklaruj nazwę aplikacji na . MyApp


    <application
            android:name=".MyApp"

Gdy chcesz uzyskać kontekst po prostu zadzwoń:

CoreHelper.contextGetter()

// or if you use the nullable version
CoreHelper.contextGetter?.invoke()
Mam nadzieję, że to pomoże.
 4
Author: Hayi Nukman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-08-03 18:01:00

Myślę, że potrzebujesz ciała do metody getAppContext():

public static Context getAppContext()
   return MyApplication.context; 
 3
Author: Kognos,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-04-30 03:42:25

Właśnie opublikowałem framework inspirowany jQuery dla Androida o nazwie Vapor API , który ma na celu ułatwienie tworzenia aplikacji.

The central $ klasa elewacji utrzymuje a WeakReference (link do awesome Java blog post o tym by Ethan Nicholas) do aktualnego Activity kontekstu, który możesz pobrać wywołując:

$.act()

A WeakReference utrzymuje referencję bez uniemożliwiania zbierania śmieci odzyskiwania oryginalnego obiektu, więc nie powinieneś mieć problem z wyciekami pamięci.

Minusem jest oczywiście to, że ryzykujesz, że $.act() może zwrócić null. Nie natknąłem się jeszcze na ten scenariusz, więc może jest to tylko minimalne ryzyko, warto wspomnieć.

Możesz również ustawić kontekst ręcznie, jeśli nie używasz VaporActivity jako klasa Activity:

$.act(Activity);

Ponadto, wiele frameworków Vapor API używa tego przechowywanego kontekstu z natury, co może oznaczać, że nie musisz go przechowywać samodzielnie, jeśli zdecyduj się na użycie frameworka. Sprawdź stronę , aby uzyskać więcej informacji i próbek.

Mam nadzieję, że to pomoże:)

 2
Author: Darius,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-15 07:44:31

Jeśli z jakiegoś powodu chcesz mieć kontekst aplikacji w dowolnej klasie, nie tylko tych rozszerzających aplikację/aktywność, może dla niektórych klas fabrycznych lub pomocniczych. Możesz dodać następujący singleton do swojej aplikacji.

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

Następnie zainicjalizuj go w klasie aplikacji onCreate za pomocą

GlobalAppContextSingleton.getInstance().initialize(this);

Użyj go w dowolnym miejscu, wywołując

GlobalAppContextSingleton.getInstance().getApplicationContext()

Nie polecam tego podejścia do niczego poza kontekstem aplikacji. Ponieważ może to spowodować wycieki pamięci.

 2
Author: Versa,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-23 14:20:08

Używam wariacji wzoru Singletona, aby mi w tym pomóc.

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

Następnie wywołuję ApplicationContextSingleton.setContext( this ); w mojej aktywności .onCreate () i ApplicationContextSingleton.setContext( null ); w onDestroy();

 1
Author: Bamaco,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-07-23 11:07:37

Odpowiedź Rohita wydaje się prawidłowa. Należy jednak pamiętać, że AndroidStudio "Instant Run" zależy od tego, że nie ma atrybutów static Context w kodzie, o ile wiem.

 1
Author: payne,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-07-12 13:04:46