Listy zadań HTML
L01 - Lista 1
Lista 1
Termin wykonania:
- gr wt: 21.10
- gr czw: 23.10
Zad 1 - 6 pkt
Aplikacja "ComposeShop" posiada jedną główną strukturę nawigacyjną (obsługiwaną przez dolny pasek i szufladę) oraz jeden zagnieżdżony potok nawigacyjny przeznaczony do obsługi procesu składania zamówienia. (Przykład działania na filmie L1)
Główny interfejs aplikacji, opiera się na połączeniu dolnej nawigacji i szuflady nawigacyjnej. Wszystkie te ekrany znajdują się w jednym, głównym grafie nawigacyjnym (NavHost).
Ekrany w Dolnej Nawigacji (Bottom Navigation):
Nawigacja odbywa się przez kliknięcie odpowiedniej ikony na dolnym pasku, co wywołuje navController.navigate("nazwa_ekranu").
- Ekran Główny: Pierwszy ekran po uruchomieniu aplikacji. Wyświetla listę.
- Ekran Kategorii: Lista z kategoriami produktów (np. "Elektronika", "Moda", "Dom").
- Ekran Koszyka: Wyświetla listę produktów dodanych do koszyka (dane mogą być statyczne). Przycisk "Przejdź do kasy" włącza zagnieżdżony potok nawigacyjy.
- Ekran Ulubionych: Lista produktów oznaczonych przez użytkownika jako ulubione.
Ekrany w Szufladzie Nawigacyjnej (Drawer Navigation)
- Ekran Mojego Profilu: Wyświetla podstawowe informacje o użytkowniku (np. imię, adres email).
- Ekran Historii Zamówień: Lista poprzednich zamówień z datą i statusem.
- Ekran Ustawień: Zawiera opcje konfiguracyjne, np. przełączniki do włączenia trybu ciemnego czy powiadomień (symulowane).
- Przycisk "Wyloguj": Symuluje proces wylogowania, przenosząc do ekranu startowego.
Zagnieżdżony Potok Nawigacyjny: Proces Składania Zamówienia
Nawigacja do tego potoku rozpoczyna się na Ekranie Koszyka. Po wejściu w ten tryb, dolna nawigacja i szuflada przestają być widoczne, aby użytkownik mógł w pełni skupić się na procesie zakupu. Nawigacja między ekranami w tym grafie jest liniowa – użytkownik przechodzi z kroku 1 do 2, a potem do 3.
- Ekran 1: Adres dostawy - Zawiera prosty formularz do wpisania danych adresowych.Przycisk "Dalej" przenosi do kolejnego ekranu w potoku
- Ekran 2: Metoda płatności - Umożliwia wybór jednej z kilku opcji płatności (np. za pomocą RadioButton). Przycisk "Podsumowanie" przenosi do ostatniego kroku.
- Ekran 3: Podsumowanie i Potwierdzenie - Wyświetla wszystkie zebrane informacje: produkty z koszyka, adres dostawy i wybraną metodę płatności.
- Przycisk "Zamów i zapłać" kończy cały proces. Jego kliknięcie wykonuje kluczową akcję nawigacyjną - Usuwa cały graf składania zamówienia (checkout_graph) z historii nawigacji, dzięki czemu użytkownik nie może przypadkowo wrócić przyciskiem "wstecz" do ekranu płatności. Przenosi użytkownika z powrotem do głównego ekranu aplikacji.
Odpowiedź ustna - 4 pkt
Oceny
| ocena | punkty |
|---|---|
| 3,0 | 6 pkt |
| 3,5 | 7 pkt |
| 4,0 | 8 pkt |
| 4,5 | 9 pkt |
| 5,0 | 10 pkt |
Przykładowe wykonanie
Pytania
Tematyka: Compose Navigation, NavHost, NavController, Nested Graphs, Drawer, Bottom Navigation, Back Stack.
Pytania dodatkowe: Activity, Intent, Context.
- Co to jest Activity i jaka jest jej rola w aplikacji opartej w całości o Jetpack Compose?
- Czym jest Context i do czego jest potrzebny w aplikacji na Androida?
- Do czego służy mechanizm Intent w systemie Android?
- Jaka jest rola komponentu NavHost w nawigacji Compose?
- Do czego służy NavController?
- Co to jest route (ścieżka) w kontekście nawigacji?
- Jak definiujemy ekran (cel nawigacji) wewnątrz NavHost?
- Co określa parametr startDestination w NavHost?
- Co to jest i w jakim celu stosujemy nawigacje zagnieżdżone (nested navigation)?
- Co robi funkcja navController.popBackStack()?
- Co to jest "back stack" (stos nawigacji)?
- Jak przekazać prosty argument (np. ID produktu) do innego ekranu w Compose Navigation?
- Czym jest Deep Link i jak Intent Filter w manifeście może być z nim powiązany?
- Jak centralizacja ścieżek nawigacji (np. w obiekcie object) pomaga w utrzymaniu kodu?
- Opisz cykl życia Activity (najważniejsze stany).
- Co to jest jawny (explicit) Intent? Podaj przykład użycia.
- Co to jest niejawny (implicit) Intent? Podaj przykład użycia.
- Jak stan NavController (rememberNavController()) przeżywa rekompozycję?
- Do czego służy funkcja setContent w ComponentActivity?
- Jak otworzyć i zamknąć szufladę nawigacyjną (Drawer) programowo?
- Czym jest Application Context oraz Activity Context?
- Czy jeden NavController może zarządzać wieloma NavHost?
Lista 2
Termin wykonania:
- gr wt: 4.11
- gr czw: 06.11
Zad 1 - 6 pkt
Aplikacja "Kino GURU" służy do przeglądania informacji o filmach, która wykorzystuje współbieżność do jednoczesnego pobierania różnych, niezależnych od siebie danych.
Aplikacja składa się z dwóch ekranów
- lista popularnych filmów
- ekran szczegółów filmu.
Kluczowym elementem jest ekran szczegółów, który musi równocześnie pobrać i wyświetlić wiele informacji o wybranym filmie.
Ekran Listy Filmów: Po uruchomieniu ekranu, aplikacja powinna wyświetlić stan ładowania ( CircularProgressIndicator). W tle, za pomocą korutyny (launch), powinna zostać wywołana funkcja suspend fun fetchPopularMovies(): List<Movie>, która po 2 sekundach zwraca listę filmów (np. 10-15 pozycji).
Po pobraniu danych, lista filmów powinna zostać wyświetlona na ekranie (np. w LazyColumn). Każdy element listy powinien być klikalny.
Ekran Szczegółów Filmu: Po kliknięciu na film (na ekranie listy filmów), użytkownik jest przenoszony na ekran szczegółów (wykorzystaj Compose Navigation). Na tym ekranie aplikacja musi równocześnie pobrać trzy niezależne zestawy danych dla danego filmu:
- Szczegóły podstawowe (
suspend fun fetchMovieDetails()): reżyser, rok produkcji, gatunek (opóźnienie: 1.5 sekundy). - Listę recenzji (
suspend fun fetchMovieReviews()): lista opinii użytkowników (opóźnienie: 3 sekundy). - Listę podobnych filmów (
suspend fun fetchSimilarMovies()): lista kilku innych tytułów (opóźnienie: 2 sekundy).
Należy użyć konstruktora async dla każdego z trzech zapytań, aby uruchomić je współbieżnie. Główna korutyna (launch) musi poczekać na zakończenie wszystkich trzech operacji za pomocą .await(). Przez cały czas ładowania danych na ekranie powinien być widoczny wskaźnik postępu (CircularProgressIndicator).
Po pobraniu wszystkich danych, ekran powinien wyświetlić je w odpowiednich sekcjach (np. "Opis", "Recenzje", "Polecane"). Dodatkowo, na ekranie powinien znaleźć się tekst informujący o całkowitym czasie operacji pobierania danych. Czas ten powinien wynosić około 3 sekund (czas najdłuższej operacji), a nie 6.5 sekundy (suma czasów).
Odpowiedź ustna - 4 pkt
Oceny
| ocena | punkty |
|---|---|
| 3,0 | 6 pkt |
| 3,5 | 7 pkt |
| 4,0 | 8 pkt |
| 4,5 | 9 pkt |
| 5,0 | 10 pkt |
Przykładowe wykonanie
Pytania
Tematyka: Blokowanie wątku UI, suspend, CoroutineScope, launch, async, await, Deferred, współbieżność.
Pytania dodatkowe: Cykl życia Activity, Intent, Context.
- Dlaczego wykonywanie długich operacji na głównym wątku UI jest problemem?
- Co to jest korutyna i czym różni się od tradycyjnego wątku?
- Czym jest funkcja suspend?
- Jakiej reguły musimy przestrzegać przy wywoływaniu funkcji suspend?
- Jaka jest fundamentalna różnica między Thread.sleep() a delay()?
- Co to jest CoroutineScope i jaka jest jego rola?
- Co to jest "ustrukturyzowana współbieżność" (Structured Concurrency)?
- Co się dzieje z korutynami, gdy ich nadrzędny CoroutineScope jest anulowany?
- Do czego służy konstruktor korutyn launch?
- Czy launch zwraca wynik operacji? Jeśli nie, co zwraca?
- Do czego służy konstruktor korutyn async?
- Jaka jest główna różnica między launch a async?
- Co to jest obiekt Deferred?
- Co robi funkcja .await() wywołana na obiekcie Deferred?
- Wyjaśnij, dlaczego wykonanie trzech zapytań (1.5s, 3s, 2s) współbieżnie trwa około 3s, a nie 6.5s.
- Co to znaczy, że operacja jest "asynchroniczna"?
- Co to znaczy, że operacje są "współbieżne"?
- Co to jest Activity i jakie są jej główne stany cyklu życia?
- Jak cykl życia Activity jest powiązany z lifecycleScope?
- Czy Intent jest operacją synchroniczną czy asynchroniczną?
- Jak można sprawdzić, czy korutyna jest aktywna?
- Co robi runBlocking i dlaczego nie powinniśmy go używać w kodzie produkcyjnym na Androidzie?
- Czy można uruchomić korutynę bez CoroutineScope?
- Jak obsłużyć wyjątek (błąd) wewnątrz korutyny uruchomionej przez launch?
- Czym różni się współbieżność od równoległości?
- Jak rememberCoroutineScope() jest powiązany z cyklem życia komponentu Composable?
Lista 3
Termin wykonania:
- gr wt: 02.12
- gr czw: 04.12
Zad 1 - 3 pkt
Stwórz aplikację, która będzie prostym dashboardem do śledzenia fikcyjnych cen dwóch kryptowalut ("ComposeCoin" i "KotlinCoin") oraz salda portfela użytkownika.
Wymagania Funkcjonalne:
- Architektura:
- Aplikacja musi być zaimplementowana w architekturze
MVVM. - Należy stworzyć klasę
Repository, która będzie symulowała źródła danych. ViewModelbędzie odpowiedzialny za logikę, łączenie strumieni i udostępnianie stanu dla UI.- Warstwa Danych (
Repository): - Repozytorium musi udostępniać dwa niezależne, zimne strumienie
Flow: fun getComposeCoinPriceStream(): Flow<Double>: Co 3 sekundy emituje nową, losową cenę dla "ComposeCoin".fun getKotlinCoinPriceStream(): Flow<Double>: Co 5 sekund emituje nową, losową cenę dla "KotlinCoin".- Warstwa Logiki (
ViewModel): ViewModelmusi stworzyć jeden, główny obiekt stanuUiState, który będzie zawierał: aktualne ceny obu walut, ilość posiadanych monet (można założyć stałe wartości początkowe, np. 10 ComposeCoinów i 2 KotlinCoiny) oraz obliczoną całkowitą wartość portfela.- Zastosuj
flowOnaby symulacja pobierania danych z repozytorium odbywała się w tle,combinedo połączenia strumieni orazstateIndo zmiany zimnego strumienia w gorący. - Warstwa Prezentacji (UI -
Composable): - Ekran powinien wyświetlać dane z
UiState, obserwującStateFlowzViewModelu. - Interfejs powinien wyświetlać stan ładowania (
CircularProgressIndicator), zanimStateFlowwyemituje pierwszą wartość.
Zad 2 - 3 pkt
Stwórz aplikację, która symuluje weryfikację numeru seryjnego produktu. Wynik walidacji (sukces lub błąd) ma być wyświetlony jako jednorazowe powiadomienie.
Wymagania Funkcjonalne:
- Architektura:
- Aplikacja musi być zaimplementowana w architekturze
MVVM. - Warstwa Danych (
Repository): - Stwórz klasę
SerialNumberRepository. - Wewnątrz niej zdefiniuj zwykłą funkcję:
fun verifySerialNumber(serial: String): Boolean {
// Symulacja weryfikacji
Thread.sleep(2500)
// Prosta logika walidacji
return serial.startsWith("SN-") && serial.length == 10
}
- Warstwa Logiki (
ViewModel): ViewModelpowinien miećStateFlowprzechowujący stan UI (UiState).- Stwórz funkcję
onVerifyClicked(). Wewnątrz tej funkcji, w korutynie viewModelScope.launch: ViewModelmusi udostępniać zdarzenia walidacji jako gorącySharedFlow. Strumień ten powinien być utworzony z zimnegoFlow.- Wykorzystaj
withContextaby przenieść wykonanie blokującej funkcjirepository.verifySerialNumber()na wątek z puliDefaultorazshareIndo stworzenia gorącego, współdzielonego strumienia, który roześle jednorazowy wynik walidacji (event) do wszystkich obserwatorów w UI (w zadaniu jest jedenSnackbar)
- Warstwa Prezentacji (UI -
Composable): - Ekran powinien zawierać
TextFielddo wpisania numeru seryjnego, przycisk Weryfikuj i wskaźnikCircularProgressIndicatorwidoczny podczas weryfikacji. - UI musi subskrybować
SharedFlowzViewModel.
Odpowiedź ustna - 4 pkt
Oceny
| ocena | punkty |
|---|---|
| 3,0 | 6 pkt |
| 3,5 | 7 pkt |
| 4,0 | 8 pkt |
| 4,5 | 9 pkt |
| 5,0 | 10 pkt |
Przykładowe wykonania
Pytania
Tematyka: MVVM, StateFlow, SharedFlow, "zimne" vs "gorące" strumienie, stateIn, sharedIn, withContext, combine.
Pytania dodatkowe: Architektura Androida, Activity, Intent, Context.
- Jaki problem rozwiązuje architektura aplikacji, np. MVVM?
- Czym jest viewModelScope i jak jest powiązany z cyklem życia ViewModelu?
- Opisz role poszczególnych komponentów w architekturze MVVM: Model, View, ViewModel.
- Czym jest Context? Czy można go bezpiecznie przechowywać w ViewModelu?
- Jaka jest główna odpowiedzialność ViewModelu?
- Dlaczego ViewModel przeżywa zmiany konfiguracji, np. obrót ekranu?
- Jaka jest różnica między "zimnym" a "gorącym" strumieniem danych?
- Którym typem strumienia jest Flow, a którym StateFlow?
- Wymień kluczowe cechy StateFlow.
- Do czego w architekturze MVVM najczęściej używamy StateFlow?
- Do czego służy funkcja .collectAsState() lub collectAsStateWithLifecycle w Compose?
- Jaki problem rozwiązuje SharedFlow?
- Dlaczego nie powinniśmy używać StateFlow do wysyłania jednorazowych zdarzeń, np. nawigacji?
- Opisz wzorzec z _privateMutableStateFlow i publicStateFlow. Jaki jest jego cel?
- Do czego służy funkcja .asStateFlow()?
- Do czego służy operator combine na strumieniach Flow?
- Do czego służy funkcja withContext? Podaj przykład użycia.
- Czym się różni Dispatchers.IO od Dispatchers.Default?
- Jaki jest cel operatora stateIn?
- Co oznacza parametr started = SharingStarted.WhileSubscribed(5000)?
- Jaki jest cel operatora sharedIn?
- Czym różni się withContext od flowOn?
- Co to jest Activity i jak komunikuje się z ViewModel?
- Czym jest Context i dlaczego jest potrzebny do dostępu do zasobów systemowych?
- Jak za pomocą Intent można przekazać dane między dwiema Activity?
- Wyjaśnij, co to jest jednokierunkowy przepływ danych (UDF).
- Czym się różni collectAsState od collectAsStateWithLifecycle?
- Jaką rolę pełni viewModelScope w ViewModelu?
Lista 4
Termin wykonania:
- gr wt: 08.01
- gr czw: 15.01
Zad 1 - 3 pkt
Stwórz aplikację która będzie prostą, osobistą biblioteką gier wideo. Użytkownik będzie mógł dodawać nowe gry do bazy ROOM, oznaczać je jako ulubione oraz filtrować widok, aby pokazać wszystkie gry lub tylko te ulubione. Ustawienie filtra będzie trwale zapisywane za pomocą DataStore.
Wymagania Funkcjonalne:
- Architektura:
- Aplikacja musi być zaimplementowana w architekturze
MVVM. - Należy stworzyć dwa osobne Repozytoria:
GamesRepositorydo obsługi operacji na bazie danychRoom.SettingsRepositorydo obsługi zapisu i odczytu ustawień zPreferences DataStore.- Przechowywanie Danych (
Room): - Stwórz encję (@Entity) Game zawierającą pola:
id(klucz główny, auto-generowany),title(tytuł gry),genre(gatunek) orazisFavorite(Boolean). - Stwórz
GameDaoz następującymi metodami: suspend fun insert(game: Game)suspend fun update(game: Game)suspend fun delete(game: Game)fun getAllGamesStream(): Flow<List<Game>>(zwraca strumień wszystkich gier)fun getFavoriteGamesStream(): Flow<List<Game>>(zwraca strumień tylko ulubionych gier, WHERE isFavorite = true)- Skonfiguruj klasę
@Database. - Przechowywanie Ustawień (
Preferences DataStore): - Użyj
Preferences DataStoredo zapisania jednego ustawienia:showOnlyFavorites(Boolean). - Stwórz
SettingsRepository, który będzie udostępniałFlow<Boolean>z tym ustawieniem oraz funkcjęsuspend fun setShowOnlyFavorites(showOnly: Boolean). - Warstwa Logiki (ViewModel):
- Stwórz
GamesViewModel, który będzie przyjmował w konstruktorze oba repozytoria (GamesRepositoryiSettingsRepository). ViewModelmusi stworzyć jeden główny obiekt stanuUiState, który będzie zawierał: listę gier do wyświetlenia oraz aktualny stan filtrashowOnlyFavorites.ViewModelmusi nasłuchiwać na zmiany w strumieniu ustawień zSettingsRepository. Gdy ustawienieshowOnlyFavoritessię zmieni,ViewModelmusi przełączyć się na odpowiedni strumień zGamesRepository(getAllGamesStreamlubgetFavoriteGamesStream). Możesz wykorzystaćflatMapLatest.ViewModelmusi udostępniać funkcje do:- Dodawania nowej gry.
- Przełączania statusu
isFavoritedla istniejącej gry. - Zmiany ustawienia filtra
showOnlyFavorites. - Warstwa Prezentacji (UI - Composable):
- Ekran główny powinien wyświetlać listę gier (
LazyColumn). Każdy element listy powinien pokazywać tytuł, gatunek i ikonę serca, która pozwala na przełączenie statusuisFavorite. - Na górze ekranu (np. w
TopAppBarlub pod nią) umieść przełącznik (Switch) powiązany z ustawieniemshowOnlyFavorites. Jego zmiana powinna trwale zapisać preferencję i automatycznie odfiltrować listę gier. - Dodaj prosty formularz (np.
TextFieldiButtonlubFloatingActionButton) do dodawania nowych gier do bazy.
Zad 2 - 3 pkt
Stworzenie aplikacji, która pobiera dane z publicznego API serialu "Rick and Morty" z pomocą biblioteki Retrofit. Aplikacja pozwoli na przeglądanie listy postaci oraz na jej dynamiczne filtrowanie według statusu (żywy, martwy, nieznany).
API: The Rick and Morty API (https://rickandmortyapi.com/) - nie wymaga klucza.
Wymagania Funkcjonalne:
- Architektura:
- Aplikacja musi być zaimplementowana w architekturze
MVVM. - Należy stworzyć
RetrofitInstanceiApiServicedo komunikacji z API. - Warstwa Logiki (ViewModel):
- Stwórz
CharacterViewModel. - ViewModel powinien mieć
StateFlow, który przechowuje aktualnyUiState.UiStatepowinien zawierać: listę postaci, stan ładowania (isLoading), ewentualny błąd oraz aktualnie wybrany filtr statusu. - ViewModel musi udostępniać funkcję, np.
fun onFilterChanged(newStatus: String), która: - Aktualizuje stan
UiState, abyUIodzwierciedliło nowy wybrany filtr. - Uruchamia w
viewModelScopekorutynę, która pobiera nową, odfiltrowaną listę postaci zRepository, używając nowego statusu jako parametru. - Warstwa Prezentacji (UI - Composable):
- Ekran powinien wyświetlać listę postaci (
LazyColumn), pokazując ich obrazek, imię i status. - Na górze ekranu umieść grupę przycisków (
RowzButtonlubFilterChip), które pozwolą użytkownikowi wybrać filtr: "Wszyscy" (bez parametru status), "Żywi", "Martwi", "Nieznani". - Kliknięcie przycisku filtra powinno wywołać funkcję
onFilterChangedwViewModelu. - Interfejs musi poprawnie obsługiwać stan ładowania (
CircularProgressIndicator) i wyświetlać komunikaty o błędach. - Wskazówka: Do ładowania obrazków z
URLmożna użyć popularnej bibliotekiCoil:implementation("io.coil-kt:coil-compose:numer_wersji")
Odpowiedź ustna - 4 pkt
Oceny
| ocena | punkty |
|---|---|
| 3,0 | 6 pkt |
| 3,5 | 7 pkt |
| 4,0 | 8 pkt |
| 4,5 | 9 pkt |
| 5,0 | 10 pkt |
Przykładowe wykonania
Pytania
Tematyka: Room, DataStore, Retrofit, Entity, DAO, Database, @Query, @GET.
Pytania dodatkowe: Podstawy SQL, Activity, Intent, Context.
- Czym DataStore różni się od SharedPreferences?
- Dlaczego odczyt danych z DataStore zwraca Flow? Jaka jest tego zaleta?
- Co to jest relacyjna baza danych?
- Czym się różni SQL od SQLite?
- Wymień i opisz krótko 4 podstawowe operacje CRUD w języku SQL.
- Wymień 3 główne komponenty lub adnotacje biblioteki Room.
- Jaka jest rola adnotacji @Entity?
- Do czego służy @PrimaryKey?
- Jaka jest rola interfejsu z adnotacją @Dao?
- Jaka jest zaleta zwracania Flow z funkcji w DAO?
- Do czego służy biblioteka Retrofit?
- Co to jest API REST?
- Co to jest JSON?
- Jaka jest rola interfejsu ApiService w Retrofit?
- Do czego służy adnotacja @GET?
- Jak za pomocą adnotacji @Query przekazać parametr do zapytania?
- Co to jest DTO (Data Transfer Object) i dlaczego go używamy?
- Co to jest Activity i jakie ma znaczenie dla cyklu życia bazy danych?
- Czym jest Context i dlaczego jest potrzebny do stworzenia instancji bazy Room lub DataStore?
- Jak za pomocą Intent można otworzyć stronę internetową w przeglądarce?
- Co oznacza OnConflictStrategy.REPLACE w adnotacji @Insert?
- Co robi adnotacja @Upsert w Room?
- Do czego służy konwerter, np. GsonConverterFactory, w Retrofit?
- Co to jest baseUrl w Retrofit?
- Jak wygląda typowy schemat przepływu danych w architekturze z Room i Retrofit?
Lista 5
Termin wykonania:
- gr wt: 03.02
- gr. czw: 29.01
Zad 1 - 6 pkt
Stwórz aplikację będącą prostym przelicznikiem walut. Użytkownik będzie mógł wybrać walutę źródłową i docelową z listy, wpisać kwotę, a aplikacja pobierze aktualne kursy z publicznego API i wyświetli przeliczony wynik.
API: Użyj darmowego i niewymagającego klucza API Frankfurter (https://frankfurter.dev).
- Endpoint do pobrania listy dostępnych walut:
https://api.frankfurter.dev/v1/currencies - Endpoint do pobrania kursów:
https://api.frankfurter.dev/v1/latest(można użyć atrybutówbaseisymbols)
- Architektura:
- Aplikacja musi być zaimplementowana w architekturze MVVM z warstwą domeny.
- Schemat przepływu:
View➔ViewModel➔Use Case➔Repository➔API(Retrofit). - Należy użyć
Hiltdo wstrzykiwania zależności na wszystkich warstwach. - Warstwa Danych (Data Layer):
Retrofit: StwórzApiServicez dwiema funkcjami suspend odpowiadającymi powyższym endpointom.Repository: StwórzCurrencyRepository, które będzie pośrednikiem w komunikacji zApiService.- Warstwa Domeny (
Domain Layer): GetSupportedCurrenciesUseCase: Pobranie i ewentualne posortowanie listy dostępnych kodów walut (np. "USD", "PLN", "EUR").ConvertCurrencyUseCase: Wykonanie głównej logiki biznesowej – przeliczenia kwoty z jednej waluty na drugą.FormatCurrencyResultUseCase: Sformatowanie wyniku liczbowego na czytelny dla użytkownika String (np. "100.00 USD = 405.50 PLN"). Jest toUse Caseodpowiedzialny za logikę prezentacji.- Warstwa Logiki i Prezentacji (ViewModel i UI):
ViewModel:- Musi wstrzyknąć (
@Inject) wszystkie potrzebneUse Case'y. - Zarządza stanem
UiState, który zawiera m.in.: listę dostępnych walut, wybraną walutę źródłową i docelową, wpisaną kwotę, stan ładowania oraz finalny, sformatowany wynik. ViewModelnie wykonuje żadnych obliczeń ani formatowania – jedynie wywołuje odpowiednieUse Case'y i aktualizuje stan na podstawie ich wyników.UI(Composable):- Ekran powinien zawierać:
TextFielddo wpisania kwoty.- Dwa rozwijane menu (
DropDownMenulubExposedDropdownMenuBox) do wyboru waluty źródłowej i docelowej (lista walut powinna być pobrana przy starcie za pomocąGetSupportedCurrenciesUseCase). ButtonPrzelicz, który uruchamia logikę w ViewModelu.Textwyświetlający sformatowany wynik zUiState.- Interfejs musi obsługiwać stan ładowania (np. blokując przycisk i pokazując
CircularProgressIndicator).
Logika biznesowa musi być zamknięta w Use Case'ach. Stwórz co najmniej trzy klasy Use Case:
Odpowiedź ustna - 4 pkt
Oceny
| ocena | punkty |
|---|---|
| 3,0 | 6 pkt |
| 3,5 | 7 pkt |
| 4,0 | 8 pkt |
| 4,5 | 9 pkt |
| 5,0 | 10 pkt |
Przykładowe wykonanie
Pytania
Tematyka: Warstwa domeny, Use Case, Hilt, @Inject, @Module, @Provides, @HiltViewModel.
Pytania dodatkowe: Activity, Intent, Context w kontekście DI.
- Jaki problem rozwiązuje Wstrzykiwanie Zależności (Dependency Injection - DI)?
- Czym się różni Hilt od Daggera?
- Jak Hilt lub inne DI upraszcza pracę z MVVM?
- Jaka jest rola adnotacji @HiltAndroidApp i gdzie ją umieszczamy?
- Jaka jest rola adnotacji @AndroidEntryPoint?
- Jak wstrzykujemy zależności do ViewModelu za pomocą Hilt? Jakich dwóch adnotacji użyjemy?
- Czym różni się @Inject constructor od metody z adnotacją @Provides? Kiedy którego używamy?
- Co to jest moduł Hilt (@Module)?
- Do czego służy adnotacja @InstallIn?
- Co oznacza @InstallIn(SingletonComponent::class)?
- Dlaczego Retrofit i Room Database zazwyczaj dostarczamy jako singletony?
- Jakiej funkcji używamy w Composable, aby uzyskać ViewModel zarządzany przez Hilt?
- Dlaczego w architekturze aplikacji wprowadzamy dodatkową warstwę domeny (domain layer)?
- Co to jest Use Case lub Interactor?
- Jaka jest główna zasada przy projektowaniu Use Case'ów?
- W jaki sposób ViewModel staje się chudszy po wprowadzeniu Use Case'ów?
- Co robi operator fun invoke w klasie Use Case?
- Czy Use Case może zależeć od ViewModelu? Uzasadnij odpowiedź.
- Czy Use Case może zależeć od frameworka Androida, np. od Context? Dlaczego?
- Narysuj i opisz schemat Czystej Architektury: View -> ViewModel -> Use Case -> Repository.
- Jakie są zalety posiadania warstwy domeny?
- Co to jest Activity i jak Hilt zarządza zależnościami w jej cyklu życia?
- Czym jest Context i jak Hilt go dostarcza? Jakiej adnotacji używamy?
- Co to jest Intent?
- Jak Hilt dostarcza SavedStateHandle do ViewModelu?
- Wyjaśnij, jak Hilt buduje graf zależności na przykładzie: ViewModel potrzebuje Repository, a Repository potrzebuje ApiService.
- Jaka jest rola adnotacji @Singleton?
- Czy Repository zawsze musi być singletonem? Uzasadnij.
- Opisz, jak Hilt rozwiązuje problem tworzenia ViewModelu z parametrami.