4 komentarze

Activity – podstawowe informacje, cykl życia

Lipiec 8, 2011 Activity Podstawowe komponenty Tutoriale

Activity (Aktywność) to jeden z podstawowych komponentów systemu Android. Klasa ta (a raczej jej podklasy) odpowiedzialna jest za interakcję z użytkownikiem, tworzenie okna naszej aplikacji i uruchamianie innych podstawowych komponentów systemowych. Zazwyczaj jedna podklasa Activity reprezentuje jedno okno naszej aplikacji.
W tym artykule zagłębimy się w jej budowę, cykl życia i kilka innych mechanizmów z nią związanych.

Przygotowanie

Przed przystąpieniem do tego artykuły powinniśmy stworzyć nowy projekt Androidowy w Eclipse. W opisie zakładam, że będzie się on nazywał HelloActivities, a główna Aktywność będzie nosiła nazwę MainActivity.

Tworzenie nowego Activity

Nowa klasa AktywnościNew - Class

Pierwszą czynnością, jaką się zajmiemy będzie stworzenie nowej Aktywności w naszym projekcie. Aby się do tego zabrać musimy stworzyć klasę, która dziedziczy po Activity. Klikamy więc prawym przyciskiem myszy na pakiet, w którym umieszczona jest główna Aktywność -> New -> Class.

W tym momencie powinniśmy zobaczyć kreator tworzenia nowej klasy. Wypełniamy pola name (nazwa klasy – ja przyjąłem, że będzie to AnotherActivity) oraz Superclass (klasa, z której dziedziczymy – wybieramy android.app.Activity).
Po zakończeniu edycji powinniśmy zobaczyć pusty szkielet naszej klasy, w którym od razu przeciążymy metodę onCreate():

package pl.froger.helloactivities;

import android.app.Activity;
import android.os.Bundle;

public class AnotherActivity extends Activity {
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
	}
}

Layout dla nowej Aktywności

Kolejną rzeczą, którą należy wykonać jest stworzenie wyglądu naszego Activity. Możemy to zrobić poprzez dodanie nowego pliku .xml do katalogu res/layout/. My jednak wykorzystamy edytor dostarczony wraz z wtyczką ADT. Klikamy prawym przyciskiem myszy na nasz projekt -> New -> Other -> wyszukujemy Android XML File, który zaznaczamy, po czym ukazuje nam się kreator Androidowego pliku XML.

Wypełniamy następujące pola:

  • File – nazwa pliku (na potrzeby tego wpisy zakładam another.xml)
  • Typ – Layout
  • Folder – /res/layout
  • Root elementLinearLayout

Po zatwierdzeniu powinien ukazać nam się plik z layoutem. Aby nie komplikować, dodamy do niego jedynie TextView z jakimś ciągiem znaków.

Teraz pozostało nam już tylko załadowanie layoutu w naszej nowej Aktywności:

...
public class AnotherActivity extends Activity {
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.another);
	}
}

Informacja o nowej Aktywności w AndroidManifest.xml

Ostatnią czynnością, którą należy wykonać jest zadeklarowanie naszej nowej Aktywności w pliku AndroidManifest.xml. W naszym przypadku sprowadza się to do dodania kolejnego elementu do węzła <application> (dodam, że zwykle jest to trochę więcej niż 1 linijka kodu ;) ).

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="pl.froger.helloactivities" android:versionCode="1"
      android:versionName="1.0">
    <uses-sdk android:minSdkVersion="8" />
    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name=".MainActivity" android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name=".AnotherActivity"></activity>
    </application>
</manifest>

W tym momencie możemy być pewni, że nasza aplikacja posiada drugą Aktywność czekającą w gotowości na uruchomienie. O tym jak to zrobić napiszę jeszcze w tym artykule.

Cykl życia Activity

Zanim zagłębimy się w cykl życia Androidowych okienek, warto uzmysłowić sobie po co w ogóle został on wprowadzony.

Activity Stack

Przede wszystkim warto jest wiedzieć, że wszystkie Aktywności (niezależnie od tego do jakiej aplikacji należą) znajdują się na wspólnym stosie. Kiedy nowa Aktywność jest uruchamiana, zostaje ona umieszczona na jego szczycie. I właśnie na podstawie pozycji na stosie ustalana jest „ważność” danego Activity (im niżej znajduje się obiekt Aktywności tym mniejszy ma priorytet).
Priorytety natomiast wykorzystywane są w procesie odzyskiwania zasobów. Kiedy bowiem systemowi zaczyna ich brakować, może zostać podjęta decyzja o usunięciu z pamięci obiektów Aktywności. I jak nietrudno się domyślić, usuwane są te, które znajdują się najniżej na stosie.

Aby taki zabieg nie miał żadnych negatywnych skutków wprowadzono cykl życia Aktywności, który pozwala ustalić jak w danych sytuacjach powinna zachowywać się aktywność.

Opis cyklu życia

Activity lifecycle

Źródło: http://developer.android.com/reference/android/app/Activity.html

Jak widać, cykl życia składa się z kilku metod (które wykorzystujemy poprzez ich przeciążanie w naszej klasie):

  • onCreate() – metoda, która powinna zostać użyta do inicjalizacji Aktywności. To tutaj powinien zostać zbudowany interfejs użytkownika, powiązanane dane z kontrolerami, ewentualnie stworzone Serwisy i/lub wątki.
  • onDestroy() – podczas przeciążania tej metody powinniśmy zadbać o to by usunąć wszelkie zasoby stworzone w metodzie onCreate() oraz zamknąć wszelkie połączenia, czy to z bazą danych czy z siecią, jakie nawiązaliśmy przy okazji działania aktywności.
  • onStart()/onStop() – czas życia aktywności pomiędzy tymi metodami nazywany jest Widzialnym Czasem Życia (The Visible Lifetime), ponieważ to wtedy Aktywność jest widoczna dla użytkownika.
    • onStop() – powinna zostać użyta do zatrzymania wszelkich animacji, wątków, serwisów i innych, ktore związane są z interfejsem aktywności.
    • onStart()/onRestart() – przywracamy lub restartujemy wszystkie czynności/procesy, które zostały zatrzymane przez onStop().
  • onResume()/onPause() – pomiędzy tymi metodami zawiera się Aktywny Czas Życia (The Active Lifetime). Zachodzi on wtedy gdy nasza aktywność jest przez nas używana. To wtedy odbierane/wysyłane są wszelkie działania użytkownika. Pomiędzy tymi metodami powinny znajdować się lekkie i szybkie instrukcje.

Cykl życia w praktyce

Aby przekonać się jak cykl życia działa w realnej aplikacji rozbudujemy nasz projekt o kilka metod, które pozwolą przyjrzeć się temu „z bliższa” :) .

UI

Interfejs naszej aplikacji rozbudujemy o przycisk, który będzie uruchamiał nową aktywność, oraz pole tekstowe, które będzie służyło do wyświetlania listy stanów, przez które przeszła nasza główna aktywność.

<?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">
	<Button
		android:layout_height="wrap_content"
		android:id="@+id/btnOpenNewActivity"
		android:layout_width="fill_parent"
		android:text="@string/btn_NewActivity"></Button>
	<TextView
		android:textAppearance="?android:attr/textAppearanceLarge"
		android:layout_height="wrap_content"
		android:text="@string/lifetime"
		android:gravity="center"
		android:layout_width="fill_parent"></TextView>
	<TextView
		android:layout_width="wrap_content"
		android:layout_height="wrap_content"
		android:id="@+id/tvLifetime"></TextView>
</LinearLayout>

Kod źródłowy aplikacji

Do klasy naszej głównej Aktywności dodamy i zainicializujemy pola layoutu. Dodatkowo do przycisku przypiszemy akcję uruchamiającą nową Aktywność (którą na razie zostawimy bez komentarza).

...
public class MainActivity extends Activity {
	private Button btnOpenNewActivity;
	private TextView tvLifetime;

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		tvLifetime = (TextView) findViewById(R.id.tvLifetime);
		btnOpenNewActivity = (Button) findViewById(R.id.btnOpenNewActivity);
		btnOpenNewActivity.setOnClickListener(new OnClickListener() {
			public void onClick(View v) {
				Intent startAnotherActivity = new Intent(getApplicationContext(), AnotherActivity.class);
				startActivity(startAnotherActivity);
			}
		});
	}
}

Teraz pozostało nam tylko przeciążyć każdą z metod cyklu życia Aktywności i sprawić by każda z nich w momencie wywołania wpisywała informacje o tym do pola tekstowego tvLifetime.

@Override
	public void onCreate(Bundle savedInstanceState) {
		...
		setCurrentStateInTextView("onCreate");
	}

	@Override
	protected void onRestart() {
		super.onRestart();
		setCurrentStateInTextView("onRestart");
	}
	@Override
	protected void onStart() {
		super.onStart();
		setCurrentStateInTextView("onStart");
	}
	@Override
	protected void onResume() {
		super.onResume();
		setCurrentStateInTextView("onResume");
	}
	@Override
	protected void onPause() {
		setCurrentStateInTextView("onPause");
		super.onPause();
	}
	@Override
	protected void onStop() {
		setCurrentStateInTextView("onStop");
		super.onStop();
	}
	@Override
	protected void onDestroy() {
		setCurrentStateInTextView("onDestroy");
		super.onDestroy();
	}

	private void setCurrentStateInTextView(String state) {
		String text = tvLifetime.getText().toString() + state + " "
				+ getNowTime() + "\n";
		tvLifetime.setText(text);
	}
	private String getNowTime() {
		DateFormat df = new SimpleDateFormat("H:m:s:S");
		Date now = Calendar.getInstance().getTime();
		return df.format(now);
	}

Myślę, że powyższy kod nie wymaga komentarzy. :)

Prezentacja działania aplikacji

Oto ekran naszej Aktywności zaraz po uruchomieniu aplikacji:

Następnie uruchamiamy naszą drugą aktywność i przyciskiem powrotu wracamy do nasze głównej Aktywności. Jak widać, najpierw wywoływane są metody onPause oraz onStop. Następnie wyświetlana jest druga Aktywność (innymi słowy to ona umieszczona jest na szczycie stosu). Później, gdy druga aktywność jest ze stosu zdejmowana, ponownie główna Aktywność przechodzi na jego szczyt. Skutkuje to  wywołaniem kolejno metod onRestart, onStart oraz onResume.

Kompletny kod źródłowy aplikacji

main.xml

<?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">
	<Button
		android:layout_height="wrap_content"
		android:id="@+id/btnOpenNewActivity"
		android:layout_width="fill_parent"
		android:text="@string/btn_NewActivity"></Button>
	<TextView
		android:textAppearance="?android:attr/textAppearanceLarge"
		android:layout_height="wrap_content"
		android:text="@string/lifetime"
		android:gravity="center"
		android:layout_width="fill_parent"></TextView>
	<TextView
		android:layout_width="wrap_content"
		android:layout_height="wrap_content"
		android:id="@+id/tvLifetime"></TextView>
</LinearLayout>

another.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
	xmlns:android="http://schemas.android.com/apk/res/android"
	android:orientation="vertical"
	android:layout_width="match_parent"
	android:layout_height="match_parent">
	<TextView
		android:layout_width="wrap_content"
		android:layout_height="wrap_content"
		android:text="Welcome to another activity"></TextView>
</LinearLayout>

MainActivity.java

package pl.froger.helloactivities;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

public class MainActivity extends Activity {
	private Button btnOpenNewActivity;
	private TextView tvLifetime;

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		tvLifetime = (TextView) findViewById(R.id.tvLifetime);
		btnOpenNewActivity = (Button) findViewById(R.id.btnOpenNewActivity);
		btnOpenNewActivity.setOnClickListener(new OnClickListener() {
			public void onClick(View v) {
				Intent startAnotherActivity = new Intent(getApplicationContext(), AnotherActivity.class);
				startActivity(startAnotherActivity);
			}
		});
		setCurrentStateInTextView("onCreate");
	}

	@Override
	protected void onRestart() {
		super.onRestart();
		setCurrentStateInTextView("onRestart");
	}
	@Override
	protected void onStart() {
		super.onStart();
		setCurrentStateInTextView("onStart");
	}
	@Override
	protected void onResume() {
		super.onResume();
		setCurrentStateInTextView("onResume");
	}
	@Override
	protected void onPause() {
		setCurrentStateInTextView("onPause");
		super.onPause();
	}
	@Override
	protected void onStop() {
		setCurrentStateInTextView("onStop");
		super.onStop();
	}
	@Override
	protected void onDestroy() {
		setCurrentStateInTextView("onDestroy");
		super.onDestroy();
	}

	private void setCurrentStateInTextView(String state) {
		String text = tvLifetime.getText().toString() + state + " "
				+ getNowTime() + "\n";
		tvLifetime.setText(text);
	}
	private String getNowTime() {
		DateFormat df = new SimpleDateFormat("H:m:s:S");
		Date now = Calendar.getInstance().getTime();
		return df.format(now);
	}
}

AnotherActivity.java

package pl.froger.helloactivities;

import android.app.Activity;
import android.os.Bundle;

public class AnotherActivity extends Activity {
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.another);
	}
}

Cały projekt dostępny jest na naszym githubie – HelloActivities.

Komentarze (4) Subskrybuj

 

  1. [...] 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): [...]

  2. [...] powiadomień (Notifications) są jednym z podstawowych komponentów systemu (obok Aktywności, Intencji oraz Dostarczycieli treści). Jest to zbiór elementów, które „zaczepiają” [...]

  3. [...] 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): [...]

Prześlij komentarz

Zaloguj się lub skorzystaj z profilu:

[rpxlogin redirect="http://www.android4devs.pl" prompt="" style="large"]

Możesz również zostawić komentarz bez rejestracji, korzystając z poniższego formularza:

Musisz być zalogowany aby móc pisać komentarze.