1. Witamy
Ten praktyczny moduł jest częścią modułu 1: Wprowadzenie do kursu Podstawy programowania na Androida (wersja 2). Najwięcej korzyści z tego kursu odniesiesz, jeśli będziesz wykonywać laboratoria programistyczne w kolejności:
- Pełną listę laboratoriów programistycznych w ramach kursu znajdziesz w artykule Codelabs dla programistów Androida (wersja 2).
- Szczegółowe informacje o kursie, w tym linki do wszystkich rozdziałów z koncepcjami, aplikacji i slajdów, znajdziesz w podręczniku Podstawy programowania na Androida (wersja 2).
Wprowadzenie
W tym praktycznym samouczku dowiesz się więcej o cyklu życia aktywności. Cykl życia to zbiór stanów, w których może znajdować się aktywność w całym okresie jej istnienia, od momentu utworzenia do momentu jej usunięcia i odzyskania przez system jej zasobów. Gdy użytkownik przełącza się między aktywnościami w aplikacji (a także wchodzi do niej i z niej wychodzi), aktywności przechodzą przez różne stany w swoim cyklu życia.
Każdy etap cyklu aktywności ma odpowiednią metodę wywołania: onCreate(), onStart(), onPause() itd. Gdy aktywność zmieni stan, zostanie wywołana powiązana metoda wywołania zwrotnego. Jedną z tych metod, które już znasz, jest onCreate(). Zastępując dowolną metodę wywołania cyklu życia w klasach Activity, możesz zmienić domyślne zachowanie aktywności w reakcji na działania użytkownika lub systemu.
Stan aktywności może się też zmieniać w odpowiedzi na zmiany konfiguracji urządzenia, np. gdy użytkownik obróci urządzenie z poziomego na pionowe. Gdy nastąpią te zmiany konfiguracji, aktywność zostanie zniszczona i powtórnie utworzona w stanie domyślnym, a użytkownik może utracić informacje wprowadzone w ramach tej aktywności. Aby uniknąć dezorientacji użytkowników, zadbaj o to, aby aplikacja zapobiegała nieoczekiwanemu utracie danych. W dalszej części tego praktycznego kursu eksperymentujesz z zmianami konfiguracji i dowiesz się, jak zachować stan aktywności w odpowiedzi na zmiany konfiguracji urządzenia i inne zdarzenia cyklu życia aktywności.
W tej części praktycznej dodasz do aplikacji TwoActivities instrukcje logowania i obserwujesz zmiany w cyklu życia aktywności podczas korzystania z aplikacji. Następnie zaczniesz pracować z tymi zmianami i poznasz sposób obsługi danych wejściowych użytkownika w tych warunkach.
Wymagania wstępne
Powinieneś mieć możliwość:
- Utwórz i uruchom projekt aplikacji w Android Studio.
- dodawać do aplikacji instrukcje logowania i wyświetlać te logi w panelu Logcat;
- zrozumieć działanie aktywności i intencji oraz umieć z nimi pracować,
Czego się nauczysz
- Jak działa cykl życia działania
- Gdy aktywność rozpoczyna się, jest wstrzymywana, kończy się i zostaje zniszczona.
- Informacje o metodach wywołania powiązanych z cyklem życia i zmianami w działaniach.
- Efekt działań (np. zmian konfiguracji), które mogą powodować zdarzenia cyklu życia aktywności.
- Jak zachować stan działania w zdarzeniu cyklu życia.
Jakie zadania wykonasz
- Dodaj do aplikacji TwoActivities kod z poprzedniego praktycznego ćwiczenia, aby zaimplementować różne wywołania zwrotne cyklu działania aktywności, które obejmują instrukcje rejestrowania.
- Obserwuj zmiany stanu podczas działania aplikacji i interakcji z każdą aktywnością w aplikacji.
- Zmodyfikuj aplikację, aby zachować stan instancji aktywności, która została nieoczekiwanie utworzona w odpowiedzi na zachowanie użytkownika lub zmianę konfiguracji na urządzeniu.
2. Aplikacje ogółem
W tym praktycznym ćwiczeniu dodasz do aplikacji TwoActivities. Wygląd i działanie aplikacji są mniej więcej takie same jak w poprzednim ćwiczeniu. Zawiera 2 implementacje aktywności i umożliwia użytkownikowi wysyłanie danych między nimi. Zmiany wprowadzone w aplikacji w ramach tego ćwiczenia nie wpłyną na widoczne zachowanie użytkowników.
3. 3. Zadanie 1. Dodaj wywołania zwrotne cyklu życia do klasy TwoActivities
W tym zadaniu zaimplementujesz wszystkie metody wywołania cyklu życia aktywności, aby wyprowadzać komunikaty do logcat podczas wywoływania tych metod. Te komunikaty dziennika pozwolą Ci zobaczyć, kiedy cykl życia aktywności zmienia stan, i jak te zmiany stanu wpływają na działanie aplikacji.
1.1 (Opcjonalnie) Skopiuj projekt TwoActivities
W zadaniach praktycznych w tym module zmodyfikujesz istniejący projekt TwoActivities utworzony w poprzednim module praktycznym. Jeśli wolisz zachować poprzedni projekt TwoActivities, wykonaj czynności opisane w sekcji Dodatek: narzędzia, aby utworzyć kopię projektu.
1.2 Wdrażanie wywołań zwrotnych w MainActivity
- Otwórz projekt TwoActivities w Android Studio i w panelu Projekt > Android otwórz MainActivity.
- W metodzie onCreate() dodaj te instrukcje logowania:
Log.d(LOG_TAG, "-------");
Log.d(LOG_TAG, "onCreate");
- Dodaj zastąpienie dla wywołania zwrotnego onStart() z oświadczeniem w dzienniku dla tego zdarzenia:
@Override
public void onStart(){
super.onStart();
Log.d(LOG_TAG, "onStart");
}
Aby użyć skrótu, w Android Studio kliknij Kod > Zastąpij metody. Pojawi się okno z wszystkimi metodami, które możesz zastąpić w swoich zajęciach. Wybranie co najmniej 1 metody wywołania zwrotnego z listy spowoduje wstawienie pełnego szablonu dla tych metod, w tym wymaganego wywołania superklasy.
- Użyj metody onStart() jako szablonu do implementacji wywołań zwrotnych cyklu życia onPause(), onRestart(), onResume(), onStop() i onDestroy().
Wszystkie metody wywołania mają te same sygnatury (z wyjątkiem nazwy). Jeśli skopiujesz i wkleisz metodę onStart(), aby utworzyć inne metody wywołania, pamiętaj, aby zaktualizować zawartość, aby wywołać właściwą metodę w superklasie, i zapisać właściwą metodę.
- Uruchom aplikację.
- Kliknij kartę Logcat u dołu Android Studio, aby wyświetlić panel Logcat. Powinny pojawić się 3 wiadomości dziennika, które pokazują 3 stany cyklu życia, przez które przeszła aktywność po rozpoczęciu:
D/MainActivity: -------
D/MainActivity: onCreate
D/MainActivity: onStart
D/MainActivity: onResume
1.3 Wdróż wywołania zwrotne cyklu życia w klasie SecondActivity
Teraz, gdy zaimplementujesz metody wywołania cyklu życia w MainActivity, zrób to samo w przypadku SecondActivity.
- Otwórz SecondActivity.
- U góry klasy dodaj stałą dla zmiennej LOG_TAG:
private static final String LOG_TAG = SecondActivity.class.getSimpleName();
- Dodaj do drugiej aktywności wywołania zwrotne cyklu życia i polecenia logowania. (metody wywołania możesz skopiować z MainActivity).
- Dodaj instrukcję logowania do metody returnReply() tuż przed metodą finish():
Log.d(LOG_TAG, "End SecondActivity");
1.4 Obserwuj log podczas działania aplikacji**
- Uruchom aplikację.
- Kliknij kartę Logcat u dołu Android Studio, aby wyświetlić panel Logcat.
- Wpisz Aktywność w polu wyszukiwania. Dziennik logcat na Androidzie może być bardzo długi i nieuporządkowany. Ponieważ zmienna LOG_TAG w każdej klasie zawiera albo słowo MainActivity, albo SecondActivity, to słowo kluczowe pozwala filtrować log tylko pod kątem interesujących Cię elementów.
Eksperymentuj z aplikacją i zauważ, że zdarzenia cyklu życia występują w odpowiedzi na różne działania. Spróbuj wykonać te czynności:
- Używaj aplikacji normalnie (wysyłaj wiadomości, odpowiadaj na wiadomości).
- Aby wrócić z drugiej czynności do czynności głównej, użyj przycisku Wstecz.
- Aby wrócić z drugiej aktywności do głównej, kliknij strzałkę w górę na pasku aplikacji.
- Obróć urządzenie w różnych momentach w głównej i drugiej aktywności w aplikacji i obserwuj, co dzieje się w dzienniku i na ekranie.
- Naciśnij przycisk Przegląd (kwadratowy przycisk po prawej stronie przycisku Strona główna) i zamknij aplikację (kliknij X).
- Wróć do ekranu głównego i uruchom ponownie aplikację.
WSKAZÓWKA: jeśli aplikacja działa w emulatorze, możesz symulować obrót za pomocą klawiszy Ctrl + F11 lub Ctrl + Fn + F11.
Kod rozwiązania zadania 1
Poniższe fragmenty kodu pokazują kod rozwiązania pierwszego zadania.
MainActivity
Poniższe fragmenty kodu pokazują dodany kod w MainActivity, ale nie całą klasę.
Metoda onCreate():
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Log the start of the onCreate() method.
Log.d(LOG_TAG, "-------");
Log.d(LOG_TAG, "onCreate");
// Initialize all the view variables.
mMessageEditText = findViewById(R.id.editText_main);
mReplyHeadTextView = findViewById(R.id.text_header_reply);
mReplyTextView = findViewById(R.id.text_message_reply);
}
Inne metody cyklu życia:
@Override
protected void onStart() {
super.onStart();
Log.d(LOG_TAG, "onStart");
}
@Override
protected void onPause() {
super.onPause();
Log.d(LOG_TAG, "onPause");
}
@Override
protected void onRestart() {
super.onRestart();
Log.d(LOG_TAG, "onRestart");
}
@Override
protected void onResume() {
super.onResume();
Log.d(LOG_TAG, "onResume");
}
@Override
protected void onStop() {
super.onStop();
Log.d(LOG_TAG, "onStop");
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.d(LOG_TAG, "onDestroy");
}
SecondActivity
Poniższe fragmenty kodu pokazują dodany kod w klasie SecondActivity, ale nie całą klasę.
U góry klasy SecondActivity:
private static final String LOG_TAG = SecondActivity.class.getSimpleName();
Metoda returnReply():
public void returnReply(View view) {
String reply = mReply.getText().toString();
Intent replyIntent = new Intent();
replyIntent.putExtra(EXTRA_REPLY, reply);
setResult(RESULT_OK, replyIntent);
Log.d(LOG_TAG, "End SecondActivity");
finish();
}
Inne metody cyklu życia:
To samo co w MainActivity powyżej.
4. 4. Zadanie 2. Zapisywanie i przywracanie stanu instancji aktywności
W zależności od zasobów systemowych i zachowania użytkowników poszczególne czynności w aplikacji mogą być niszczone i odtwarzane częściej, niż Ci się wydaje.
Możesz zauważyć to zachowanie w ostatniej sekcji, gdy obrócisz urządzenie lub jego emulator. Obrócenie urządzenia to jeden z przykładów zmiany konfiguracji urządzenia. Chociaż rotacja jest najczęstszą, wszystkie zmiany konfiguracji powodują zniszczenie bieżącej aktywności i jej ponowne utworzenie, jakby była nowa. Jeśli nie uwzględnisz tego zachowania w kodzie, po zmianie konfiguracji układ aktywności może powrócić do domyślnego wyglądu i wartości początkowych, a użytkownicy mogą utracić swoje miejsce, dane lub stan postępów w aplikacji.
Stan każdej aktywności jest przechowywany jako zestaw par klucz-wartość w obiekcie pakietu o nazwie stan instancji aktywności. System zapisuje informacje o domyślnym stanie w pakiecie stanu instancji tuż przed zatrzymaniem aktywności i przekazuje ten pakiet do nowej instancji aktywności w celu przywrócenia.
Aby uniknąć utraty danych w komponencie Activity, gdy zostanie on nieoczekiwanie zniszczony i powtórnie utworzony, musisz zaimplementować metodę onSaveInstanceState(). System wywołuje tę metodę w Twojej aktywności (między onPause() a onStop()), gdy istnieje możliwość jej zniszczenia i ponownego utworzenia.
Dane zapisane w stanie instancji są specyficzne tylko dla tej instancji danej aktywności w bieżącej sesji aplikacji. Gdy zatrzymasz i ponownie uruchomisz nową sesję aplikacji, stan instancji aktywności zostanie utracony, a wygląd aktywności wróci do domyślnego. Jeśli chcesz zapisywać dane użytkowników między sesjami aplikacji, użyj udostępnionych preferencji lub bazy danych. Więcej informacji na ten temat znajdziesz w jednym z następnych praktycznych ćwiczeń.
2.1 Zapisz stan instancji Activity za pomocą onSaveInstanceState()
Zauważysz pewnie, że obrócenie urządzenia w żaden sposób nie wpływa na stan drugiej czynności. Dzieje się tak, ponieważ drugi układ i stan działania są generowane na podstawie układu i intencji, które je aktywowały. Nawet jeśli aktywność zostanie ponownie utworzona, intencja nadal będzie dostępna i dane w niej zawarte będą nadal używane za każdym razem, gdy wywoływana jest metoda onCreate() w drugiej aktywności.
Możesz też zauważyć, że w każdej aktywności tekst wpisany w elementach EditText wiadomości lub odpowiedzi jest zachowywany nawet po obróceniu urządzenia. Dzieje się tak, ponieważ informacje o stanie niektórych elementów widoku w układzie są automatycznie zapisywane po zmianach konfiguracji, a bieżąca wartość elementu EditText jest jednym z takich przypadków.
Jedynymi elementami stanu aktywności, które Cię interesują, są elementy TextView nagłówka odpowiedzi i tekst odpowiedzi w głównej aktywności. Oba elementy TextView są domyślnie niewidoczne; pojawiają się dopiero po wysłaniu wiadomości z drugiej aktywności do głównej aktywności.
W tym zadaniu dodasz kod, który zachowa stan instancji tych 2 elementów TextView za pomocą metody onSaveInstanceState().
- Otwórz MainActivity.
- Dodaj do aktywności tę podstawową implementację metody onSaveInstanceState() lub użyj opcji Kod > Zastąp metody, aby wstawić podstawową zastępczą implementację.
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
}
- Sprawdź, czy nagłówek jest obecnie widoczny, a jeśli tak, zapisz stan widoczności w bundle stanu za pomocą metody putBoolean() i klucza „reply_visible”.
if (mReplyHeadTextView.getVisibility() == View.VISIBLE) {
outState.putBoolean("reply_visible", true);
}
Pamiętaj, że nagłówek i tekst odpowiedzi są niewidoczne, dopóki nie otrzymasz odpowiedzi z drugiej aktywności. Jeśli nagłówek jest widoczny, oznacza to, że są dane odpowiedzi, które należy zapisać. Pamiętaj, że interesuje nas tylko stan widoczności – nie musisz zapisywać rzeczywistego tekstu nagłówka, ponieważ nigdy się nie zmienia.
- W tym samym teście dodaj tekst odpowiedzi do pakietu.
outState.putString("reply_text",mReplyTextView.getText().toString());
Jeśli nagłówek jest widoczny, możesz założyć, że sama wiadomość z odpowiedzią też jest widoczna. Nie musisz testować ani zapisywać bieżącego stanu widoczności wiadomości z odpowiedzią. Do pakietu stanu trafia tylko tekst wiadomości z kluczem „reply_text”.
Stan zapisywany jest tylko dla tych elementów widoku, które mogą ulec zmianie po utworzeniu aktywności. Inne elementy widoku w aplikacji (EditText, Button) można w każdej chwili odtworzyć na podstawie układu domyślnego.
Pamiętaj, że system zapisuje stan niektórych elementów widoku, takich jak zawartość pola tekstowego EditText.
2.2 Przywróć stan instancji aktywności w metodzie onCreate()
Po zapisaniu stanu instancji aktywności musisz go też przywrócić, gdy zostanie ona ponownie utworzona. Możesz to zrobić w metodzie onCreate() lub za pomocą wywołania zwrotnego onRestoreInstanceState(), który jest wywoływany po metodzie onStart() po utworzeniu aktywności.
W większości przypadków lepszym miejscem do przywracania stanu aktywności jest metoda onCreate(), która zapewnia, że interfejs użytkownika, w tym stan, jest dostępny jak najszybciej. Czasami wygodniej jest to zrobić w metodzie onRestoreInstanceState() po zakończeniu inicjalizacji lub umożliwić podklasom podjęcie decyzji o tym, czy mają użyć domyślnej implementacji.
- W metodzie onCreate() po zainicjowaniu zmiennych View za pomocą findViewById() dodaj test, aby upewnić się, że savedInstanceState nie jest null.
// Initialize all the view variables.
mMessageEditText = findViewById(R.id.editText_main);
mReplyHeadTextView = findViewById(R.id.text_header_reply);
mReplyTextView = findViewById(R.id.text_message_reply);
// Restore the state.
if (savedInstanceState != null) {
}
Gdy Aktywność zostanie utworzona, system przekaże do onCreate() jako jedyny argument stan Bundle. Gdy po raz pierwszy wywołasz metodę onCreate() i uruchamiasz aplikację, Bundle jest nullem – przy pierwszym uruchomieniu aplikacji nie ma jeszcze żadnego stanu. Kolejne wywołania metody onCreate() mają pakiet wypełniony danymi zapisanymi w metodie onSaveInstanceState().
- W ramach tej weryfikacji pobierz bieżącą widoczność (prawda lub fałsz) z elementu Bundle za pomocą klucza „reply_visible”.
if (savedInstanceState != null) {
boolean isVisible =
savedInstanceState.getBoolean("reply_visible");
}
- Poniżej tego wiersza dodaj test dla zmiennej isVisible.
if (isVisible) {
}
Jeśli w pakiecie stanów jest klucz reply_visible (a wartość isVisible jest więc równa true), musisz przywrócić stan.
- W teście isVisible ustaw nagłówek jako widoczny.
mReplyHeadTextView.setVisibility(View.VISIBLE);
- Pobierz wiadomość tekstową z pakietu za pomocą klucza „reply_text” i ustaw odpowiedź TextView, aby wyświetlić ten ciąg znaków.
mReplyTextView.setText(savedInstanceState.getString("reply_text"));
- Upewnij się, że widoczny jest też element TextView odpowiedzi:
mReplyTextView.setVisibility(View.VISIBLE);
- Uruchom aplikację. Spróbuj obrócić urządzenie lub emulator, aby sprawdzić, czy wiadomość z odpowiedzią (jeśli taka istnieje) pozostaje na ekranie po ponownym utworzeniu aktywności.
Kod rozwiązania zadania 2
Poniższe fragmenty kodu pokazują rozwiązanie tego zadania.
MainActivity
Poniższe fragmenty kodu pokazują dodany kod w MainActivity, ale nie całą klasę.
Metoda onSaveInstanceState():
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// If the heading is visible, message needs to be saved.
// Otherwise we're still using default layout.
if (mReplyHeadTextView.getVisibility() == View.VISIBLE) {
outState.putBoolean("reply_visible", true);
outState.putString("reply_text",
mReplyTextView.getText().toString());
}
}
Metoda onCreate():
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d(LOG_TAG, "-------");
Log.d(LOG_TAG, "onCreate");
// Initialize all the view variables.
mMessageEditText = findViewById(R.id.editText_main);
mReplyHeadTextView = findViewById(R.id.text_header_reply);
mReplyTextView = findViewById(R.id.text_message_reply);
// Restore the saved state.
// See onSaveInstanceState() for what gets saved.
if (savedInstanceState != null) {
boolean isVisible =
savedInstanceState.getBoolean("reply_visible");
// Show both the header and the message views. If isVisible is
// false or missing from the bundle, use the default layout.
if (isVisible) {
mReplyHeadTextView.setVisibility(View.VISIBLE);
mReplyTextView.setText(savedInstanceState
.getString("reply_text"));
mReplyTextView.setVisibility(View.VISIBLE);
}
}
}
Ukończony projekt:
Projekt w Android Studio: TwoActivitiesLifecycle
5. Kodowanie
Wyzwanie: utwórz prostą aplikację do tworzenia listy zakupów z główną aktywnością dla listy tworzonej przez użytkownika i drugą aktywnością dla listy typowych produktów.
- Główna aktywność powinna zawierać listę do utworzenia, która powinna składać się z 10 pustych elementów TextView.
- Przycisk „Dodaj produkt” w głównej czynności uruchamia drugą czynność, która zawiera listę typowych produktów (ser, ryż, jabłka itp.). Do wyświetlania elementów użyj elementów przycisku.
- Wybranie elementu powoduje powrót użytkownika do głównej czynności i zaktualizowanie pustego elementu TextView, aby uwzględnić wybrany element.
Użyj intencji, aby przekazać informacje z jednego Activity do drugiego. Upewnij się, że bieżący stan listy zakupów jest zapisywany, gdy użytkownik obróci urządzenie.
6. Podsumowanie
- Cykl życia aktywności to zbiór stanów, przez które przechodzi aktywność, począwszy od jej utworzenia, a skończywszy na odzyskaniu przez system Android zasobów dla tej aktywności.
- Gdy użytkownik przechodzi z jednego Activity do drugiego, a także w ramach aplikacji i poza nią, każde Activity przechodzi przez kolejne stany w swoim cyklu życia.
- Każdy stan w cyklu życia aktywności ma odpowiednią metodę wywołania zwrotnego, którą możesz zastąpić w klasie Activity.
- Metody cyklu życia to onCreate(), onStart(), onPause(), onRestart(), onResume(), onStop(), onDestroy().
- Przesłonięcie metody wywołania cyklu życia umożliwia dodanie zachowania, które występuje, gdy aktywność przechodzi w dany stan.
- W Android Studio możesz dodawać do klas metody zastępcze szkieletu za pomocą opcji Kod > Zastąpić.
- Zmiany konfiguracji urządzenia, takie jak obracanie, powodują, że aktywność jest usuwana i powtórnie tworzona, jakby była nowa.
- Część stanu aktywności jest zachowana po zmianie konfiguracji, w tym bieżące wartości elementów EditText. W przypadku wszystkich innych danych musisz je zapisać samodzielnie.
- Zapisz stan instancji aktywności w metodzie onSaveInstanceState().
- Dane stanu instancji są przechowywane w postaci prostych par klucz-wartość w pakiecie. Używaj metod pakietu do umieszczania danych w pakiecie i wybierania ich z niego.
- Przywróć stan instancji w metodzie onCreate(), która jest preferowaną metodą, lub w metodzie onRestoreInstanceState().