Czy istnieje sposób na umieszczenie bieżącej Context
instancji w metodzie statycznej?
Szukam tego, ponieważ nienawidzę zapisywania instancji „Kontekst” za każdym razem, gdy się zmienia.
Context
, może być lepszy sposób zaprojektowania kodu.
Czy istnieje sposób na umieszczenie bieżącej Context
instancji w metodzie statycznej?
Szukam tego, ponieważ nienawidzę zapisywania instancji „Kontekst” za każdym razem, gdy się zmienia.
Context
, może być lepszy sposób zaprojektowania kodu.
Odpowiedzi:
Zrób to:
W pliku manifestu Androida zadeklaruj następujące informacje.
<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 zadzwoń wszędzie, MyApplication.getAppContext()
aby uzyskać statyczny kontekst aplikacji.
static context
zmienną jako volatile
?
Większość aplikacji, które chcą w wygodny sposób uzyskać kontekst aplikacji, tworzy własną klasę, która się rozszerza android.app.Application
.
PRZEWODNIK
Możesz to zrobić, najpierw tworząc klasę w swoim projekcie w następujący sposób:
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 swoim AndroidManifest należy podać nazwę swojej klasy w tagu AndroidManifest.xml:
<application
...
android:name="com.example.App" >
...
</application>
Następnie możesz pobrać kontekst aplikacji dowolną metodą statyczną, korzystając z następujących czynności:
public static void someMethod() {
Context context = App.getContext();
}
OSTRZEŻENIE
Przed dodaniem do projektu czegoś podobnego do powyższego należy rozważyć, co mówi dokumentacja:
Zwykle nie ma potrzeby dzielenia aplikacji. W większości sytuacji statyczne singletony mogą zapewnić tę samą funkcjonalność w bardziej modułowy sposób. Jeśli twój singleton potrzebuje globalnego kontekstu (na przykład w celu zarejestrowania odbiorników rozgłoszeniowych), funkcja do jego odzyskania może otrzymać kontekst, który wewnętrznie używa Context.getApplicationContext () podczas pierwszej budowy singletonu.
ODBICIE
Istnieje również inny sposób uzyskania kontekstu aplikacji za pomocą odbicia. W Androidzie często spogląda się na refleksję i osobiście uważam, że nie należy tego używać w produkcji.
Aby pobrać 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, ActivityThread
więc tak naprawdę nie ma różnicy między następującą metodą a tą opisaną powyżej:
public static Application getApplicationUsingReflection() throws Exception {
return (Application) Class.forName("android.app.AppGlobals")
.getMethod("getInitialApplication").invoke(null, (Object[]) null);
}
Miłego kodowania!
Zakładając, że mówimy o uzyskaniu kontekstu aplikacji, zaimplementowałem go zgodnie z sugestią @Rohit Ghatol rozszerzającego aplikację. To, co się wtedy wydarzyło, polega na tym, że nie ma gwarancji, że tak pobrany kontekst zawsze będzie różny od zera. Kiedy jest to potrzebne, zwykle dlatego, że chcesz zainicjować pomocnika lub zdobyć zasób, którego nie możesz opóźnić w czasie; załatwienie sprawy zerowej nie pomoże. Zrozumiałem, że zasadniczo walczę z architekturą Androida, jak stwierdzono w dokumentacji
Uwaga: zwykle nie ma potrzeby podklasy aplikacji. W większości sytuacji statyczne singletony mogą zapewnić tę samą funkcjonalność w bardziej modułowy sposób. Jeśli twój singleton potrzebuje globalnego kontekstu (na przykład, aby zarejestrować odbiorniki rozgłoszeniowe), włącz Context.getApplicationContext () jako argument Context podczas wywoływania metody getInstance () twojego singletona.
i wyjaśnione przez Dianne Hackborn
Jedynym powodem, dla którego aplikacja istnieje, jest to, że podczas opracowywania wersji wcześniejszej niż 1.0 jeden z naszych programistów ciągle mnie denerwował, że muszę mieć obiekt aplikacji najwyższego poziomu, z którego mogliby czerpać, aby mogli mieć bardziej „normalny” „dla nich model aplikacji i ostatecznie się poddałem. Na zawsze będę żałować, że się poddałem. :)
Sugeruje również rozwiązanie tego problemu:
Jeśli chcesz uzyskać stan globalny, który można udostępniać w różnych częściach aplikacji, użyj singletonu. [...] A to bardziej naturalnie prowadzi do tego, jak powinieneś zarządzać tymi rzeczami - inicjowanie ich na żądanie.
więc pozbyłem się rozszerzania aplikacji i przekazałem kontekst bezpośrednio do getInstance () pomocnika singletona, jednocześnie zapisując odwołanie 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;
}
}
dzwoniący przekaże następnie lokalny kontekst do pomocnika:
Helper.getInstance(myCtx).doSomething();
Tak więc, aby poprawnie odpowiedzieć na to pytanie: istnieją sposoby na statyczny dostęp do kontekstu aplikacji, ale wszystkie powinny być odradzane i powinieneś preferować przekazywanie lokalnego kontekstu do getInstance () singletona.
Dla wszystkich zainteresowanych możesz przeczytać bardziej szczegółową wersję na blogu fwd
getInstance(ctx)
. Masz katalog główny instance
typu GC MyHelper
, który ma prywatne pole mContext
typu Context
, które odwołuje się do kontekstu aplikacji zebranego za pośrednictwem przekazanego kontekstu getInstance()
. instance
nigdy nie jest ustawiany ani usuwany, więc GC nigdy nie wyłapie kontekstu aplikacji, do którego się odwołuje instance
. Nie wyciekają żadne działania, więc jest to tanie IMO.
this
in Application.onCreate()
, co poprawia akceptowaną odpowiedź.
Nie, nie sądzę, żeby tak było. Niestety utknąłeś dzwoniąc getApplicationContext()
z Activity
jednej lub innej podklasy Context
. Również ta kwestia jest nieco podobne.
Oto nieudokumentowany sposób uzyskania aplikacji (która jest kontekstem) z dowolnego miejsca w wątku interfejsu użytkownika. Opiera się na ukrytej metodzie statycznej ActivityThread.currentApplication()
. Powinien 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.
Nadal lepiej jest użyć rozwiązania @RohitGhatol , jeśli możesz zmienić kod aplikacji.
To zależy od tego, do czego używasz kontekstu. Mogę wymyślić przynajmniej jedną wadę tej metody:
Jeśli próbujesz utworzyć AlertDialog
z AlertDialog.Builder
, Application
kontekst nie będzie działać. Uważam, że potrzebujesz kontekstu dla obecnego Activity
...
Sposób Kotlina :
Oczywisty:
<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
}
}
Następnie można uzyskać dostęp do nieruchomości za pośrednictwem MyApplication.instance
Jeśli jesteś otwarty na używanie RoboGuice , możesz wprowadzić kontekst do dowolnej klasy. Oto mała próbka tego, jak to zrobić za pomocą 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) {
}
}
}
W pewnym momencie użyłem tego:
ActivityThread at = ActivityThread.systemMain();
Context context = at.getSystemContext();
To jest prawidłowy kontekst, z którego korzystałem i korzystałem z usług systemowych.
Ale użyłem go tylko w modyfikacjach szkieletu / bazy i nie wypróbowałem go w aplikacjach na Androida.
Ostrzeżenie , że trzeba wiedzieć: Przy rejestracji dla odbiorników z tym kontekście, to nie będzie działać, a otrzymasz:
java.lang.SecurityException: Dany pakiet dzwoniącego android nie działa w procesie ProcessRecord
open class MyApp : Application() {
override fun onCreate() {
super.onCreate()
mInstance = this
}
companion object {
lateinit var mInstance: MyApp
fun getContext(): Context? {
return mInstance.applicationContext
}
}
}
i uzyskaj jak Kontekst
MyApp.mInstance
lub
MyApp.getContext()
Możesz użyć następujących opcji:
MainActivity.this.getApplicationContext();
MainActivity.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();
Jeśli nie chcesz modyfikować pliku manifestu, możesz ręcznie zapisać kontekst w zmiennej statycznej w początkowej czynnoś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, kiedy 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.
Według tego źródła można 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();
}
}
Proxy implementacja kontekstu, który po prostu deleguje wszystkie swoje wywołania do innego kontekstu. Można go podklasować, aby zmodyfikować zachowanie bez zmiany oryginalnego kontekstu.
Jeśli z jakiegoś powodu chcesz kontekst aplikacji w dowolnej klasie, nie tylko rozszerzającej aplikację / działalność, 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 zainicjuj go w klasie aplikacji onCreate za pomocą
GlobalAppContextSingleton.getInstance().initialize(this);
używaj go w dowolnym miejscu, dzwoniąc
GlobalAppContextSingleton.getInstance().getApplicationContext()
Jednak nie polecam tego podejścia do niczego poza kontekstem aplikacji. Ponieważ może powodować wycieki pamięci.
Używam odmiany wzoru Singleton, 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;
}
}
I wtedy zadzwonić ApplicationContextSingleton.setContext( this );
w moim activity.onCreate () i ApplicationContextSingleton.setContext( null );
w onDestroy () ;
Właśnie wydałem inspirowaną przez jQuery platformę dla systemu Android o nazwie Vapor API która ma na celu uproszczenie tworzenia aplikacji.
Centralna $
klasa elewacji utrzymuje WeakReference
(link do niesamowitego postu na blogu Java na ten temat autorstwa Ethana Nicholasa) do bieżącego Activity
kontekstu, który można odzyskać, wywołując:
$.act()
ZA WeakReference
zachowuje odwołanie, nie zapobiegając odzyskiwaniu oryginalnego obiektu przez śmietnik, więc nie powinieneś mieć problemu z wyciekami pamięci.
Minusem jest oczywiście to, że ryzykujesz $.act()
zerową wartością. Jeszcze nie spotkałem się z tym scenariuszem, więc warto wspomnieć, że jest to tylko minimalne ryzyko.
Możesz także ustawić kontekst ręcznie, jeśli nie używasz VaporActivity
jako swojej Activity
klasy:
$.act(Activity);
Ponadto, większość frameworku API Vapor korzysta z tego przechowywanego kontekstu, co może oznaczać, że nie musisz go wcale przechowywać, jeśli zdecydujesz się użyć frameworka. Sprawdź stronę, aby uzyskać więcej informacji i próbek.
Mam nadzieję że to pomogło :)
Odpowiedź Rohita wydaje się poprawna. Należy jednak pamiętać, że „Natychmiastowe uruchomienie” AndroidStudio zależy od braku static Context
atrybutów w kodzie, o ile wiem.
w Kotlin, umieszczenie kontekstu / kontekstu aplikacji w obiekcie towarzyszącym nadal generuje 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
}
Po prostu oszukujemy strzępy, aby nie wykryły przecieku pamięci, instancja aplikacji nadal może powodować wyciek pamięci, ponieważ klasa aplikacji i jej potomek są kontekstem.
Alternatywnie możesz użyć interfejsu funkcjonalnego lub właściwości funkcjonalnych, aby uzyskać kontekst aplikacji.
Wystarczy utworzyć klasę obiektu:
object CoreHelper {
lateinit var contextGetter: () -> Context
}
lub możesz użyć go bezpieczniej, używając typu zerowalnego:
object CoreHelper {
var contextGetter: (() -> Context)? = null
}
i w swojej klasie aplikacji dodaj ten wiersz:
class MyApp: Application() {
override fun onCreate() {
super.onCreate()
CoreHelper.contextGetter = {
this
}
}
}
i w swoim manifeście zadeklaruj nazwę aplikacji . MyApp
<application
android:name=".MyApp"
Jeśli chcesz uzyskać kontekst, po prostu zadzwoń:
CoreHelper.contextGetter()
// or if you use the nullable version
CoreHelper.contextGetter?.invoke()
Mam nadzieję, że to pomoże.
Spróbuj czegoś takiego
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(); } }