0Pricing
Android Academy · Lekcja

Moduły funkcji i moduły podstawowe

Wyznacz granice modułów

Moduły funkcji i moduły podstawowe to bezpłatna lekcja Android Academy na CoddyKit. To lekcja 2 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Android Academy, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Android Academy zawiera 4 lekcji w sumie.

Dwa rodzaje modułów

W większości zmodularyzowanych aplikacji na Androida kod jest uporządkowany w dwóch głównych rodzajach modułów: modułach feature i core, a nad nimi znajduje się niewielki moduł :app.

  • Moduły feature zawierają fragment aplikacji widoczny dla użytkownika (ekran lub przepływ).
  • Moduły core zawierają współdzieloną infrastrukturę używaną przez wiele funkcji.

W tej lekcji dowie się Pan lub dowie się Pani, jak prawidłowo wyznaczać te granice.

Budowa modułu funkcji

Moduł funkcji, taki jak :feature:profile, zawiera wszystko, czego potrzebuje jedna funkcja: ekrany Compose, obiekt ViewModel oraz stan interfejsu użytkownika. Jest pionowy: obejmuje cały fragment od interfejsu użytkownika po model widoku.

Zależy od modułów core w zakresie współdzielonych elementów, ale nie powinien zależeć od innych modułów funkcji.

// feature/profile/ProfileScreen.kt
@Composable
fun ProfileScreen(viewModel: ProfileViewModel = hiltViewModel()) {
    val state by viewModel.uiState.collectAsStateWithLifecycle()
    when (state) {
        is ProfileUiState.Loading -> CircularProgressIndicator()
        is ProfileUiState.Success -> ProfileContent((state as ProfileUiState.Success).user)
        is ProfileUiState.Error -> ErrorMessage()
    }
}

ViewModel funkcji

Każda funkcja ma własny obiekt ViewModel. Pobiera on dane za pośrednictwem repozytorium z modułu core i udostępnia stan interfejsu użytkownika. Moduł funkcji nie wie, w jaki sposób pobierane są dane — zna jedynie kontrakt repozytorium.

// feature/profile/ProfileViewModel.kt
@HiltViewModel
class ProfileViewModel @Inject constructor(
    private val userRepository: UserRepository // from :core:data
) : ViewModel() {
    val uiState: StateFlow<ProfileUiState> =
        userRepository.observeUser()
            .map { ProfileUiState.Success(it) }
            .stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), ProfileUiState.Loading)
}

Budowa modułu core

Moduł core jest poziomy: udostępnia jedną funkcję używaną w wielu modułach. Typowe moduły core to:

  • :core:model — zwykłe klasy danych współdzielone w całej aplikacji
  • :core:network — klienty Retrofit/Ktor
  • :core:database — konfiguracja Room
  • :core:data — repozytoria łączące sieć i bazę danych
  • :core:designsystem — motyw i kompozysy wielokrotnego użytku
// core/model/User.kt
data class User(
    val id: String,
    val name: String,
    val avatarUrl: String
)

Moduł systemu projektowego

:core:designsystem jest jednym z najczęściej wykorzystywanych modułów. Zawiera MaterialTheme, schematy kolorów, typografię oraz kompozysy wielokrotnego użytku, takie jak przyciski i karty. Każda funkcja korzysta z takiego samego wyglądu bez kopiowania kodu.

// core/designsystem/AppTheme.kt
@Composable
fun AppTheme(
    darkTheme: Boolean = isSystemInDarkTheme(),
    content: @Composable () -> Unit
) {
    val colors = if (darkTheme) DarkColors else LightColors
    MaterialTheme(
        colorScheme = colors,
        typography = AppTypography,
        content = content
    )
}

Moduł danych odpowiada za repozytoria

Moduł :core:data udostępnia interfejsy repozytoriów, od których zależą moduły funkcji, ukrywając jednocześnie implementację. Zwykle zależy od modułów :core:network i :core:database, łącząc je w jedno źródło prawdy.

// core/data/UserRepository.kt
interface UserRepository {
    fun observeUser(): Flow<User>
    suspend fun refresh()
}

// core/data/OfflineFirstUserRepository.kt
internal class OfflineFirstUserRepository @Inject constructor(
    private val api: UserApi,        // :core:network
    private val dao: UserDao         // :core:database
) : UserRepository {
    override fun observeUser(): Flow<User> = dao.observe().map { it.toUser() }
    override suspend fun refresh() { dao.upsert(api.fetch().toEntity()) }
}

Lekki model i design system

Moduły rdzeniowe najniższego poziomu powinny zależeć od możliwie najmniejszej liczby elementów. :core:model najlepiej, aby w ogóle nie miał zależności od Androida — tylko zwykłe klasy danych Kotlin. Dzięki temu można go używać wszędzie, a jego kompilacja jest szybka.

Gdyby :core:model zaczął zależeć od Retrofit lub Room, każdy moduł korzystający z klas danych musiałby dołączyć te ciężkie biblioteki.

// core/model/build.gradle.kts
plugins {
    id("myapp.jvm.library") // pure Kotlin, no Android
}
// No Retrofit, no Room, no Compose here — just data classes

Cienki moduł :app

Moduł :app pełni rolę składacza. Powinien zawierać bardzo niewiele logiki: klasę Application, pojedynczą klasę MainActivity, główny NavHost oraz konfigurację wstrzykiwania zależności. Wszystkie rzeczywiste ekrany znajdują się w modułach funkcjonalności.

Cienki moduł aplikacji oznacza, że większość zmian zachodzi w modułach funkcjonalności, więc moduł aplikacji rzadko wymaga ponownej kompilacji.

// app/MainActivity.kt
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            AppTheme {            // from :core:designsystem
                AppNavHost()      // routes into :feature:* screens
            }
        }
    }
}

Wyznaczanie dobrych granic

Jak zdecydować, co powinno stać się modułem? Oto kilka praktycznych wskazówek:

  • Funkcjonalność = ekran lub przepływ, który użytkownik może nazwać (Home, Profile, Checkout).
  • Moduł core = możliwość wykorzystywana przez co najmniej 2 funkcjonalności.
  • Jeśli dwie funkcjonalności potrzebują tego samego kodu, przenieś go niżej, do modułu core.
  • Jeśli moduł robi zbyt wiele niezwiązanych ze sobą rzeczy, podziel go.

Opcjonalnie: podział na api i impl

Duże aplikacje czasami dzielą funkcjonalność na publiczny moduł :feature:profile:api (interfejsy, trasy nawigacji) oraz prywatny moduł :feature:profile:impl (ekrany, modele widoku). Inne funkcjonalności zależą tylko od niewielkiego modułu api, nigdy od implementacji.

To rozwiązanie zaawansowane; w większości aplikacji pojedynczy moduł na funkcjonalność w zupełności wystarczy. Warto jednak wiedzieć, że w bardzo dużych bazach kodu stosuje się taki wzorzec.

// Other features see only the contract, not the screens
// feature/home depends on :feature:profile:api
interface ProfileEntry {
    val route: String
    fun NavGraphBuilder.register(navController: NavController)
}

Łączenie grafu

Tak warstwy łączą się w naszym przykładzie. Zwróć uwagę, że zależności zawsze wskazują w dół: app -> feature -> data -> network/database -> model.

// :app           -> :feature:home, :feature:profile
// :feature:home  -> :core:data, :core:designsystem
// :feature:profile -> :core:data, :core:designsystem
// :core:data     -> :core:network, :core:database, :core:model
// :core:network  -> :core:model
// :core:database -> :core:model
// :core:model    -> (nothing)

Szybkie sprawdzenie

Moduły :feature:home i :feature:profile muszą pobierać i buforować dane użytkownika. Gdzie powinien znajdować się kod repozytorium?

Podsumowanie: moduły funkcjonalności i core

Nauczyłeś się dzielić kod na dwie warstwy:

  • Moduły funkcjonalności to pionowe wycinki (ekran + ViewModel + stan interfejsu użytkownika).
  • Moduły core to poziome możliwości (model, sieć, baza danych, dane, design system).
  • Moduł :app pozostaje cienki i tylko składa funkcjonalności.
  • Wspólny kod przenosi się niżej, do core; :core:model pozostaje niezależny od Androida i lekki.

W następnej części zajmiesz się zależnościami między tymi modułami i utrzymaniem przejrzystego grafu.

Często zadawane pytania

Czy lekcja „Moduły funkcji i moduły podstawowe” jest bezpłatna?

Tak — pełny tekst „Moduły funkcji i moduły podstawowe” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Android Academy, przejdź na CoddyKit PRO. Kurs Android Academy zawiera 4 lekcji w sumie.

Co nauczysz się w „Moduły funkcji i moduły podstawowe”?

Wyznacz granice modułów Ćwiczysz Android Academy z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć Android Academy?

Nie wymagamy żadnego doświadczenia. Android Academy w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 2 z 4.

Ile czasu zajmuje lekcja „Moduły funkcji i moduły podstawowe”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji Android Academy?

Tak. Każda lekcja Android Academy zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Dlaczego warto stosować moduły
  2. Moduły funkcji i moduły podstawowe
  3. Zarządzanie zależnościami modułów
  4. Nawigacja między modułami
← Powrót do Android Academy