Kiedy wywołać kontekst działania LUB kontekst aplikacji?

265

Było wiele postów na temat tego, jakie są te dwa konteksty .. Ale wciąż nie rozumiem tego dobrze

Jak rozumiem do tej pory: każdy jest instancją w swojej klasie, co oznacza, że ​​niektórzy programiści zalecają używanie go this.getApplicationContext()tak często, jak to możliwe, aby nie „wyciec” pamięci. Wynika to z faktu, że drugi this(pobieranie Activitykontekstu instancji) wskazuje, Activityże jest on niszczony za każdym razem, gdy użytkownik przechyla telefon lub opuszcza aplikację itp. Co najwyraźniej Garbage Collector (GC) nie łapie i dlatego zużywa zbyt dużo pamięci ..

Ale czy ktoś może wymyślić kilka naprawdę dobrych przykładów kodowania, w których warto byłoby użyć this(uzyskanie kontekstu bieżącej Activityinstancji), a kontekst aplikacji będzie bezużyteczny / zły?

Norfeldt
źródło

Odpowiedzi:

408

getApplicationContext()prawie zawsze się myli. Pani Hackborn (między innymi) były bardzo wyraźne, że tylko używać getApplicationContext(), gdy wiesz, dlaczego używasz getApplicationContext()i tylko wtedy, gdy trzeba użyć getApplicationContext().

Mówiąc wprost, „niektórzy programiści” używają getApplicationContext()(lub getBaseContext(), w mniejszym stopniu), ponieważ ich doświadczenie z Javą jest ograniczone. Wypełniają wewnętrzną klasy (np An OnClickListenerdla Buttonw sposób Activity) i potrzebują Context. Zamiast MyActivity.thisuzyskiwać dostęp do klasy zewnętrznej this, używają getApplicationContext()lub getBaseContext()uzyskują Contextobiekt.

Ty tylko używać getApplicationContext()kiedy wiedzą Państwo potrzebują Contextczegoś, które mogą żyć dłużej niż jakakolwiek inna prawdopodobnie Contextmasz do dyspozycji. Scenariusze obejmują:

  • Użyj, getApplicationContext()jeśli potrzebujesz czegoś powiązanego z czymś, Contextco samo będzie miało zasięg globalny. Używam getApplicationContext(), na przykład, w WakefulIntentService, do statycznego, WakeLockktóry ma być używany dla usługi. Ponieważ WakeLockjest to statyczne i muszę się Contextdo niego dostać PowerManager, najbezpieczniej jest go używać getApplicationContext().

  • Użyj, getApplicationContext()gdy łączysz się Servicez z Activity, jeśli chcesz przekazać ServiceConnection(tj. Uchwyt do wiązania) między Activityinstancjami przez onRetainNonConfigurationInstance(). Android wewnętrznie śledzi powiązania za ich pośrednictwem ServiceConnectionsi przechowuje odniesienia do tych, Contextsktóre tworzą powiązania. Jeśli utworzysz powiązanie z Activity, nowe Activitywystąpienie będzie zawierało odwołanie do tego, ServiceConnectionktóre ma niejawne odniesienie do starego Activity, a starego Activitynie można wyrzucać.

Niektórzy programiści używają niestandardowych podklas Applicationwłasnych danych globalnych, które pobierają getApplicationContext(). To z pewnością możliwe. Wolę statyczne elementy danych, jeśli tylko z jednego powodu możesz mieć tylko jedenApplication obiekt niestandardowy . Zbudowałem jedną aplikację za pomocą niestandardowego Applicationobiektu i okazało się to bolesne. Pani Hackborn zgadza się również z tym stanowiskiem .

Oto powody, dla których niegetApplicationContext() możesz używać gdziekolwiek jesteś:

  • To nie jest kompletne Context, obsługujące wszystko, co Activityrobi. Różne rzeczy, które spróbujesz z tym zrobić Context, zawiodą, głównie związane z GUI .

  • Może powodować wycieki pamięci, jeśli „ Contextfrom” getApplicationContext()zatrzymuje coś utworzonego przez wywołania, których nie usuwasz. Z Activity, jeśli coś się trzyma, kiedy Activityśmieci zostaną zebrane, wszystko inne również się wypłukuje. ApplicationObiekt pozostaje przez cały okres procesu.

CommonsWare
źródło
1
Dziękuję bardzo za tę odpowiedź. Kolejny link, który znalazłem przed przeczytaniem tej odpowiedzi, może również pomóc niektórym ludziom wyjść. stackoverflow.com/questions/7298731/... - ten link wyjaśnia moje obawy dotyczące wycieku pamięci.
Norfeldt,
27
@Norfeldt: FYI, link w twoim komentarzu prowadzi z powrotem do tej odpowiedzi.
CommonsWare
2
dziękuję .. to był link: stackoverflow.com/questions/5796611/... opisuje wyciek pamięci, którego bałam się uzyskać za pomocą tego
Norfeldt
6
@djaqeel: Ta ostatnia część twojego cytatu jest prawie prawdziwa. Jest lepiej sformułowany jako „nie nadawaj kontekstu Aktywności temu, co będzie żyło dłużej niż Działanie, na przykład statycznemu członkowi danych”. Jednak nadal używasz tylko getApplicationContext()wtedy, gdy dokładnie wiesz , dlaczego potrzebujesz go w danej sytuacji. Nadmuchujesz układ? Skorzystaj z aktywności. Powiązanie z usługą, gdzie potrzebujesz tego powiązania, aby przetrwać zmianę konfiguracji? Użyj getApplicationContext(), aby powiązanie nie było powiązane z Activityinstancją.
CommonsWare
7
@Sever: Omawiam to w mojej odpowiedzi. Dave Smith ma również świetny post na blogu obejmujący konteksty: doubleencore.com/2013/06/context Jego akapit podsumowujący: „W większości przypadków używaj kontekstu bezpośrednio dostępnego z otaczającego komponentu, w którym pracujesz. Możesz bezpiecznie trzymać odwołanie do niego, o ile odwołanie nie wykracza poza cykl życia tego komponentu. Gdy tylko zachodzi potrzeba zapisania odwołania do kontekstu z obiektu, który żyje poza Twoją Aktywnością lub Usługą, nawet tymczasowo, przełącz odnośnik, który zapisujesz do kontekstu aplikacji ”.
CommonsWare
48

Myślę, że na stronie SDK jest wiele słabo udokumentowanych rzeczy, to jedna z nich. Twierdzę, że zamierzam powiedzieć, że wydaje się, że lepiej jest domyślnie używać kontekstu aplikacji i używać kontekstu aktywności tylko wtedy, gdy naprawdę tego potrzebujesz. Jedynym miejscem, w którym kiedykolwiek widziałem, że potrzebujesz kontekstu aktywności, jest okno dialogowe postępu. SBERG412 twierdzi, że musisz użyć kontekstu działania dla wiadomości toast, ale dokumenty systemu Android wyraźnie pokazują używany kontekst aplikacji. Z tego przykładu Google zawsze korzystałem z kontekstu aplikacji dla tostów. Jeśli to źle, Google upuścił piłkę tutaj.

Oto więcej do przemyślenia i przeglądu:

W przypadku wiadomości Toast Przewodnik Google Dev korzysta z kontekstu aplikacji i wyraźnie mówi, aby go użyć: Powiadomienia o Toast

W sekcji okien dialogowych przewodnika dla programistów widać, że AlertDialog.Builder używa kontekstu aplikacji, a następnie pasek postępu używa kontekstu aktywności. To nie jest wyjaśnione przez Google. Dialogi

Wydaje się, że dobrym powodem do korzystania z kontekstu aplikacji jest to, że chcesz obsługiwać zmiany konfiguracji, takie jak zmiana orientacji, i chcesz zachować obiekty, które potrzebują kontekstu, takiego jak Widoki. Jeśli spojrzysz tutaj: Zmiany w czasie wykonywania Zachowaj ostrożność podczas używania kontekstu aktywności, który może spowodować wyciek. Można tego uniknąć dzięki kontekstowi aplikacji z widokami, które należy zachować (przynajmniej tak rozumiem). W aplikacji, którą piszę, zamierzam użyć kontekstu aplikacji, ponieważ próbuję zachować pewne widoki i inne rzeczy przy zmianie orientacji, a nadal chcę, aby aktywność została zniszczona i odtworzona po zmianie orientacji. Dlatego muszę użyć kontekstu aplikacji, aby nie spowodować wycieku pamięci (patrz Unikanie wycieków pamięci). Wydaje mi się, że istnieje wiele dobrych powodów, aby używać kontekstu aplikacji zamiast kontekstu działania, a dla mnie prawie wydaje się, że używałbyś go częściej niż kontekstu działania. Tak właśnie wydaje się robić wiele książek o Androidzie i tak właśnie wygląda większość przykładów Google.

Dokumentacja Google naprawdę wydaje się, że korzystanie z kontekstu aplikacji jest w większości przypadków w porządku i faktycznie pojawia się częściej niż użycie kontekstu aktywności w ich przykładach (przynajmniej w przykładach, które widziałem). Jeśli korzystanie z kontekstu aplikacji jest naprawdę takim problemem, Google naprawdę musi położyć na to większy nacisk. Muszą to wyjaśnić i muszą powtórzyć niektóre ze swoich przykładów. Nie winiłbym tego całkowicie za niedoświadczonych programistów, ponieważ władza (Google) naprawdę sprawia, że ​​wygląda na to, że korzystanie z kontekstów aplikacji nie stanowi problemu.

Andi Jay
źródło
5
Całkowicie się zgadzam. Odpowiedź CommonsWare była dla mnie zaskoczeniem. Cieszę się, że znalazłem to pytanie, ponieważ dokumentacja Google sugeruje, że użycie getApplicationContext może być tak niebezpieczne.
Steve Schwarcz
38

Wykorzystałem tę tabelę jako wskazówkę, kiedy używać różnych typów kontekstu, takich jak kontekst aplikacji (tj .:) getApplicationContext()i kontekst aktywności , a także kontekst BroadcastReceiver :

wprowadź opis zdjęcia tutaj

Wszystkie zasługi należą do oryginalnego autora tutaj, aby uzyskać więcej informacji.

CommonSenseCode
źródło
11

Jakiego kontekstu użyć?

Istnieją dwa rodzaje kontekstu:

  1. Kontekst aplikacji jest powiązany z aplikacją i zawsze będzie taki sam przez cały okres użytkowania aplikacji - nie zmienia się. Jeśli więc używasz Toast, możesz użyć kontekstu aplikacji, a nawet kontekstu aktywności (oba), ponieważ toast może być wyświetlany z dowolnego miejsca w aplikacji i nie jest dołączony do konkretnego okna. Istnieje jednak wiele wyjątków, jeden wyjątek dotyczy sytuacji, gdy trzeba użyć lub przekazać kontekst działania.

  2. Kontekst działania jest powiązany z działaniem i może zostać zniszczony, jeśli działanie zostanie zniszczone - może istnieć wiele działań (więcej niż prawdopodobne) w jednej aplikacji. A czasem absolutnie potrzebujesz uchwytu kontekstu aktywności. Na przykład, jeśli uruchomisz nowe działanie, musisz użyć kontekstu działania w jego zamiarze, aby nowe działanie uruchamiające było połączone z bieżącym działaniem pod względem stosu działań. Możesz jednak użyć kontekstu aplikacji, aby uruchomić nową aktywność, ale musisz ustawić flagę, Intent.FLAG_ACTIVITY_NEW_TASKaby traktować ją jako nowe zadanie.

Rozważmy kilka przypadków:

  • MainActivity.this odnosi się do kontekstu MainActivity, który rozszerza klasę Activity, ale klasa podstawowa (działanie) również rozszerza klasę Context, więc można jej użyć do zaoferowania kontekstu działania.

  • getBaseContext() oferuje kontekst działania.

  • getApplication() oferuje kontekst aplikacji.

  • getApplicationContext() oferuje również kontekst aplikacji.

Aby uzyskać więcej informacji, sprawdź ten link .

Zohra Khan
źródło
Co z przypadkiem, w którym należy wyświetlić AlertDialog w aplikacji, np. Proces asynchroniczny pokazujący wynik. Przykładem tego może być : użytkownik klika pobieranie, uruchamia żądanie pobierania downloadmanager, a gdy odbierany jest gotowy sygnał, powinien wyświetlić okno dialogowe np. „Co chcesz zrobić z tym pobieraniem?”. Moje rozwiązanie (hack) zapisuje najnowsze Activityw static Applicationklasie i żąda bieżącego Activitypo zakończeniu pobierania. Wątpię jednak, aby była to właściwa implementacja. TL; DR Jak wyświetlić AlertDialog w dowolnym miejscu w aplikacji?
CybeX
@KGCybeX Jeśli chcesz wyświetlić cokolwiek i gdziekolwiek w aplikacji po zakończeniu pobierania, musisz ręcznie zarejestrować odbiornik transmisji w swojej aktywności, który nasłuchuje konkretnej wiadomości, którą wyemituje Twoja usługa pobierania, i rób co chcesz po otrzymaniu wiadomości lub załącz Twoja aktywność w tej usłudze bezpośrednio.
ExiRouS,
6

Zastanawiałem się, dlaczego nie używać kontekstu aplikacji dla każdej obsługiwanej operacji. W końcu zmniejsza ryzyko wycieku pamięci i braku kontroli zerowej dla getContext () lub getActivity () (podczas korzystania z wstrzykniętego kontekstu aplikacji lub uzyskanego metodą statyczną z aplikacji). Stwierdzenia, takie jak te pani Hackborn, by używać kontekstu aplikacji tylko w razie potrzeby, nie wydają mi się przekonujące bez wyjaśnienia, dlaczego. Wygląda jednak na to, że znalazłem nieznane, dlaczego:

odkryłem, że występują problemy z niektórymi kombinacjami wersji Androida / urządzenia, które nie są zgodne z tymi zasadami. Na przykład, jeśli mam BroadcastReceiver, któremu przekazano kontekst, i przekonwertuję ten kontekst na kontekst aplikacji, a następnie spróbuję wywołać registerReceiver () w kontekście aplikacji, istnieje wiele przypadków, w których działa to dobrze, ale także wiele przypadków, w których otrzymuję awaria z powodu ReceiverCallNotAllowedException. Te awarie występują w wielu wersjach Androida od API 15 do 22. https://possiblemobile.com/2013/06/context/#comment-2443283153

Ponieważ nie ma gwarancji, że wszystkie operacje opisane jako obsługiwane przez kontekst aplikacji w poniższej tabeli będą działać na wszystkich urządzeniach z Androidem! wprowadź opis zdjęcia tutaj

Malachiasz
źródło
4

Dwa świetne przykłady użycia kontekstu działania a kontekstu aplikacji to wyświetlanie komunikatu Toast lub wbudowanego okna dialogowego, ponieważ użycie kontekstu aplikacji spowoduje wyjątek:

ProgressDialog.show(this, ....);

lub

Toast t = Toast.makeText(this,....);

Oba wymagają informacji z kontekstu działania, które nie są podane w kontekście aplikacji.

SBerg413
źródło
5
Hm .. Którą wersję systemu operacyjnego Android przetestowałeś? Testowałem na 4.4.4 i działa dobrze. Ponadto, jak wspomniano @Andi Jay, oficjalny dokument programisty Androida wykorzystał kontekst aplikacji w swoim przykładowym kodzie. developer.android.com/guide/topics/ui/notifiers/...
김준호
1
@Chińska nazwa, tak, może działać, ale kiedyś w przyszłości ta aplikacja również się zawiesi.
Ojonugwa Jude Ochalifu
1
Kiedy korzystam z kontekstu działania w Toast, przecieka pamięć!
Jemshit Iskenderov
3

Kontekst aplikacji na żywo aż aplikacja żyje tylko i to nie zależy od aktywności życiowej Cyklu ale kontekstowego keep obiektu długowieczne . Jeśli obiekt, z którego korzystasz tymczasowo, korzysta z kontekstu aplikacji i kontekstu działania jest całkowicie przeciwny do kontekstu aplikacji.

Ganesh Katikar
źródło