Tworzę aplikację, która wymaga logowania. Utworzyłem główną aktywność i logowanie.
W onCreate
metodzie głównej aktywności dodałem następujący warunek:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
...
loadSettings();
if(strSessionString == null)
{
login();
}
...
}
onActivityResult
Metoda, która jest wykonywana, gdy formularz logowania kończy wygląda następująco:
@Override
public void onActivityResult(int requestCode,
int resultCode,
Intent data)
{
super.onActivityResult(requestCode, resultCode, data);
switch(requestCode)
{
case(SHOW_SUBACTICITY_LOGIN):
{
if(resultCode == Activity.RESULT_OK)
{
strSessionString = data.getStringExtra(Login.SESSIONSTRING);
connectionAvailable = true;
strUsername = data.getStringExtra(Login.USERNAME);
}
}
}
Problem polega na tym, że formularz logowania pojawia się czasami dwukrotnie ( login()
metoda jest wywoływana dwukrotnie), a także, gdy klawiatura telefonu przesuwa się, formularz logowania pojawia się ponownie i myślę, że problem jest zmienny strSessionString
.
Czy ktoś wie, jak ustawić zmienną globalną, aby uniknąć wyświetlania formularza logowania po pomyślnym uwierzytelnieniu użytkownika?
android
singleton
global-variables
state
Niko Gamulin
źródło
źródło
Odpowiedzi:
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ą dowolnejContext.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: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:
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ń:
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:
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:
i dodaj własne:
źródło
Utwórz tę podklasę
W AndroidManifest.xml dodaj android: name
Przykład
źródło
java.lang.IllegalAccessException: access to class is not allowed
Sugerowany przez Soonil sposób utrzymania stanu aplikacji jest dobry, jednak ma jeden słaby punkt - zdarzają się przypadki, gdy system operacyjny zabija cały proces aplikacji. Oto dokumentacja na ten temat - Procesy i cykle życia .
Rozważ przypadek - Twoja aplikacja przechodzi w tło, ponieważ ktoś do Ciebie dzwoni (aplikacja Telefon jest teraz na pierwszym planie). W takim przypadku && pod pewnymi innymi warunkami (sprawdź powyższy link, aby dowiedzieć się, jakie mogą być), system operacyjny może zabić proces aplikacji, w tym
Application
instancję podklasy. W rezultacie stan zostaje utracony. Gdy później wrócisz do aplikacji, system operacyjny przywróci stos aktywności iApplication
instancję podklasy, jednakmyState
pole to będzienull
.AFAIK, jedynym sposobem na zagwarantowanie bezpieczeństwa stanu jest użycie dowolnego rodzaju utrwalania stanu, np. Użycie prywatnego dla pliku aplikacji lub
SharedPrefernces
(w końcu używa prywatnego dla pliku aplikacji w wewnętrznym systemie plików).źródło
SharedPreferences
; tak to widziałem. Wydaje mi się dziwne nadużywanie systemu preferencji dla zapisanego stanu, ale działa tak dobrze, że kwestia staje się kwestią terminologii.Tylko uwaga ...
Dodaj:
lub jakkolwiek nazwałeś swoją podklasę do istniejącego
<application>
znacznika. Próbowałem dodawać kolejny<application>
znacznik do manifestu i otrzymywałem wyjątek.źródło
Nie mogłem też znaleźć sposobu na określenie znacznika aplikacji, ale po wielu pracach Googling stało się oczywiste z dokumentacji pliku manifestu: użyj androida: nazwa, oprócz domyślnej ikony i etykiety w sekcji aplikacji.
android: nazwa W pełni kwalifikowana nazwa podklasy aplikacji zaimplementowanej dla aplikacji. Po uruchomieniu procesu aplikacji klasa ta jest tworzona przed dowolnym składnikiem aplikacji.
Podklasa jest opcjonalna; większość aplikacji nie będzie go potrzebować. W przypadku braku podklasy system Android korzysta z instancji podstawowej klasy aplikacji.
źródło
Co powiesz na zapewnienie gromadzenia pamięci natywnej za pomocą takich globalnych struktur?
Działania mają
onPause/onDestroy()
metodę wywoływaną po zniszczeniu, ale klasa aplikacji nie ma odpowiedników. Jaki mechanizm jest zalecany, aby zapewnić, że struktury globalne (szczególnie zawierające odniesienia do pamięci natywnej) są odpowiednio usuwane, gdy aplikacja zostanie zabita lub stos zadań zostanie umieszczony w tle?źródło
Wystarczy zdefiniować nazwę aplikacji, jak poniżej, która będzie działać:
źródło
Jak wspomniano powyżej, system operacyjny może zabić APLIKACJĘ bez żadnego powiadomienia (nie ma zdarzenia onDestroy), więc nie ma możliwości zapisania tych zmiennych globalnych.
SharedPreferences może być rozwiązaniem Z WYJĄTKIEM, że masz zmienne COMPLEX STRUCTURED (w moim przypadku miałem tablicę liczb całkowitych do przechowywania identyfikatorów, które użytkownik już obsługiwał). Problem z SharedPreferences polega na tym, że trudno jest przechowywać i pobierać te struktury za każdym razem, gdy potrzebne są wartości.
W moim przypadku miałem SERVICE w tle, więc mogłem przenieść tam te zmienne, a ponieważ usługa ma zdarzenie onDestroy, mogłem łatwo zapisać te wartości.
źródło
Jeśli niektóre zmienne są przechowywane w sqlite i musisz ich używać w większości działań w swojej aplikacji. to aplikacja może być najlepszym sposobem na osiągnięcie tego. Zapytaj o zmienne z bazy danych podczas uruchamiania aplikacji i zapisz je w polu. Następnie możesz użyć tych zmiennych w swoich działaniach.
Więc znajdź właściwą drogę, a nie ma najlepszej drogi.
źródło
Możesz mieć pole statyczne do przechowywania tego rodzaju stanu. Lub umieść go w pakiecie zasobów i przywróć stamtąd na onCreate (pakiet saveInstanceState). Upewnij się tylko, że całkowicie rozumiesz zarządzany cykl życia aplikacji na Androida (np. Dlaczego login () jest wywoływany przy zmianie orientacji klawiatury).
źródło
NIE używaj innego
<application>
znacznika w pliku manifestu. Po prostu dokonaj jednej zmiany w istniejącym<application>
znaczniku, dodaj ten wierszandroid:name=".ApplicationName"
gdzie,ApplicationName
będzie nazwą podklasy (używanej do przechowywania globalnego), którą zamierzasz utworzyć.więc w końcu twój JEDEN I TYLKO
<application>
znacznik w pliku manifestu powinien wyglądać następująco: -źródło
możesz użyć Intents, Sqlite lub Shared Preferencje. Jeśli chodzi o przechowywanie multimediów, takich jak dokumenty, zdjęcia i filmy, możesz zamiast tego utworzyć nowe pliki.
źródło
Możesz to zrobić za pomocą dwóch metod:
Korzystanie z preferencji wspólnych
Korzystanie z klasy aplikacji
Przykład:
Możesz użyć wyższej klasy, aby zaimplementować logowanie w MainActivity, jak poniżej. Kod będzie wyglądał mniej więcej tak:
Ta metoda będzie działać w przypadku tymczasowego przechowywania. Naprawdę nie masz pojęcia, kiedy system operacyjny zabije aplikację z powodu małej ilości pamięci. Gdy aplikacja znajduje się w tle, a użytkownik porusza się po innej aplikacji, która wymaga więcej pamięci do uruchomienia, aplikacja zostanie zabita, ponieważ system operacyjny ma wyższy priorytet dla procesów na pierwszym planie niż tło. Dlatego obiekt aplikacji będzie miał wartość zerową przed wylogowaniem użytkownika. Dlatego w tym celu zalecam użycie drugiej metody określonej powyżej.
Korzystanie z wspólnych preferencji.
źródło
Po aktywacji wynik jest wywoływany przed wznowieniem. Więc przejdź do logowania, aby wznowić, a drugie logowanie może zostać zablokowane, gdy aktywność domieszki zwróci wynik pozytywny. Wznowienie jest wywoływane za każdym razem, więc nie ma obaw, że nie zostanie wywołane za pierwszym razem.
źródło
Podejście do podklasowania zostało również zastosowane przez środowisko BARACUS. Z mojego punktu widzenia podklasowanie Aplikacja była przeznaczona do pracy z cyklami życia Androida; To co dowolny kontener aplikacji. Zamiast mieć globale, rejestruję fasolę w tym kontekście i pozwalam jej wstrzykiwać do dowolnej klasy zarządzanej przez kontekst. Każda iniekcja fasoli jest singletonem.
Zobacz ten przykład, aby uzyskać szczegółowe informacje
Po co wykonywać pracę ręczną, skoro można mieć znacznie więcej?
źródło
źródło
Możesz utworzyć klasę, która rozszerza
Application
klasę, a następnie zadeklarować zmienną jako pole tej klasy i podać dla niej metodę gettera.A następnie, aby uzyskać dostęp do tej zmiennej w swoim działaniu, użyj tego:
źródło