Napisałem tę odpowiedź jeszcze w 2009 roku, kiedy Android był stosunkowo nowy i było wiele słabo rozwiniętych obszarów w rozwoju Androida. Dodałem długi aneks na dole tego postu, odnosząc się do pewnej krytyki i wyszczególniając filozoficzne nieporozumienie, które mam z wykorzystaniem Singletonów zamiast Podklasy Klasy. Przeczytaj na własne ryzyko.
ORYGINALNA ODPOWIEDŹ:
Bardziej ogólnym problemem, jaki napotykasz, jest sposób zapisywania stanu w kilku działaniach i we wszystkich częściach aplikacji. Zmienna statyczna (na przykład singleton) jest powszechnym sposobem osiągnięcia tego celu w Javie. Odkryłem jednak, że bardziej eleganckim sposobem w Androidzie jest powiązanie swojego stanu z kontekstem aplikacji.
Jak wiadomo, każde działanie jest również kontekstem, który jest informacją o jego środowisku wykonywania w najszerszym tego słowa znaczeniu. Twoja aplikacja ma również kontekst, a Android gwarantuje, że będzie istnieć jako pojedyncza instancja w całej aplikacji.
Można to zrobić, tworząc własną podklasę android.app.Application , a następnie określ tę klasę w znaczniku aplikacji w swoim manifeście. Teraz Android automatycznie utworzy instancję tej klasy i udostępni ją dla całej aplikacji. Możesz uzyskać do niego dostęp za context
pomocą dowolnej Context.getApplicationContext()
metody ( Activity
zapewnia również metodę, getApplication()
która ma dokładnie taki sam efekt). Poniżej znajduje się niezwykle uproszczony przykład z zastrzeżeniami do naśladowania:
class MyApp extends Application {
private String myState;
public String getState(){
return myState;
}
public void setState(String s){
myState = s;
}
}
class Blah extends Activity {
@Override
public void onCreate(Bundle b){
...
MyApp appState = ((MyApp)getApplicationContext());
String state = appState.getState();
...
}
}
Ma to zasadniczo taki sam efekt, jak użycie zmiennej statycznej lub singletonu, ale całkiem dobrze integruje się z istniejącą strukturą Androida. Pamiętaj, że to nie zadziała w różnych procesach (jeśli Twoja aplikacja będzie jednym z rzadkich, który ma wiele procesów).
Warto zwrócić uwagę na powyższy przykład; załóżmy, że zamiast tego zrobiliśmy coś takiego:
class MyApp extends Application {
private String myState = /* complicated and slow initialization */;
public String getState(){
return myState;
}
}
Teraz ta powolna inicjalizacja (np. Uderzanie w dysk, uderzanie w sieć, blokowanie czegokolwiek itp.) Będzie wykonywana za każdym razem, gdy aplikacja zostanie utworzona! Możesz pomyśleć, że to tylko jeden raz na proces, a ja i tak będę musiał zapłacić koszt, prawda? Na przykład, jak wspomina Dianne Hackborn, jest całkowicie możliwe, że Twój proces zostanie utworzony - tylko - do obsługi zdarzenia transmisji w tle. Jeśli przetwarzanie transmisji nie potrzebuje tego stanu, potencjalnie wykonałeś całą serię skomplikowanych i powolnych operacji za darmo. Leniwa instancja to tutaj nazwa gry. Poniżej przedstawiono nieco bardziej skomplikowany sposób korzystania z aplikacji, który ma sens tylko w przypadku najprostszych zastosowań:
class MyApp extends Application {
private MyStateManager myStateManager = new MyStateManager();
public MyStateManager getStateManager(){
return myStateManager ;
}
}
class MyStateManager {
MyStateManager() {
/* this should be fast */
}
String getState() {
/* if necessary, perform blocking calls here */
/* make sure to deal with any multithreading/synchronicity issues */
...
return state;
}
}
class Blah extends Activity {
@Override
public void onCreate(Bundle b){
...
MyStateManager stateManager = ((MyApp)getApplicationContext()).getStateManager();
String state = stateManager.getState();
...
}
}
Podczas gdy wolę podklasę aplikacji niż używanie tutaj singletonów jako bardziej eleganckiego rozwiązania, wolę programiści używają singletonów, jeśli jest to naprawdę konieczne, zamiast nie myśleć w ogóle o wydajności i wielowątkowych implikacjach skojarzenia stanu z podklasą aplikacji.
UWAGA 1: Również, jak skomentowano, w celu poprawnego powiązania zastąpienia aplikacji z aplikacją, w pliku manifestu niezbędny jest tag. Ponownie zobacz dokumentację Androida, aby uzyskać więcej informacji. Przykład:
<application
android:name="my.application.MyApp"
android:icon="..."
android:label="...">
</application>
UWAGA 2: użytkownik 608578 pyta poniżej, jak to działa z zarządzaniem cyklami życia natywnych obiektów. Nie jestem w stanie w najmniejszym stopniu korzystać z natywnego kodu w Androidzie i nie mam kwalifikacji, aby odpowiedzieć na to, jak to współdziałałoby z moim rozwiązaniem. Jeśli ktoś ma odpowiedź na to pytanie, jestem skłonny je podziękować i umieścić informacje w tym poście dla maksymalnej widoczności.
UZUPEŁNIENIE:
Jak zauważyli niektórzy, nie jest to rozwiązanie dla stanu trwałego , coś, co być może powinienem był bardziej podkreślić w pierwotnej odpowiedzi. Oznacza to, że nie jest to rozwiązanie do zapisywania informacji o użytkownikach lub innych, które mają być utrwalane przez cały czas istnienia aplikacji. Dlatego uważam, że większość krytyki poniżej związanej z zabijaniem aplikacji w dowolnym momencie itp., Jest dyskusyjna, ponieważ wszystko, co kiedykolwiek wymagało utrwalenia na dysku, nie powinno być przechowywane przez podklasę aplikacji. Ma to być rozwiązanie do przechowywania tymczasowego, łatwego do odtworzenia stanu aplikacji (na przykład, czy użytkownik jest zalogowany) i komponentów, które są z natury pojedynczymi instancjami (na przykład menedżerem sieci aplikacji) ( NIE singletonem!).
Dayerman był na tyle uprzejmy, aby wskazać na interesującą rozmowę z Reto Meier i Dianne Hackborn, w której odradza się stosowanie podklas aplikacji na rzecz wzorców Singletona. Somatik zwrócił też uwagę na coś takiego wcześniej, chociaż wtedy tego nie widziałem. Ze względu na rolę Reto i Dianne w utrzymywaniu platformy Android, w dobrej wierze nie mogę zalecić ignorowania ich rad. Co mówią, idzie. Nie zgadzam się z opiniami wyrażonymi w odniesieniu do preferowania Singleton nad podklasami aplikacji. W moim sporze wykorzystam koncepcje najlepiej wyjaśnione w tym objaśnieniu StackExchange wzorca projektowego Singleton, aby nie musiałem definiować terminów w tej odpowiedzi. Gorąco zachęcam do przejrzenia linku przed kontynuowaniem. Punkt po punkcie:
Dianne stwierdza: „Nie ma powodu, aby podklasować z aplikacji. Nie różni się to od utworzenia singletonu ...” To pierwsze twierdzenie jest nieprawidłowe. Istnieją dwa główne powody tego. 1) Klasa aplikacji zapewnia lepszą dożywotnią gwarancję twórcy aplikacji; gwarantowana jest żywotność aplikacji. Singleton nie jest WYŁĄCZNIE związany z czasem życia aplikacji (chociaż jest efektywny). Może to nie stanowić problemu dla przeciętnego programisty aplikacji, ale twierdzę, że jest to dokładnie rodzaj umowy, którą powinien oferować interfejs API systemu Android, i zapewnia on także znacznie większą elastyczność systemowi Android, minimalizując czas życia powiązanego dane. 2) Klasa Application udostępnia twórcy aplikacji z uchwytem pojedynczej instancji dla stanu, co bardzo różni się od posiadacza stanu Singleton. Aby zobaczyć listę różnic, zobacz link objaśniający Singleton powyżej.
Dianne kontynuuje: „... prawdopodobnie będzie to coś, czego będziesz żałować w przyszłości, gdy odkryjesz, że obiekt aplikacji staje się tym wielkim splątanym bałaganem z powodu niezależnej logiki aplikacji”. Z pewnością nie jest to niepoprawne, ale nie jest to powód, aby wybierać Singleton zamiast podklasy aplikacji. Żaden z argumentów Diane nie podaje powodu, dla którego użycie Singletona jest lepsze niż podklasa aplikacji, wszystko, co próbuje ustalić, to, że użycie singletonu nie jest gorsze niż podklasa aplikacji, co moim zdaniem jest fałszywe.
Kontynuuje: „A to bardziej naturalnie prowadzi do tego, jak powinieneś zarządzać tymi rzeczami - inicjowanie ich na żądanie”. Ignoruje to fakt, że nie ma powodu, dla którego nie można zainicjować na żądanie przy użyciu podklasy aplikacji. Znowu nie ma różnicy.
Dianne kończy się słowami: „Sama platforma ma mnóstwo ton singletonów dla wszystkich małych współużytkowanych danych, które utrzymuje dla aplikacji, takich jak pamięci podręczne załadowanych zasobów, pule obiektów itp. Działa świetnie”. Nie twierdzę, że używanie Singletonów nie działa dobrze lub nie jest uzasadnioną alternatywą. Argumentuję, że Singletony nie zawierają tak silnej umowy z systemem Android jak przy użyciu podklasy aplikacji, a ponadto, że używanie Singletonów ogólnie wskazuje na nieelastyczną konstrukcję, której nie można łatwo modyfikować i prowadzi do wielu problemów w przyszłości. IMHO, silna umowa, którą Android API oferuje aplikacjom dla programistów, jest jednym z najbardziej atrakcyjnych i przyjemnych aspektów programowania w Androidzie i pomogła w szybkim wdrożeniu programistów, co doprowadziło platformę Android do sukcesu, jaki osiągnęła dzisiaj.
Dianne również skomentował poniżej, wspominając o dodatkowej wadzie korzystania z podklas Aplikacji, mogą zachęcać lub ułatwiać pisanie kodu o mniejszej wydajności. Jest to bardzo prawdziwe i zedytowałem tę odpowiedź, aby podkreślić znaczenie rozważenia perf i przyjęcia właściwego podejścia, jeśli używasz podklasy aplikacji. Jak twierdzi Dianne, należy pamiętać, że twoja klasa aplikacji będzie tworzona za każdym razem, gdy proces zostanie załadowany (może być wiele razy na raz, jeśli aplikacja działa w wielu procesach!), Nawet jeśli proces jest ładowany tylko dla emisji w tle zdarzenie. Dlatego ważne jest, aby używać klasy Application raczej jako repozytorium wskaźników do współużytkowanych komponentów aplikacji, a nie jako miejsce do przetwarzania!
Pozostawiam wam następującą listę wad singletonów, skradzionych z wcześniejszego linku StackExchange:
- Niemożność korzystania z klas abstrakcyjnych lub interfejsowych;
- Niemożność podziału na podklasy;
- Wysokie sprzężenie w aplikacji (trudne do modyfikacji);
- Trudne do przetestowania (nie można fałszować / wyśmiewać w testach jednostkowych);
- Trudne do zrównoleglenia w przypadku stanu zmiennego (wymaga szerokiego blokowania);
i dodaj własne:
- Niejasna i niemożliwa do zarządzania umowa na całe życie, nieodpowiednia dla rozwoju Androida (lub większości innych);