Lipiec 25, 2011
Activity Tutoriale
Problem ze znikającymi danymi podczas rotacji ekranu jest chyba najbardziej powszechnym problemem dla wszystkich początkujących programistów Androida. O tym skąd się on bierze i jak się przed nim zabezpieczyć przeczytacie w tym wpisie.
Problem jest oczywiście bardziej ogólny. Zacznijmy jednak od wytłumaczenia dlaczego najczęściej mamy z nim do czynienia podczas rotacji.
Podczas zmiany orientacji ekranu nasza Aktywność ulega zniszczeniu. Innymi słowy wywoływane są kolejno poniższe metody z jej cyklu życia (opis poszczególnych metod cyklu przedstawiony jest tutaj):
po czym nasza Aktywność znika z pamięci.
Następnie system tworzy nową instancję wyświetlanej Aktywności, tyle, że tym razem w wersji odwróconej. Wywoływane są metody:
i naszym oczom ukazuje się nowa (w pełnym tego słowa znaczeniu) Aktywność.
W związku z tym, że jest to nowa instancja klasy Activity, normalnym jest, że informacje w niej zawarte nie są lub nie muszą być takie same jak w zniszczonej przed chwilą poprzedniej instancji klasy Aktywności.
Taka sytuacja może mieć miejsce również w innych przypadkach. Pomijając zamierzone zamknięcie i otwarcie aplikacji, czasami może się zdarzyć, że nasza Aktywność, która jest niewidoczna, zostanie na chwilę „ubita” w celu odzyskania zasobów. Kiedy zatem będziemy chcieli do niej wrócić, może się okazać, że i tu straciliśmy niektóre dane, ponieważ Aktywność, którą przywołaliśmy nie jest tym samym obiektem, który chwilę wcześniej oglądaliśmy.
Na szczęście aby zapobiec przypadkom utraty danych Aktywności twórcy wprowadzili mechanizm służący do ich chwilowego przechowywania. Co ciekawe działa on domyślnie w każdym okienku, czego efektem jest zachowywanie stanu niektórych elementów UI.
Mała uwaga – należy zatem pamiętać, że nie jest błędem to, że tracimy niektóre dane podczas tworzenia Aktywności na nowo. Raczej usprawnieniem jest to, że niektórych nie tracimy.
Działanie mechanizmu opiera się na dwóch metodach:
Należy pamiętać, że mechanizm ten służy tylko i wyłącznie do chwilowego przechowywania stanu. Wszelkie dane są niszczone po zamknięciu aplikacji, dlatego też nie nadaje się on do innych zastosowań.
Poniżej przedstawię bardzo prosty przykład prezentujący wykorzystanie powyższych metod.
Działanie naszej aplikacji ma polegać na dodawaniu nowych elementów do pola ListView, których treść będziemy wpisywać w polu tekstowym.
Oto plik main.xml opisujący wygląd naszej aplikacji:
Oraz jej kod źródłowy:
Oto jak zachowuje się nasza aplikacja przy rotacji:
Aby zapobiec temu co stało się powyżej, powinniśmy nadpisać dwie metody klasy Activity:
Dodatkowo polecam pozostawienie wywołań tych metod z klasy bazowej (słowo super), ponieważ dzięki temu zapisywany jest stan niektórych elementów interfejsu, o czym pisałem powyżej.
Dla ciekawskich polecam zakomentowanie tych wywołań. Wtedy nie dość, że zostanie usunięta zawartość ListView, to zniknie też wpisany przez nas tekst w polu EditText, który dotychczas był „przekazywany” pomiędzy starą a nową wersją Aktywności.
Do zapisywania stanu korzystamy z metod rozpoczynających się od put…, które wywołujemy na obiekcie Bundle (w naszym przypadku outState):
W tym wypadku do paczki dopisujemy cały ArrayList, w którym przechowywana jest lista dodanych przez nas obiektów String.
Do odczytu, analogicznie stosujemy metody o początku get…:
Po kompletną listę metod do zapisywania/odczytu danych w paczce Bundle odsyłam do dokumentacji.
Tak nasza aplikacja będzie działała dzięki powyższym zabiegom:
Zaloguj się lub skorzystaj z profilu:
Możesz również zostawić komentarz bez rejestracji, korzystając z poniższego formularza:
Musisz być zalogowany aby móc pisać komentarze.
Przechowywanie stanu Aktywności podczas zmiany orientacji ekranu
Dlaczego?
Rozwiązanie
Przykładowa aplikacja
Zapis i odczyt stanu Aktywności
Przygotowania
Komentarze (0)
Prześlij komentarz
- onPause()
- onStop()
- onDestroy()
- onCreate()
- onStart()
- onResume()
- onSaveInstanceState(Bundle outState) – służy do zapisywania stanu i jest wywoływana przed metodą onStop() w cyklu życia Aktywności. Należy jednak pamiętać, że sama nie należy do tego cyklu gdyż wywoływana jest tylko wtedy gdy Aktywność jest niszczona „bez wiedzy” użytkownika (jeżeli użytkownik sam wyłącza okno/aplikację, metoda ta nie jest wołana).
- onRestoreInstanceState(Bundle savedInstanceState) – służy do odzyskiwania stanu i jest wywoływana zaraz po metodzie onStart() Aktywności. Co ciekawe jako jej argument otrzymujemy ten sam obiekt, co metoda onCreate(Bundle), dlatego też większość implementacji przenosi odzyskiwanie stanu właśnie tam.
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> <EditText android:layout_height="wrap_content" android:id="@+id/etText" android:layout_width="fill_parent" android:hint="Type text here..." /> <Button android:layout_height="wrap_content" android:id="@+id/btnAdd" android:text="Add" android:layout_width="100dp" /> <ListView android:id="@+id/listView" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout>
package pl.froger.statesavingexample;
import java.util.ArrayList;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
public class MainActivity extends Activity {
private EditText etText;
private Button btnAdd;
private ListView listView;
private ArrayList<String> itemList;
private ArrayAdapter<String> adapter;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
initUI();
}
private void initUI() {
etText = (EditText) findViewById(R.id.etText);
listView = (ListView) findViewById(R.id.listView);
btnAdd = (Button) findViewById(R.id.btnAdd);
initButtonClick();
initList();
}
private void initButtonClick() {
btnAdd.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
itemList.add(etText.getText().toString());
adapter.notifyDataSetChanged();
}
});
}
private void initList() {
itemList = new ArrayList<String>();
adapter = new ArrayAdapter<String>(getApplicationContext(),
android.R.layout.simple_list_item_1, itemList);
listView.setAdapter(adapter);
}
} @Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
} @Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putStringArrayList("listItems", itemList);
}@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
ArrayList items = savedInstanceState.getStringArrayList("listItems");
for (String item : items) {
itemList.add(item);
super.onRestoreInstanceState(savedInstanceState);
}