Intencje – Intent

Jak przechodzić między ekranami aplikacji Android, przekazywać dane oraz przesyłać całe obiekty. Każdy nowy ekran tworzony ręcznie – zobaczysz dokładnie jak pliki są ze sobą powiązane.

Intent putExtra / getExtras AndroidManifest Serializable INF.04
Język przykładów:
01

Jak zbudowana jest aplikacja Android

Aplikacja Android składa się z wielu ekranów. Każdy ekran to osobna aktywność (ang. Activity). Żeby przejść z jednego ekranu na drugi, wysyłamy intencję (ang. Intent).

Intent to obiekt klasy Androida, który pełni rolę wiadomości lub rozkazu. Możesz powiedzieć Androidowi: „otwórz ekran X” albo „otwórz ekran X i przekaż mu te dane„. Android odbierze wiadomość, znajdzie właściwy ekran i go uruchomi.

Ekran A
MainActivity
Intent
wiadomość do systemu
Android OS
odbiera i realizuje
Ekran B
DrugaActivity
Dwa rodzaje intencji

Jawna (explicit) – wskazujesz konkretną klasę aktywności. Używana wewnątrz swojej aplikacji: Intent(this, DrugaActivity.class).
Niejawna (implicit) – mówisz tylko co chcesz zrobić, np. „wyświetl URL”. Android sam wybiera aplikację, która to obsłuży.

02

Trzy pliki składające się na jeden ekran

Zanim napiszemy pierwszy Intent, musisz zrozumieć jak Android „składa” ekran z trzech niezależnych plików. Każdy z nich odpowiada za coś innego i pliki te są ze sobą ściśle powiązane.

PlikDo czego służyGdzie w projekcie
activity_xxx.xmlWygląd – rozmieszczenie przycisków, pól, tekstów na ekranieres/layout/
XxxActivity.java/.ktLogika – co się dzieje po kliknięciu, jak reaguje na zdarzeniajava/ lub kotlin/
AndroidManifest.xmlRejestracja – informuje Android OS że taka aktywność istnieje w aplikacjiapp/manifests/

Te trzy pliki łączą się ze sobą w konkretnych miejscach. Poniżej widać gdzie dokładnie:

res/layout/activity_main.xml – definiuje wygląd XML
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" ...>
    <Button android:id="@+id/btnPrzejdz" android:text="Przejdź" ... />
</LinearLayout>
java/MainActivity.java – logika, połączenie z layoutem Java
public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);  // ← łączy klasę z plikiem XML
    }
}
kotlin/MainActivity.kt – logika, połączenie z layoutem Kotlin
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)  // ← łączy klasę z plikiem XML
    }
}
AndroidManifest.xml – rejestracja w systemie XML
<application ...>
    <activity android:name=".MainActivity">  <!-- ← rejestruje klasę w OS -->
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
</application>
Co łączy te pliki?

Powiązanie XML ↔ Klasa tworzy setContentView(R.layout.activity_main) – nazwa po R.layout. musi być identyczna z nazwą pliku XML.
Powiązanie Klasa ↔ Manifest tworzy android:name=".MainActivity" – nazwa po kropce musi być identyczna z nazwą klasy Java/Kotlin.
Każdy nowy ekran wymaga wszystkich trzech powiązań. Brak któregokolwiek to błąd lub crash aplikacji.

03

Przykład – aplikacja z dwoma ekranami

Zbudujemy aplikację krok po kroku: kliknięcie przycisku na pierwszym ekranie przenosi użytkownika na drugi ekran. Każdy plik tworzymy ręcznie, żebyś widział jak zbudowane jest powiązanie między nimi.

Dlaczego ręcznie – nie przez kreator?

Kreator „New → Activity” tworzy wszystkie pliki automatycznie i ukrywa połączenia między nimi. Robiąc to ręcznie, widzisz każde powiązanie: plik XML, klasę Activity i wpis w Manifeście tworzysz osobno i rozumiesz, czemu każdy z nich jest potrzebny.

Krok 1 – tworzymy layout drugiego ekranu

W panelu projektu kliknij prawym przyciskiem myszy folder res/layout, wybierz New → Layout Resource File. W polu File name wpisz activity_druga i kliknij OK.

Nie dodawaj rozszerzenia w nazwie pliku

Wpisz samo activity_druga – Android Studio doda .xml automatycznie. Wpisanie activity_druga.xml spowoduje, że plik będzie miał podwójne rozszerzenie.

Kiedy plik się otworzy, przełącz edytor na widok kodu (zakładka Code w prawym górnym rogu) i wpisz:

res/layout/activity_druga.xml XML
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:gravity="center"
    android:padding="24dp">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="To jest drugi ekran!"
        android:textSize="22sp"
        android:layout_marginBottom="32dp" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Wróć"
        android:onClick="wrocWstecz" />  <!-- musi istnieć metoda o tej nazwie w klasie -->

</LinearLayout>

Krok 2 – tworzymy klasę DrugaActivity

Kliknij prawym przyciskiem myszy na pakiet z klasami (np. java/com/example/apka), wybierz New → Java Class (lub Kotlin Class/File). Wpisz nazwę DrugaActivity.

Otwiera się pusty plik. Wpisz poniższy kod. Zwróć uwagę na dwa kluczowe powiązania:

java/DrugaActivity.java Java
package com.example.apka;   // wpisz nazwę pakietu swojego projektu

import android.os.Bundle;
import android.view.View;
import androidx.appcompat.app.AppCompatActivity;

public class DrugaActivity extends AppCompatActivity {  // każdy ekran dziedziczy po AppCompatActivity

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_druga);  // ← połączenie: klasa ↔ plik XML
    }

    // Metoda o nazwie identycznej z android:onClick="wrocWstecz" w XML
    public void wrocWstecz(View v) {
        finish();  // zamknij ten ekran – Android wróci do poprzedniego
    }
}
kotlin/DrugaActivity.kt Kotlin
package com.example.apka

import android.os.Bundle
import android.view.View
import androidx.appcompat.app.AppCompatActivity

class DrugaActivity : AppCompatActivity() {  // każdy ekran dziedziczy po AppCompatActivity

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_druga)  // ← połączenie: klasa ↔ plik XML
    }

    // Metoda o nazwie identycznej z android:onClick="wrocWstecz" w XML
    fun wrocWstecz(v: View) {
        finish()  // zamknij ten ekran – Android wróci do poprzedniego
    }
}
Trzy obowiązkowe elementy każdej metody obsługującej android:onClick

Metoda wskazana w android:onClick musi spełniać trzy warunki: być publiczna (public), zwracać void (nic nie zwracać) i przyjmować jeden parametr View v. Android przekazuje w tym parametrze referencję do widoku który kliknięto. Brak któregokolwiek warunku = crash w trakcie działania aplikacji.

Krok 3 – rejestrujemy DrugaActivity w Manifeście

Otwórz plik AndroidManifest.xml (znajdziesz go w app/manifests/). Wewnątrz tagu <application> dopisz jeden wiersz dla nowego ekranu.

Bez wpisu w Manifeście – crash po kliknięciu przycisku

To najczęstszy błąd początkujących. Klasa DrugaActivity istnieje, layout istnieje – ale Android OS nie wie że ta klasa jest ekranem tej aplikacji. Manifest to oficjalna lista ekranów aplikacji. Bez wpisu aplikacja kompiluje się bez błędów, ale w chwili uruchomienia intencji wyrzuca wyjątek: ActivityNotFoundException.

AndroidManifest.xml – dopisz podświetlony wiersz XML
<application
    android:allowBackup="true"
    android:label="@string/app_name"
    android:theme="@style/Theme.Apka">

    <activity android:name=".MainActivity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>

    <!-- NOWY WIERSZ: rejestracja drugiego ekranu -->
    <activity android:name=".DrugaActivity" />  <!-- kropka + nazwa klasy -->

</application>
Co oznacza kropka w android:name?

Zapis ".DrugaActivity" to skrót. Android uzupełnia go pełną nazwą pakietu z atrybutu package na początku pliku Manifest, np. com.example.apka.DrugaActivity. Możesz też wpisać pełną nazwę – efekt ten sam. Ważne: nazwa po kropce musi być identyczna z nazwą klasy.

Krok 4 – wysyłamy Intent z MainActivity

Teraz uzupełniamy activity_main.xml o przycisk i MainActivity o metodę która wyśle intencję. Layout główny zapewne już masz – dopisz lub zmień przycisk tak, żeby wskazywał na metodę przejdz.

res/layout/activity_main.xml – przycisk XML
<Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Przejdź na drugi ekran"
    android:onClick="przejdz" />  <!-- nazwa metody musi istnieć w MainActivity -->
java/MainActivity.java – metoda przejdz Java
public void przejdz(View v) {
    // Intent(kontekst, klasa docelowa)
    // kontekst = aktywność w której jesteśmy (this)
    // klasa docelowa = ekran który chcemy otworzyć
    Intent intent = new Intent(this, DrugaActivity.class);

    // przekaż intencję systemowi – Android otworzy DrugaActivity
    startActivity(intent);
}
kotlin/MainActivity.kt – metoda przejdz Kotlin
fun przejdz(v: View) {
    // ::class.java – Kotlin wymaga tego zapisu zamiast .class
    val intent = Intent(this, DrugaActivity::class.java)
    startActivity(intent)
}

Uruchom aplikację. Kliknięcie przycisku otworzy drugi ekran. Przycisk „Wróć” oraz systemowy przycisk Back zamkną drugi ekran i wrócą do pierwszego.

Mapa zależności – wszystkie połączenia w jednym miejscu

activity_main.xmlandroid:onClick="przejdz" → metoda przejdz() w MainActivity
MainActivitysetContentView(R.layout.activity_main) → plik activity_main.xml
MainActivityIntent(this, DrugaActivity.class) → klasa DrugaActivity
DrugaActivitysetContentView(R.layout.activity_druga) → plik activity_druga.xml
AndroidManifest.xmlandroid:name=".DrugaActivity" → klasa DrugaActivity

04

Przekazywanie danych między ekranami

Intent może nieść ze sobą dane – jak koperta z listem. Metoda putExtra() „wkłada” dane do intencji, a metoda getExtras() je „wyjmuje” po drugiej stronie. Każda wartość jest opisana kluczem – unikalnym Stringiem który musi być ten sam po obu stronach.

Wysyłanie – putExtra(klucz, wartość)

Rozbudujemy poprzedni przykład: na pierwszym ekranie użytkownik wpisuje imię i wiek, na drugim ekranie te dane są wyświetlane.

Klucz to zwykły String – musi być identyczny po obu stronach

Klucz w putExtra("KLUCZ_IMIE", imie) i w getString("KLUCZ_IMIE") musi być dokładnie ten sam. Wpisujemy go bezpośrednio jako tekst w obu miejscach. Jeśli się pomylisz w literowaniu – dane po prostu nie dotrą (getString zwróci null), bez żadnego błędu kompilacji. Dlatego warto pisać klucze wielkimi literami – wizualnie wyróżniają się w kodzie i łatwiej je zauważyć.

Zaktualizuj layout główny – dodaj pola EditText:

res/layout/activity_main.xml – pola formularza XML
<LinearLayout ...orientation="vertical" android:padding="24dp">

    <EditText
        android:id="@+id/etImie"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="Wpisz imię"
        android:inputType="textPersonName"
        android:layout_marginBottom="12dp" />

    <EditText
        android:id="@+id/etWiek"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="Wpisz wiek"
        android:inputType="number"
        android:layout_marginBottom="24dp" />

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Pokaż profil"
        android:onClick="przejdz" />

</LinearLayout>

Zaktualizuj layout drugiego ekranu – dodaj pola TextView do wyświetlenia danych:

res/layout/activity_druga.xml – pola wynikowe XML
<LinearLayout ...orientation="vertical" android:gravity="center" android:padding="32dp">

    <TextView
        android:id="@+id/tvImie"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="20sp"
        android:layout_marginBottom="8dp" />

    <TextView
        android:id="@+id/tvWiek"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="20sp"
        android:layout_marginBottom="32dp" />

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Wróć"
        android:onClick="wrocWstecz" />

</LinearLayout>

Teraz zaktualizuj MainActivity – metoda przejdz pobiera dane z pól i pakuje je do intencji:

java/MainActivity.java Java
public class MainActivity extends AppCompatActivity {

    // Pola klasy – dostępne we wszystkich metodach, nie tylko w onCreate
    EditText etImie;
    EditText etWiek;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initUI();  // inicjalizacja widoków w osobnej metodzie – porządek w kodzie
    }

    // Metoda przypisana do przycisku android:onClick="zapiszDane" w XML
    public void zapiszDane(View v) {
        String imie = etImie.getText().toString();
        int    wiek = Integer.parseInt(etWiek.getText().toString());

        Intent intencja = new Intent(this, DrugaActivity.class);
        // Dodajemy do intencji klucz-wartość – pakujemy dane
        intencja.putExtra("KLUCZ_IMIE", imie);
        intencja.putExtra("KLUCZ_WIEK", wiek);

        startActivity(intencja);
    }

    public void initUI() {
        etImie = findViewById(R.id.etImie);
        etWiek = findViewById(R.id.etWiek);
    }
}
kotlin/MainActivity.kt Kotlin
class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }

    fun przejdz(v: View) {
        val imie = findViewById<EditText>(R.id.etImie).text.toString()
        val wiek = findViewById<EditText>(R.id.etWiek).text.toString().toInt()

        val intent = Intent(this, DrugaActivity::class.java)
        // putExtra(klucz, wartość) – klucz musi być identyczny przy odbieraniu
        intent.putExtra("KLUCZ_IMIE", imie)
        intent.putExtra("KLUCZ_WIEK", wiek)
        startActivity(intent)
    }
}

Odbieranie – getIntent().getExtras()

W DrugaActivity pobieramy dane które zostały spakowane przez putExtra(). Używamy dokładnie tych samych kluczy – te same stringi które wpisaliśmy w putExtra().

java/DrugaActivity.java – odbieranie danych Java
public class DrugaActivity extends AppCompatActivity {

    // Pola klasy – dostępne we wszystkich metodach
    TextView tvImie;
    TextView tvWiek;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_druga);

        tvImie = findViewById(R.id.tvImie);
        tvWiek = findViewById(R.id.tvWiek);

        // getIntent() zwraca intencję która uruchomiła ten ekran
        // getExtras() zwraca Bundle – paczkę ze wszystkimi danymi z putExtra()
        Bundle dane = getIntent().getExtras();

        // getExtras() może zwrócić null gdy nikt nie wysłał danych
        // Sprawdzamy null żeby nie dostać NullPointerException
        if (dane != null) {
            // Odczytaj wartości – TEN SAM klucz co przy putExtra()
            String imie = dane.getString("KLUCZ_IMIE");
            int    wiek = dane.getInt("KLUCZ_WIEK");

            tvImie.setText("Imię: " + imie);
            tvWiek.setText("Wiek: " + wiek);
        }
    }

    public void wrocWstecz(View v) {
        finish();  // zamknięcie ekranu – Android wraca do poprzedniego
    }
}
kotlin/DrugaActivity.kt – odbieranie danych Kotlin
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_druga)

    // Kotlin: intent.extras zamiast getIntent().getExtras()
    val dane = intent.extras

    // dane?.let { } – wykonaj blok TYLKO gdy dane nie jest null
    dane?.let {
        val imie = it.getString("KLUCZ_IMIE")
        val wiek  = it.getInt("KLUCZ_WIEK")

        findViewById<TextView>(R.id.tvImie).text = "Imię: $imie"
        findViewById<TextView>(R.id.tvWiek).text = "Wiek: $wiek lat"
    }
}

Dostępne typy w putExtra i getXxxExtra

TypputExtraodczyt z Bundle
StringputExtra("k", "tekst")bundle.getString("k")
intputExtra("k", 42)bundle.getInt("k")
doubleputExtra("k", 3.14)bundle.getDouble("k")
booleanputExtra("k", true)bundle.getBoolean("k")
longputExtra("k", 100L)bundle.getLong("k")
własna klasawymaga Serializable lub Parcelable → sekcja 5
05

Przekazywanie całego obiektu

Gdy danych jest więcej, wygodniej spakować je w obiekt własnej klasy i przesłać go jako jedną wartość. Intent transportuje dane jako ciąg bajtów, więc obiekt musi umieć się na bajty zamienić (serializacja) i z powrotem (deserializacja).

Obiekt Uczen
dane w pamięci
serializacja
obiekt → bajty
Intent Bundle
bajty w paczce
Nowy obiekt Uczen
bajty → obiekt

Mamy dwa sposoby na nauczenie klasy serializacji. Opisane poniżej.

Sposób 1 – Serializable (proste, zero kodu)

Tworzymy nowy plik klasy: prawy klik na pakiet → New → Java Class / Kotlin Class, nazwa: Uczen. Jedyną zmianą jest dodanie implements Serializable (Java) lub : Serializable (Kotlin) do deklaracji klasy. Żadnego dodatkowego kodu – Java i Kotlin same wiedzą jak serializować pola.

java/Uczen.java – nowy plik Java
package com.example.apka;

import java.io.Serializable;

// Jedyna zmiana względem zwykłej klasy: "implements Serializable"
public class Uczen implements Serializable {

    private String imie;
    private int    wiek;
    private String klasa;

    public Uczen(String imie, int wiek, String klasa) {
        this.imie  = imie;
        this.wiek  = wiek;
        this.klasa = klasa;
    }

    public String getImie()  { return imie;  }
    public int    getWiek()  { return wiek;  }
    public String getKlasa() { return klasa; }
}
kotlin/Uczen.kt – nowy plik Kotlin
package com.example.apka

// data class generuje automatycznie gettery, equals, toString, copy
// Jedyna zmiana: ": Serializable" – nic więcej nie trzeba pisać
data class Uczen(
    val imie  : String,
    val wiek  : Int,
    val klasa : String
) : Serializable

Wysyłanie i odbieranie obiektu wygląda tak samo jak dla zwykłych typów, tylko metoda odczytu jest inna:

java/MainActivity.java – wysyłanie obiektu Java
public void przejdz(View v) {
    String imie  = ((EditText) findViewById(R.id.etImie)).getText().toString();
    int    wiek  = Integer.parseInt(((EditText) findViewById(R.id.etWiek)).getText().toString());
    String klasa = ((EditText) findViewById(R.id.etKlasa)).getText().toString();

    // Tworzymy obiekt i pakujemy go jako jedną wartość – putExtra działa tak samo jak dla String
    Uczen uczen = new Uczen(imie, wiek, klasa);
    Intent intent = new Intent(this, DrugaActivity.class);
    intent.putExtra("KLUCZ_UCZEN", uczen);  // działa bo Uczen implements Serializable
    startActivity(intent);
}
kotlin/MainActivity.kt – wysyłanie obiektu Kotlin
fun przejdz(v: View) {
    val imie  = findViewById<EditText>(R.id.etImie).text.toString()
    val wiek  = findViewById<EditText>(R.id.etWiek).text.toString().toInt()
    val klasa = findViewById<EditText>(R.id.etKlasa).text.toString()

    val uczen = Uczen(imie, wiek, klasa)
    val intent = Intent(this, DrugaActivity::class.java)
    intent.putExtra("KLUCZ_UCZEN", uczen)
    startActivity(intent)
}
java/DrugaActivity.java – odbieranie obiektu Java
Bundle dane = getIntent().getExtras();
if (dane != null) {
    // getSerializable() zwraca typ Serializable – potrzebne rzutowanie (cast) na Uczen
    Uczen u = (Uczen) dane.getSerializable("KLUCZ_UCZEN");
    if (u != null) {
        ((TextView) findViewById(R.id.tvImie)) .setText("Imię: "  + u.getImie());
        ((TextView) findViewById(R.id.tvWiek)) .setText("Wiek: "  + u.getWiek() + " lat");
        ((TextView) findViewById(R.id.tvKlasa)).setText("Klasa: " + u.getKlasa());
    }
}
kotlin/DrugaActivity.kt – odbieranie obiektu Kotlin
intent.extras?.let {
    // as? – bezpieczne rzutowanie: null zamiast wyjątku gdy typ się nie zgadza
    val u = it.getSerializable("KLUCZ_UCZEN") as? Uczen
    u?.let {
        findViewById<TextView>(R.id.tvImie).text  = "Imię: ${it.imie}"
        findViewById<TextView>(R.id.tvWiek).text  = "Wiek: ${it.wiek} lat"
        findViewById<TextView>(R.id.tvKlasa).text = "Klasa: ${it.klasa}"
    }
}

Sposób 2 – Parcelable (wydajne, zalecane przez Android)

Parcelable to interfejs natywny dla Androida. Jest szybszy od Serializable bo nie korzysta z refleksji – Android wie dokładnie jak czytać i zapisywać każde pole. W Javie trzeba to opisać ręcznie. W Kotlinie adnotacja @Parcelize generuje cały wymagany kod automatycznie.

java/Uczen.java – wersja Parcelable Java
public class Uczen implements Parcelable {

    private String imie;
    private int    wiek;
    private String klasa;

    // Zwykły konstruktor – tworzymy obiekt normalnie
    public Uczen(String imie, int wiek, String klasa) {
        this.imie  = imie;
        this.wiek  = wiek;
        this.klasa = klasa;
    }

    // Specjalny konstruktor – Android wywołuje go PO STRONIE ODBIORCY
    // Odczytuje dane z Parcel w tej samej kolejności co writeToParcel
    protected Uczen(Parcel in) {
        imie  = in.readString();   // 1.
        wiek  = in.readInt();     // 2.
        klasa = in.readString();   // 3.
    }

    // Android wywołuje tę metodę PO STRONIE NADAWCY
    // Kolejność write MUSI być identyczna z kolejnością read powyżej
    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(imie);   // 1.
        dest.writeInt(wiek);     // 2.
        dest.writeString(klasa);   // 3.
    }

    @Override
    public int describeContents() { return 0; }

    // Wymagana fabryka – Android używa jej żeby odtworzyć obiekt ze strumienia bajtów
    public static final Creator<Uczen> CREATOR = new Creator<Uczen>() {
        @Override
        public Uczen createFromParcel(Parcel in) { return new Uczen(in); }
        @Override
        public Uczen[] newArray(int size)         { return new Uczen[size]; }
    };

    public String getImie()  { return imie;  }
    public int    getWiek()  { return wiek;  }
    public String getKlasa() { return klasa; }
}
Kolejność write i read musi być identyczna

Jeśli w writeToParcel() zapiszesz kolejno imie → wiek → klasa, to w konstruktorze Uczen(Parcel in) musisz odczytywać w tej samej kolejności. Przestawienie kolejności spowoduje, że pola dostaną złe wartości – i to bez żadnego błędu kompilacji.

kotlin/Uczen.kt – wersja @Parcelize Kotlin
import android.os.Parcelable
import kotlinx.android.parcel.Parcelize

// @Parcelize generuje automatycznie cały kod który Java wymaga pisać ręcznie:
// konstruktor Parcel, writeToParcel, describeContents, CREATOR
@Parcelize
data class Uczen(
    val imie  : String,
    val wiek  : Int,
    val klasa : String
) : Parcelable
Włącz plugin kotlin-parcelize w build.gradle

W pliku build.gradle (Module: app) w sekcji plugins { } dopisz wiersz i kliknij Sync Now:

build.gradle
id("kotlin-parcelize")

Bez tego pluginu Android Studio nie rozpozna adnotacji @Parcelize i zgłosi błąd kompilacji.

Wysyłanie jest identyczne jak przy Serializable. Przy odbieraniu używamy getParcelable() zamiast getSerializable():

java/DrugaActivity.java – odbieranie Parcelable Java
// Tylko ta jedna linia różni się od wersji Serializable
// Nie potrzeba rzutowania – getParcelable jest generyczne
Uczen u = dane.getParcelable("KLUCZ_UCZEN");
kotlin/DrugaActivity.kt – odbieranie Parcelable Kotlin
// API 33+ – typowana wersja bez rzutowania
val u = it.getParcelable("KLUCZ_UCZEN", Uczen::class.java)

// Kompatybilne ze starszymi wersjami Androida
// val u = it.getParcelable<Uczen>("KLUCZ_UCZEN")
SerializableParcelable
Ilość koduJedno słowo – nic więcejJava: ~30 linii; Kotlin + @Parcelize: jedna adnotacja
WydajnośćWolniejsze (refleksja)Szybsze (brak refleksji)
Zalecane przez GoogleNie✅ Tak
Kiedy używaćĆwiczenia, szybkie prototypyProjekt końcowy, aplikacje produkcyjne
06

Intencja niejawna – komunikacja z systemem

Dotąd wskazywaliśmy konkretną klasę aktywności. Intencja niejawna nie wskazuje klasy – mówi tylko co chcemy zrobić. Android przeszukuje zainstalowane aplikacje i wybiera tę która potrafi tę akcję wykonać.

java/MainActivity.java – przykłady intencji niejawnych Java
// Otwórz adres URL w domyślnej przeglądarce
Intent i1 = new Intent(Intent.ACTION_VIEW, Uri.parse("https://school-it.pl"));
startActivity(i1);

// Otwórz aplikację telefoniczną z wybranym numerem
Intent i2 = new Intent(Intent.ACTION_DIAL, Uri.parse("tel:112"));
startActivity(i2);

// Wyślij tekst – Android pokaże listę aplikacji: e-mail, SMS, Messenger...
Intent i3 = new Intent(Intent.ACTION_SEND);
i3.setType("text/plain");
i3.putExtra(Intent.EXTRA_TEXT, "Wiadomość do wysłania");
startActivity(Intent.createChooser(i3, "Wyślij przez..."));
kotlin/MainActivity.kt – przykłady intencji niejawnych Kotlin
// Otwórz adres URL w domyślnej przeglądarce
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse("https://school-it.pl")))

// Otwórz aplikację telefoniczną z wybranym numerem
startActivity(Intent(Intent.ACTION_DIAL, Uri.parse("tel:112")))

// Wyślij tekst – Kotlin: apply { } grupuje konfigurację obiektu
startActivity(Intent(Intent.ACTION_SEND).apply {
    type = "text/plain"
    putExtra(Intent.EXTRA_TEXT, "Wiadomość do wysłania")
}.let { Intent.createChooser(it, "Wyślij przez...") })
07

Podsumowanie

Każdy nowy ekran wymaga trzech elementów. Pominięcie któregokolwiek kończy się błędem.

ElementGdzieCo robiBrak powoduje
Plik XML layoutures/layout/Definiuje wygląd ekranuCrash w setContentView()
Klasa Activitypakiet Java/KotlinDefiniuje logikę ekranu, łączy się z XML przez setContentView()Błąd kompilacji
Wpis w ManifeścieAndroidManifest.xmlRejestruje ekran w systemie AndroidActivityNotFoundException – crash po kliknięciu
Operacja Java Kotlin
Utwórz intencjęnew Intent(this, B.class)Intent(this, B::class.java)
Uruchom ekranstartActivity(intent)startActivity(intent)
Wyślij daneintent.putExtra("k", v)intent.putExtra("k", v)
Odbierz danegetIntent().getExtras()intent.extras
Odczytaj Stringbundle.getString("k")bundle.getString("k")
Odczytaj intbundle.getInt("k")bundle.getInt("k")
Zamknij ekranfinish()finish()
Obiekt: prostoimplements Serializabledata class X(...) : Serializable
Obiekt: wydajnieimplements Parcelable + ~30 linii@Parcelize data class X(...) : Parcelable
Odbierz obiekt(X) bundle.getSerializable("k")bundle.getSerializable("k") as? X
Zadanie

Zadanie dla uczniów

Aplikacja „Wizytówka ucznia”

  • Utwórz ręcznie plik activity_formularz.xml z polami: imię, nazwisko, klasa (np. 3TI) i przyciskiem „Pokaż wizytówkę”
  • Zmień MainActivity tak, żeby używała tego nowego layoutu
  • Utwórz ręcznie klasę Uczen implementującą Serializable z tymi trzema polami
  • Utwórz ręcznie plik activity_wizytowka.xml z trzema polami TextView i przyciskiem „Wróć”
  • Utwórz ręcznie klasę WizytowkaActivity i zarejestruj ją w AndroidManifest.xml
  • Po kliknięciu przycisku utwórz obiekt Uczen, spakuj do intencji przez putExtra() i przejdź do WizytowkaActivity
  • Odbierz obiekt przez getSerializable() i wyświetl dane w polach TextView

⭐ Bonus 1: dodaj do klasy Uczen pole srednia (double) – wyświetl je na wizytówce sformatowane do 2 miejsc po przecinku (String.format("%.2f", srednia))

⭐⭐ Bonus 2: dodaj trzeci ekran PotwierdzActivity – po kliknięciu „Zatwierdź” na wizytówce przejdź tam i wyświetl „Dane zapisane!”

⭐⭐⭐ Bonus Kotlin: zamień Serializable na @Parcelize i porównaj ile linii kodu zaoszczędziłeś