Tworzenie aplikacji na Androida przy użyciu Firebase i Jetpack Compose

1. Wprowadzenie

Ostatnia aktualizacja: 16.11.2022 r.

Tworzenie aplikacji na Androida przy użyciu Firebase i Jetpack Compose

W ramach tego ćwiczenia w Codelabs utworzysz aplikację na Androida o nazwie Make It So. Interfejs tej aplikacji został w całości stworzony przy użyciu Jetpack Compose, czyli nowoczesnego zestawu narzędzi na Androida do tworzenia natywnego interfejsu użytkownika – jest intuicyjny i wymaga mniej kodu niż tworzenie plików .xml i powiązanie ich z działaniami, fragmentami lub widokami.

Pierwszym krokiem do zrozumienia, jak dobrze działają Firebase i Jetpack Compose, jest zrozumienie nowoczesnej architektury Androida. Dobra architektura systemu jest łatwa do opanowania, opracowania i utrzymywania, ponieważ zapewnia przejrzystą strukturę elementów i komunikację między nimi. W świecie Androida zalecana architektura to Model – Widok – WidokModel. Model reprezentuje warstwę, która uzyskuje dostęp do danych w aplikacji. Widok to warstwa interfejsu, który nie powinien wiedzieć o logice biznesowej. W klastrze ViewModel stosowana jest logika biznesowa, co czasem wymaga wywołania warstwy Model przez obiekt ViewModel.

Zdecydowanie zalecamy przeczytanie tego artykułu, aby dowiedzieć się, jak zastosowanie modelu Model – Widok – ViewModel działa w aplikacji na Androida utworzonej za pomocą Jetpack Compose, ponieważ ułatwi to zrozumienie bazy kodu i wykonanie kolejnych kroków.

Co utworzysz

Zrób listę rzeczy to prosta aplikacja do obsługi list zadań, która pozwala użytkownikowi dodawać i edytować zadania, dodawać flagi, priorytety i terminy oraz oznaczać zadania jako ukończone. Poniższe obrazy przedstawiają 2 główne strony tej aplikacji: stronę tworzenia zadań i stronę główną z utworzoną listą zadań.

Ekran Dodaj zadanie Ekran główny

Dodasz funkcje, których nie ma w tej aplikacji:

  • Uwierzytelnianie użytkowników za pomocą adresu e-mail i hasła
  • Dodaj detektor do kolekcji Firestore i skonfiguruj interfejs użytkownika do reagowania na zmiany
  • Dodaj niestandardowe logi czasu, aby monitorować wydajność określonego kodu w aplikacji
  • Utwórz przełącznik funkcji za pomocą Zdalnej konfiguracji i użyj wdrażania etapowego, aby ją uruchomić.

Czego się nauczysz

  • Jak korzystać z Uwierzytelniania Firebase, Monitorowania wydajności, Zdalnej konfiguracji i Cloud Firestore w nowoczesnej aplikacji na Androida
  • Dopasowanie interfejsów API Firebase do architektury MVVM
  • Jak odzwierciedlać zmiany wprowadzone za pomocą interfejsów API Firebase w interfejsie Compose

Czego potrzebujesz

2. Pobieranie przykładowej aplikacji i konfigurowanie Firebase

Pobieranie kodu przykładowej aplikacji

Sklonuj repozytorium GitHub, używając wiersza poleceń:

git clone https://github.com/FirebaseExtended/make-it-so-android.git

Skonfiguruj Firebase

Najpierw otwórz konsolę Firebase i utwórz projekt Firebase, klikając „+ Dodaj projekt” Jak widać poniżej:

Konsola Firebase

Postępuj zgodnie z instrukcjami wyświetlanymi na ekranie, aby dokończyć tworzenie projektu.

W każdym projekcie Firebase możesz utworzyć różne aplikacje: na Androida, iOS, Web, Flutter i Unity. Wybierz opcję Androida, tak jak tutaj:

Omówienie projektu Firebase

Następnie wykonaj te czynności:

  1. Wpisz com.example.makeitso jako nazwę pakietu i opcjonalnie podaj własną nazwę. W przypadku tych ćwiczeń w Codelabs nie musisz dodawać certyfikatu podpisywania debugowania.
  2. Kliknij Dalej, aby zarejestrować aplikację i uzyskać dostęp do pliku konfiguracyjnego Firebase.
  3. Kliknij Pobierz google-services.json, aby pobrać plik konfiguracji i zapisać go w katalogu make-it-so-android/app.
  4. Kliknij Dalej. Pakiety SDK Firebase są już zawarte w pliku build.gradle w przykładowym projekcie, dlatego kliknij Dalej, by przejść do sekcji Dalsze kroki.
  5. Aby zakończyć, kliknij Przejdź do konsoli.

Aby aplikacja Zrób to tak prawidłowo działała, przed przejściem do kodu musisz wykonać w konsoli 2 rzeczy: włączyć dostawców uwierzytelniania i utworzyć bazę danych Firestore. Najpierw włączmy uwierzytelnianie, aby użytkownicy mogli logować się w aplikacji:

  1. W menu Build (Tworzenie) wybierz Authentication (Uwierzytelnianie), a następnie kliknij Get Started (Rozpocznij).
  2. Na karcie Metoda logowania wybierz E-mail/hasło i włącz tę funkcję.
  3. Następnie kliknij Dodaj nowego dostawcę, wybierz i włącz Anonimowe.

Następnie skonfiguruj Firestore. W Firestore będziesz zapisywać zadania zalogowanego użytkownika. Każdy użytkownik otrzyma własny dokument w kolekcji w bazie danych.

  1. W menu Tworzenie wybierz Firestore, a następnie kliknij Utwórz bazę danych.
  2. Pozostaw opcję Rozpocznij w trybie produkcyjnym włączoną i kliknij Dalej.
  3. Gdy pojawi się prośba, wybierz lokalizację, w której będą przechowywane dane Cloud Firestore. Gdy tworzysz aplikację w wersji produkcyjnej, najlepiej, aby znajdowała się ona w regionie blisko większości użytkowników oraz w regionie, który jest typowy dla innych usług Firebase, takich jak Funkcje. W przypadku tych ćwiczeń w Codelabs możesz zachować region domyślny lub wybrać najbliższy region.
  4. Kliknij Włącz, aby udostępnić bazę danych Firestore.

Poświęćmy chwilę na utworzenie solidnych reguł zabezpieczeń w bazie danych Firestore. Otwórz panel Firestore i przejdź na kartę Reguły. Następnie zaktualizuj reguły zabezpieczeń tak:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      allow create: if request.auth != null;
      allow read, update, delete: if request.auth != null && resource.data.userId == request.auth.uid;
    }
  }
}

Zasadniczo każdy zalogowany użytkownik aplikacji może utworzyć własny dokument w ramach dowolnej kolekcji. Następnie po utworzeniu dokumentu tylko użytkownik, który go utworzył, może go wyświetlać, aktualizować lub usuwać.

Uruchamianie aplikacji

Teraz możesz uruchomić aplikację. Otwórz folder make-it-so-android/start w Android Studio i uruchom aplikację (możesz to zrobić za pomocą emulatora Androida lub prawdziwego urządzenia z Androidem).

3. Uwierzytelnianie Firebase

Którą funkcję chcesz dodać?

W obecnym stanie przykładowej aplikacji Zrób to użytkownik może zacząć z niej korzystać bez wcześniejszego logowania się. Do tego celu wykorzystuje anonimowe uwierzytelnianie. Jednak konta anonimowe nie umożliwiają użytkownikom dostępu do ich danych na innych urządzeniach ani nawet w przyszłych sesjach. Chociaż uwierzytelnianie anonimowe jest przydatne w procesie wprowadzenia, warto zawsze udostępniać użytkownikom opcję przejścia na inną formę logowania. Mając to na uwadze, w ramach tego ćwiczenia w Codelabs dowiesz się, jak dodać uwierzytelnianie przy użyciu adresu e-mail i hasła do aplikacji Make It So.

Czas na kodowanie

Gdy tylko użytkownik utworzy konto (wpisując adres e-mail i hasło), musisz poprosić interfejs Firebase Authentication API o dane logowania e-mail, a następnie połączyć nowe dane logowania z anonimowym kontem. Otwórz plik AccountServiceImpl.kt w Android Studio i zaktualizuj funkcję linkAccount, aby wyglądała tak:

model/service/impl/AccountServiceImpl.kt

override suspend fun linkAccount(email: String, password: String) {
    val credential = EmailAuthProvider.getCredential(email, password)
    auth.currentUser!!.linkWithCredential(credential).await()
}

Teraz otwórz plik SignUpViewModel.kt i wywołaj funkcję usługi linkAccount w bloku launchCatching funkcji onSignUpClick:

screens/sign_up/SignUpViewModel.kt

launchCatching {
    accountService.linkAccount(email, password)
    openAndPopUp(SETTINGS_SCREEN, SIGN_UP_SCREEN)
}

Najpierw próbuje się uwierzytelnić, a jeśli wywołanie się powiedzie, przechodzi do następnego ekranu (SettingsScreen). Jeśli podczas wykonywania tych wywołań w bloku launchCatching wystąpi błąd w pierwszym wierszu, wyjątek zostanie przechwycony i obsługiwany, a drugi wiersz w ogóle nie zostanie osiągnięty.

Gdy tylko SettingsScreen zostanie ponownie otwarty, musisz się upewnić, że opcje Zaloguj się i Utwórz konto nie są już dostępne, ponieważ użytkownik jest już uwierzytelniony. Aby to zrobić, SettingsViewModel nasłuchuje informacji o stanie bieżącego użytkownika (dostępny w wersji AccountService.kt) i sprawdzamy, czy konto jest anonimowe. Aby to zrobić, zaktualizuj uiState w SettingsViewModel.kt, aby wyglądał tak:

screen/settings/SettingsViewModel.kt

val uiState = accountService.currentUser.map {
    SettingsUiState(it.isAnonymous)
}

Ostatnia rzecz, jaką musisz zrobić, to zaktualizować pole uiState w narzędziu SettingsScreen.kt, tak aby zbierały stany wysyłane przez SettingsViewModel:

screen/settings/SettingsScreen.kt

val uiState by viewModel.uiState.collectAsState(
    initial = SettingsUiState(false)
)

Teraz za każdym razem, gdy użytkownik się zmieni, SettingsScreen będzie się ponownie skomponować, aby wyświetlić opcje zgodnie z nowym stanem uwierzytelniania użytkownika.

Czas na testy

Uruchom polecenie Zrób to tak i przejdź do ustawień, klikając ikonę koła zębatego w prawym górnym rogu ekranu. Następnie kliknij opcję tworzenia konta:

Ekran ustawień Zrób to tak Ekran rejestracji

Wpisz prawidłowy adres e-mail i silne hasło, by utworzyć konto. Powinno zadziałać. Powinno nastąpić przekierowanie na stronę ustawień, gdzie zobaczysz 2 nowe opcje: wylogowanie się i usunięcie konta. Nowe konto utworzone w panelu Uwierzytelnianie w konsoli Firebase możesz sprawdzić, klikając kartę Użytkownicy.

4. Cloud Firestore

Którą funkcję chcesz dodać?

W przypadku Cloud Firestore dodasz do kolekcji Firestore detektor, w którym będą przechowywane dokumenty reprezentujące zadania wyświetlone w sekcji Zrób to tak. Po dodaniu tego odbiornika będziesz otrzymywać wszystkie aktualizacje tej kolekcji.

Czas na kodowanie

Zaktualizuj pole Flow dostępne w StorageServiceImpl.kt, aby wyglądało tak:

model/service/impl/StorageServiceImpl.kt

override val tasks: Flow<List<Task>>
    get() =
      auth.currentUser.flatMapLatest { user ->
        firestore.collection(TASK_COLLECTION).whereEqualTo(USER_ID_FIELD, user.id).dataObjects()
      }

Ten kod dodaje detektor do kolekcji zadań opartej na: user.id. Każde zadanie jest reprezentowane przez dokument w kolekcji o nazwie tasks, a każde z nich ma pole o nazwie userId. Pamiętaj, że jeśli stan currentUser się zmieni (np. przez wylogowanie się), zostanie wysłany nowy Flow.

Teraz musisz skonfigurować pole Flow w usłudze TasksViewModel.kt tak, aby było takie samo jak w usłudze:

screen/tasks/TasksViewModel.kt

val tasks = storageService.tasks

Ostatnią rzeczą jest to, aby composable function w TasksScreens.kt, który reprezentuje interfejs użytkownika, zauważył ten przepływ i rejestrował go jako stan. Za każdym razem, gdy zmienia się stan, funkcja kompozycyjna automatycznie komponuje się ponownie i wyświetla użytkownikowi najnowszy stan. Dodaj do TasksScreen composable function:

screen/tasks/TasksScreen.kt

val tasks = viewModel
    .tasks
    .collectAsStateWithLifecycle(emptyList())

Gdy funkcja kompozycyjna ma dostęp do tych stanów, możesz zaktualizować obiekt LazyColumn (czyli strukturę, której używasz do wyświetlania listy na ekranie), tak aby wyglądał on tak:

screen/tasks/TasksScreen.kt

LazyColumn {
    items(tasks.value, key = { it.id }) { taskItem ->
        TaskItem( [...] )
    }
}

Czas na testy

Aby sprawdzić, czy wszystko zadziałało, dodaj nowe zadanie w aplikacji (kliknij przycisk dodawania w prawym dolnym rogu ekranu). Gdy utworzysz zadanie, powinno się ono pojawić w kolekcji Firestore w konsoli Firestore. Jeśli zalogujesz się w Zrób to tak na innych urządzeniach przy użyciu tego samego konta, będziesz mieć możliwość edytowania zadań do wykonania i sprawdzania ich aktualizacji na wszystkich urządzeniach w czasie rzeczywistym.

5. Monitorowanie wydajności

Którą funkcję chcesz dodać?

Wydajność jest bardzo ważna, ponieważ użytkownicy w niewielkim stopniu przestaną z niej korzystać, jeśli jej wydajność będzie niska i będą poświęcać na to zbyt dużo czasu. Dlatego czasami warto gromadzić pewne dane o konkretnej ścieżce, którą użytkownik pokonuje w Twojej aplikacji. Aby Ci w tym pomóc, Monitorowanie wydajności Firebase udostępnia niestandardowe logi czasu. Wykonaj kolejne czynności, aby dodać niestandardowe logi czasu i zmierzyć wydajność w różnych fragmentach kodu na stronie Make it Tak.

Czas na kodowanie

Jeśli otworzysz plik Performance.kt, zobaczysz funkcję wbudowaną o nazwie śledzenie. Ta funkcja wywołuje interfejs Performance Monitoring API, aby utworzyć niestandardowy log czasu, przekazując jego nazwę jako parametr. Kolejnym parametrem jest blok kodu, który chcesz monitorować. Domyślny wskaźnik zbieranych dla każdego logu czasu to czas potrzebny do pełnego uruchomienia:

model/usługa/wydajność.kt

inline fun <T> trace(name: String, block: Trace.() -> T): T = Trace.create(name).trace(block)

Możesz wybrać części bazy kodu, które Twoim zdaniem powinny być objęte pomiarem, i dodać do niej niestandardowe logi czasu. Oto przykład dodawania niestandardowego logu czasu do funkcji linkAccount, która została wyświetlona wcześniej (w AccountServiceImpl.kt) w ramach tego ćwiczenia w Codelabs:

model/service/impl/AccountServiceImpl.kt

override suspend fun linkAccount(email: String, password: String): Unit =
  trace(LINK_ACCOUNT_TRACE) {
      val credential = EmailAuthProvider.getCredential(email, password)
      auth.currentUser!!.linkWithCredential(credential).await()
  }

Teraz Twoja kolej! Dodaj niestandardowe logi czasu do aplikacji Zrób to tak i przejdź do następnej sekcji, aby sprawdzić, czy wszystko zadziałało zgodnie z oczekiwaniami.

Czas na testy

Gdy skończysz dodawać niestandardowe logi czasu, uruchom aplikację i pamiętaj, by kilka razy użyć funkcji, które chcesz mierzyć. Następnie otwórz konsolę Firebase i otwórz Panel wydajności. U dołu ekranu zobaczysz 3 karty: Żądania sieciowe, Niestandardowe logi czasu i Renderowanie ekranu.

Otwórz kartę Niestandardowe logi czasu i sprawdź, czy wyświetlają się w niej logi czasu dodane do bazy kodu oraz zobacz, ile czasu zajmuje zwykle wykonanie tych fragmentów kodu.

6. Zdalna konfiguracja

Którą funkcję chcesz dodać?

Zdalna konfiguracja ma wiele zastosowań – od zdalnej zmiany wyglądu aplikacji po konfigurowanie różnych zachowań dla różnych segmentów użytkowników. W ramach tego ćwiczenia w programowaniu użyjesz Zdalnej konfiguracji do utworzenia przełącznika funkcji, który będzie pokazywać lub ukrywać nową funkcję edycji zadania w aplikacji Zmień konfigurację.

Czas na kodowanie

Najpierw musisz utworzyć konfigurację w konsoli Firebase. Aby to zrobić, otwórz panel Zdalnej konfiguracji i kliknij przycisk Dodaj parametr. Wypełnij pola zgodnie z poniższą ilustracją:

Okno tworzenia parametru Zdalnej konfiguracji

Po wypełnieniu wszystkich pól możesz kliknąć przycisk Zapisz, a następnie Opublikuj. Po utworzeniu parametru i udostępnieniu go dla Twojej bazy kodu musisz dodać kod, który będzie pobierać nowe wartości do Twojej aplikacji. Otwórz plik ConfigurationServiceImpl.kt i zaktualizuj implementację tych 2 funkcji:

model/service/impl/ConfigurationServiceImpl.kt

override suspend fun fetchConfiguration(): Boolean {
  return remoteConfig.fetchAndActivate().await()
}

override val isShowTaskEditButtonConfig: Boolean
  get() = remoteConfig[SHOW_TASK_EDIT_BUTTON_KEY].asBoolean()

Pierwsza funkcja pobiera wartości z serwera i jest wywoływana zaraz po uruchomieniu aplikacji (SplashViewModel.kt). To najlepszy sposób, aby od samego początku mieć pewność, że najbardziej aktualne wartości będą dostępne na wszystkich ekranach. Zmiana interfejsu lub działania aplikacji później, gdy użytkownik jest w trakcie wykonywania jakiejś czynności, może być dla użytkownika nieprzyjemna.

Druga funkcja zwraca wartość logiczną, która została opublikowana dla parametru utworzonego właśnie w Konsoli. Te informacje trzeba będzie pobrać w narzędziu TasksViewModel.kt, dodając do funkcji loadTaskOptions ten kod:

screen/tasks/TasksViewModel.kt

fun loadTaskOptions() {
  val hasEditOption = configurationService.isShowTaskEditButtonConfig
  options.value = TaskActionOption.getOptions(hasEditOption)
}

Pobierasz wartość z pierwszego wiersza i używasz jej do wczytywania opcji menu dla pozycji zadania w drugim wierszu. Jeśli wartość to false, oznacza to, że menu nie będzie zawierać opcji edycji. Masz już listę opcji. Teraz musisz zadbać o to, aby interfejs wyświetlał się poprawnie. Gdy tworzysz aplikację za pomocą Jetpack Compose, poszukaj elementu composable function, który deklaruje, jak powinien wyglądać interfejs użytkownika TasksScreen. Otwórz plik TasksScreen.kt i zaktualizuj element LazyColum, aby wskazywał opcje dostępne w usłudze TasksViewModel.kt:

screen/tasks/TasksScreen.kt

val options by viewModel.options

LazyColumn {
  items(tasks.value, key = { it.id }) { taskItem ->
    TaskItem(
      options = options,
      [...]
    )
  }
}

TaskItem to kolejny composable function, który deklaruje, jak powinien wyglądać interfejs użytkownika danego zadania. Każde zadanie ma też menu z opcjami, które wyświetla się, gdy użytkownik kliknie ikonę z 3 kropkami na końcu zadania.

Czas na testy

Teraz możesz uruchomić aplikację. Sprawdź, czy wartość opublikowana za pomocą konsoli Firebase odpowiada działaniu aplikacji:

  • Jeśli to false, po kliknięciu ikony z 3 kropkami zobaczysz tylko 2 opcje.
  • Jeśli to true, po kliknięciu ikony z 3 kropkami powinny wyświetlić się 3 opcje.

Spróbuj kilka razy zmienić tę wartość w konsoli i ponownie uruchomić aplikację. Tak łatwo możesz uruchamiać nowe funkcje w swojej aplikacji dzięki Zdalnej konfiguracji.

7. Gratulacje

Gratulujemy! Udało Ci się utworzyć aplikację na Androida korzystającą z Firebase i Jetpack Compose.

Udało Ci się dodać Uwierzytelnianie Firebase, Monitorowanie wydajności, Zdalną konfigurację i Cloud Firestore do aplikacji na Androida w całości opracowanej z wykorzystaniem Jetpack Compose na potrzeby interfejsu użytkownika, co pozwoliło jej dopasować ją do zalecanej architektury MVVM.

Więcej informacji

Dokumentacja