Android Studio:
Nie umieszczaj klas kontekstu systemu Android w polach statycznych; to jest wyciek pamięci (a także przerywa Instant Run)
A więc 2 pytania:
# 1 Jak wywołać a startService
z metody statycznej bez zmiennej statycznej dla kontekstu?
# 2 Jak wysłać transmisję lokalną metodą statyczną (taką samą)?
Przykłady:
public static void log(int iLogLevel, String sRequest, String sData) {
if(iLogLevel > 0) {
Intent intent = new Intent(mContext, LogService.class);
intent.putExtra("UPDATE_MAIN_ACTIVITY_VIEW", "UPDATE_MAIN_ACTIVITY_VIEW");
mContext.startService(intent);
}
}
lub
Intent intent = new Intent(MAIN_ACTIVITY_RECEIVER_INTENT);
intent.putExtra(MAIN_ACTIVITY_REQUEST_FOR_UPDATE, sRequest));
intent.putExtra(MAIN_ACTIVITY_DATA_FOR_VIEW, sData);
intent.putExtra(MAIN_ACTIVITY_LOG_LEVEL, iLogLevel);
LocalBroadcastManager.getInstance(mContext).sendBroadcast(intent);
Jaki byłby właściwy sposób na zrobienie tego bez używania mContext
?
UWAGA: Myślę, że moim głównym pytaniem może być przekazanie kontekstu do klasy, z której pochodzi metoda wywołująca.
android
android-studio-2.2
John Smith
źródło
źródło
Odpowiedzi:
Po prostu przekaż go jako parametr do swojej metody. Nie ma sensu tworzenie statycznej instancji
Context
wyłącznie w celu uruchomieniaIntent
.Oto jak powinna wyglądać Twoja metoda:
public static void log(int iLogLevel, String sRequest, String sData, Context ctx) { if(iLogLevel > 0) { Intent intent = new Intent(ctx, LogService.class); intent1.putExtra("UPDATE_MAIN_ACTIVITY_VIEW", "UPDATE_MAIN_ACTIVITY_VIEW"); ctx.startService(intent); } }
Aktualizacja po komentarzach do pytania: kaskadowo kontekst od czynności inicjującej (poprzez parametry konstruktora lub parametry metody) aż do momentu, w którym jest to potrzebne.
źródło
MyClass
dodaj konstruktora publicznego, tak jak metodępublic MyClass(Context ctx) { // put this ctx somewhere to use later }
(To jest twój konstruktor) Teraz utwórz nową instancjęMyClass
używając tego konstruktora np.MyClass mc = new MyClass(ctx);
Po prostu upewnij się, że przekazujesz context.getApplicationContext () lub wywołujesz getApplicationContext () w dowolnym kontekście, który jest przekazywany za pośrednictwem metod / konstruktora do Twojego singletona, jeśli zdecydujesz się przechowywać go w dowolnym polu składowym.
idiotyczny przykład (nawet jeśli ktoś przejdzie w działaniu, pobierze kontekst aplikacji i użyje go do utworzenia instancji singletona):
public static synchronized RestClient getInstance(Context context) { if (mInstance == null) { mInstance = new RestClient(context.getApplicationContext()); } return mInstance; }
getApplicationContext () zgodnie z dokumentacją: "Zwróć kontekst pojedynczego, globalnego obiektu Application bieżącego procesu."
Oznacza to, że kontekst zwrócony przez "getApplicationContext ()" przejdzie przez cały proces i dlatego nie ma znaczenia, czy przechowujesz do niego statyczne odniesienie w dowolnym miejscu, ponieważ zawsze będzie tam w czasie wykonywania Twojej aplikacji (i przeżyje wszystkie obiekty / singletony utworzone przez to).
Porównaj to z kontekstem wewnątrz widoków / działań zawierających duże ilości danych, jeśli wyciekniesz kontekst utrzymywany przez działanie, system nie będzie w stanie zwolnić tego zasobu, co oczywiście nie jest dobre.
Odwołanie do działania poprzez jego kontekst powinno przeżywać ten sam cykl życia, co samo działanie, w przeciwnym razie będzie utrzymywać kontekst jako zakładnika powodując wyciek pamięci (co jest przyczyną ostrzeżenia o kłaczkach).
EDYCJA: Dla faceta, który bije przykład z powyższych dokumentów, w kodzie jest nawet sekcja komentarzy o tym, o czym właśnie napisałem:
// getApplicationContext() is key, it keeps you from leaking the // Activity or BroadcastReceiver if someone passes one in.
źródło
To tylko ostrzeżenie. Nie martw się. Jeśli chcesz użyć kontekstu aplikacji, możesz zapisać go w klasie „pojedynczej”, która jest używana do zapisywania całej klasy pojedynczej w Twoim projekcie.
źródło
W twoim przypadku nie ma sensu mieć go jako pola statycznego, ale nie sądzę, że jest to złe we wszystkich przypadkach. Jeśli teraz, co robisz, możesz mieć pole statyczne, które ma kontekst i wyzerować je później. Tworzę instancję statyczną dla mojej głównej klasy modelu, która ma kontekst wewnątrz, kontekst aplikacji, a nie kontekst działania, a także mam pole wystąpienia statycznego klasy zawierające aktywność, które null in przy niszczeniu. Nie widzę, że mam wyciek pamięci. Więc jeśli jakiś sprytny facet uważa, że się mylę, śmiało skomentuj ...
Również Instant Run działa tutaj dobrze ...
źródło
Ogólnie unikaj definiowania pól kontekstu jako statycznych. Samo ostrzeżenie wyjaśnia, dlaczego: to wyciek pamięci. Jednak zerwanie natychmiastowego biegu może nie być największym problemem na naszej planecie.
Istnieją dwa scenariusze, w których otrzymasz to ostrzeżenie. Na przykład (najbardziej oczywisty):
public static Context ctx;
A potem jest nieco trudniejszy, w którym kontekst jest zawarty w klasie:
public class Example{ public Context ctx; //Constructor omitted for brievety }
Ta klasa jest gdzieś zdefiniowana jako statyczna:
public static Example example;
Otrzymasz ostrzeżenie.
Samo rozwiązanie jest dość proste: nie umieszczaj pól kontekstu w instancjach statycznych , czy to w klasie opakowującej, czy też deklarując ją bezpośrednio jako statyczną.
Rozwiązanie ostrzeżenia jest proste: nie umieszczaj pola statycznie. W twoim przypadku przekaż kontekst jako instancję do metody. W przypadku klas, w których wykonywanych jest wiele wywołań Context, użyj konstruktora, aby przekazać kontekst (lub działanie w tym przypadku) do klasy.
Zauważ, że jest to ostrzeżenie, a nie błąd. Jeśli z jakiegoś powodu potrzebujesz statycznego kontekstu, możesz to zrobić. Chociaż tworzysz wyciek pamięci, kiedy to robisz.
źródło
Jeśli upewnisz się, że jest to kontekst aplikacji. To ma znaczenie. Dodaj
@SuppressLint("StaticFieldLeak")
źródło
Służy
WeakReference
do przechowywania kontekstu w klasach Singleton, a ostrzeżenie zniknieprivate WeakReference<Context> context; //Private contructor private WidgetManager(Context context) { this.context = new WeakReference<>(context); } //Singleton public static WidgetManager getInstance(Context context) { if (null == widgetManager) { widgetManager = new WidgetManager(context); } return widgetManager; }
Teraz możesz uzyskać dostęp do kontekstu, takiego jak
if (context.get() instanceof MainActivity) { ((MainActivity) context.get()).startActivityForResult(pickIntent, CODE_REQUEST_PICK_APPWIDGET); }
źródło