Laboratorium programowania na Androida w Cloud Firestore

1. Przegląd

Cele

W tym laboratorium kodowym zbudujesz aplikację rekomendacji restauracji na Androida wspieraną przez Cloud Firestore. Dowiesz się, jak:

  • Odczytuj i zapisuj dane w Firestore z aplikacji na Androida
  • Słuchaj zmian w danych Firestore w czasie rzeczywistym
  • Użyj uwierzytelniania Firebase i reguł bezpieczeństwa, aby zabezpieczyć dane Firestore
  • Pisz złożone zapytania Firestore

Wymagania wstępne

Przed rozpoczęciem tego laboratorium kodowania upewnij się, że masz:

  • Android Studio Flamingo lub nowszy
  • Emulator Androida z API 19 lub nowszym
  • Node.js w wersji 16 lub nowszej
  • Wersja Java 17 lub nowsza

2. Utwórz projekt Firebase

  1. Zaloguj się do konsoli Firebase przy użyciu swojego konta Google.
  2. W konsoli Firebase kliknij Dodaj projekt .
  3. Jak pokazano na poniższym zrzucie ekranu, wpisz nazwę swojego projektu Firebase (na przykład „Friendly Eats”) i kliknij Kontynuuj .

9d2f625aebcab6af.png

  1. Możesz zostać poproszony o włączenie Google Analytics, dla celów tego laboratorium kodów Twój wybór nie ma znaczenia.
  2. Po mniej więcej minucie Twój projekt Firebase będzie gotowy. Kliknij Kontynuuj .

3. Skonfiguruj przykładowy projekt

Pobierz kod

Uruchom następujące polecenie, aby sklonować przykładowy kod dla tego laboratorium kodu. Spowoduje to utworzenie folderu o nazwie friendlyeats-android na twoim komputerze:

$ git clone https://github.com/firebase/friendlyeats-android

Jeśli nie masz git na swoim komputerze, możesz również pobrać kod bezpośrednio z GitHub.

Dodaj konfigurację Firebase

  1. W konsoli Firebase wybierz Przegląd projektu w lewym panelu nawigacyjnym. Kliknij przycisk Android , aby wybrać platformę. Gdy pojawi się prośba o podanie nazwy pakietu, wpisz com.google.firebase.example.fireeats

73d151ed16016421.png

  1. Kliknij Zarejestruj aplikację i postępuj zgodnie z instrukcjami, aby pobrać plik google-services.json i przenieść go do folderu app/ kodu, który właśnie pobrałeś. Następnie kliknij Dalej .

Zaimportuj projekt

Otwórz Android Studio. Kliknij Plik > Nowy > Importuj projekt i wybierz folder friendlyeats-android .

4. Skonfiguruj emulatory Firebase

W tym laboratorium kodowania użyjesz pakietu Firebase Emulator Suite do lokalnej emulacji Cloud Firestore i innych usług Firebase. Zapewnia to bezpieczne, szybkie i bezpłatne lokalne środowisko programistyczne do tworzenia aplikacji.

Zainstaluj interfejs wiersza polecenia Firebase

Najpierw musisz zainstalować Firebase CLI . Jeśli używasz systemu macOS lub Linux, możesz uruchomić następujące polecenie cURL:

curl -sL https://firebase.tools | bash

Jeśli używasz systemu Windows, przeczytaj instrukcje instalacji , aby uzyskać samodzielny plik binarny lub zainstalować przez npm .

Po zainstalowaniu CLI uruchomienie firebase --version powinno zgłosić wersję 9.0.0 lub nowszą:

$ firebase --version
9.0.0

Zaloguj sie

Uruchom firebase login , aby połączyć CLI ze swoim kontem Google. Spowoduje to otwarcie nowego okna przeglądarki w celu zakończenia procesu logowania. Upewnij się, że wybrałeś to samo konto, którego użyłeś podczas wcześniejszego tworzenia projektu Firebase.

Z poziomu folderu friendlyeats-android uruchom firebase use --add , aby połączyć lokalny projekt z projektem Firebase. Postępuj zgodnie z instrukcjami, aby wybrać projekt, który utworzyłeś wcześniej, a jeśli zostaniesz poproszony o wybranie aliasu, wprowadź default .

5. Uruchom aplikację

Teraz nadszedł czas, aby po raz pierwszy uruchomić pakiet Firebase Emulator Suite i aplikację FriendlyEats na Androida.

Uruchom emulatory

W swoim terminalu z katalogu friendlyeats-android uruchom firebase emulators:start , aby uruchomić emulatory Firebase. Powinieneś zobaczyć takie logi:

$ firebase emulators:start
i  emulators: Starting emulators: auth, firestore
i  firestore: Firestore Emulator logging to firestore-debug.log
i  ui: Emulator UI logging to ui-debug.log

┌─────────────────────────────────────────────────────────────┐
│ ✔  All emulators ready! It is now safe to connect your app. │
│ i  View Emulator UI at http://localhost:4000                │
└─────────────────────────────────────────────────────────────┘

┌────────────────┬────────────────┬─────────────────────────────────┐
│ Emulator       │ Host:Port      │ View in Emulator UI             │
├────────────────┼────────────────┼─────────────────────────────────┤
│ Authentication │ localhost:9099 │ http://localhost:4000/auth      │
├────────────────┼────────────────┼─────────────────────────────────┤
│ Firestore      │ localhost:8080 │ http://localhost:4000/firestore │
└────────────────┴────────────────┴─────────────────────────────────┘
  Emulator Hub running at localhost:4400
  Other reserved ports: 4500

Issues? Report them at https://github.com/firebase/firebase-tools/issues and attach the *-debug.log files.

Masz teraz kompletne lokalne środowisko programistyczne działające na swoim komputerze! Pamiętaj, aby pozostawić to polecenie uruchomione przez resztę laboratorium kodu, ponieważ Twoja aplikacja na Androida będzie musiała połączyć się z emulatorami.

Połącz aplikację z emulatorami

Otwórz pliki util/FirestoreInitializer.kt i util/AuthInitializer.kt w Android Studio. Te pliki zawierają logikę umożliwiającą połączenie zestawów SDK Firebase z lokalnymi emulatorami działającymi na komputerze podczas uruchamiania aplikacji.

W metodzie create() klasy FirestoreInitializer sprawdź ten fragment kodu:

    // Use emulators only in debug builds
    if (BuildConfig.DEBUG) {
        firestore.useEmulator(FIRESTORE_EMULATOR_HOST, FIRESTORE_EMULATOR_PORT)
    }

Używamy BuildConfig aby upewnić się, że łączymy się z emulatorami tylko wtedy, gdy nasza aplikacja działa w trybie debug . Gdy skompilujemy aplikację w trybie release , ten warunek będzie fałszywy.

Widzimy, że używa metody useEmulator(host, port) do połączenia Firebase SDK z lokalnym emulatorem Firestore. W całej aplikacji będziemy używać FirebaseUtil.getFirestore() w celu uzyskania dostępu do tej instancji FirebaseFirestore , więc mamy pewność, że zawsze łączymy się z emulatorem Firestore podczas pracy w trybie debug .

Uruchom aplikację

Jeśli poprawnie dodałeś plik google-services.json , projekt powinien się teraz skompilować. W Android Studio kliknij Build > Rebuild Project i upewnij się, że nie ma pozostałych błędów.

W Android Studio Uruchom aplikację na swoim emulatorze Androida. Na początku zostanie wyświetlony ekran „Zaloguj się”. Możesz użyć dowolnego adresu e-mail i hasła, aby zalogować się do aplikacji. Ten proces logowania łączy się z emulatorem uwierzytelniania Firebase, więc żadne prawdziwe dane uwierzytelniające nie są przesyłane.

Teraz otwórz interfejs użytkownika emulatorów, przechodząc do adresu http://localhost:4000 w przeglądarce internetowej. Następnie kliknij kartę Uwierzytelnianie i powinieneś zobaczyć właśnie utworzone konto:

Emulator autoryzacji Firebase

Po zakończeniu procesu logowania powinieneś zobaczyć ekran główny aplikacji:

de06424023ffb4b9.png

Wkrótce dodamy trochę danych, aby wypełnić ekran główny.

6. Zapisz dane w Firestore

W tej sekcji zapiszemy niektóre dane do Firestore, abyśmy mogli wypełnić obecnie pusty ekran główny.

Głównym modelowym obiektem w naszej aplikacji jest restauracja (patrz model/Restaurant.kt ). Dane Firestore są podzielone na dokumenty, kolekcje i kolekcje podrzędne. Każdą restaurację będziemy przechowywać jako dokument w kolekcji najwyższego poziomu o nazwie "restaurants" . Aby dowiedzieć się więcej o modelu danych Firestore, przeczytaj o dokumentach i kolekcjach w dokumentacji .

W celach demonstracyjnych dodamy w aplikacji funkcję tworzenia dziesięciu losowych restauracji po kliknięciu przycisku „Dodaj losowe elementy” w rozszerzonym menu. Otwórz plik MainFragment.kt i zamień zawartość metody onAddItemsClicked() na:

    private fun onAddItemsClicked() {
        val restaurantsRef = firestore.collection("restaurants")
        for (i in 0..9) {
            // Create random restaurant / ratings
            val randomRestaurant = RestaurantUtil.getRandom(requireContext())

            // Add restaurant
            restaurantsRef.add(randomRestaurant)
        }
    }

W powyższym kodzie należy zwrócić uwagę na kilka ważnych rzeczy:

  • Zaczęliśmy od uzyskania odniesienia do kolekcji "restaurants" . Kolekcje są tworzone niejawnie podczas dodawania dokumentów, więc nie było potrzeby tworzenia kolekcji przed zapisaniem danych.
  • Dokumenty można tworzyć za pomocą klas danych Kotlin, których używamy do tworzenia każdego dokumentu Restauracji.
  • Metoda add() dodaje dokument do kolekcji z automatycznie generowanym identyfikatorem, więc nie musieliśmy określać unikalnego identyfikatora dla każdej restauracji.

Teraz ponownie uruchom aplikację i kliknij przycisk „Dodaj losowe elementy” w menu przepełnienia (w prawym górnym rogu), aby wywołać właśnie napisany kod:

95691e9b71ba55e3.png

Teraz otwórz interfejs użytkownika emulatorów, przechodząc do adresu http://localhost:4000 w przeglądarce internetowej. Następnie kliknij kartę Firestore i powinieneś zobaczyć dane, które właśnie dodałeś:

Emulator autoryzacji Firebase

Te dane są w 100% lokalne dla Twojej maszyny. W rzeczywistości Twój prawdziwy projekt nie zawiera jeszcze bazy danych Firestore! Oznacza to, że można bezpiecznie eksperymentować z modyfikowaniem i usuwaniem tych danych bez konsekwencji.

Gratulacje, właśnie zapisałeś dane do Firestore! W kolejnym kroku dowiemy się, jak wyświetlić te dane w aplikacji.

7. Wyświetl dane z Firestore

W tym kroku nauczymy się, jak pobierać dane z Firestore i wyświetlać je w naszej aplikacji. Pierwszym krokiem do odczytania danych z Firestore jest utworzenie Query . Otwórz plik MainFragment.kt i dodaj następujący kod na początku metody onViewCreated() :

        // Firestore
        firestore = Firebase.firestore

        // Get the 50 highest rated restaurants
        query = firestore.collection("restaurants")
            .orderBy("avgRating", Query.Direction.DESCENDING)
            .limit(LIMIT.toLong())

Teraz chcemy wysłuchać zapytania, abyśmy otrzymali wszystkie pasujące dokumenty i byli powiadamiani o przyszłych aktualizacjach w czasie rzeczywistym. Ponieważ naszym ostatecznym celem jest powiązanie tych danych z RecyclerView , musimy utworzyć klasę RecyclerView.Adapter , aby nasłuchiwać danych.

Otwórz klasę FirestoreAdapter , która została już częściowo zaimplementowana. Najpierw zaimplementujmy w adapterze EventListener i zdefiniujmy funkcję onEvent , aby mogła odbierać aktualizacje zapytania Firestore:

abstract class FirestoreAdapter<VH : RecyclerView.ViewHolder>(private var query: Query?) :
        RecyclerView.Adapter<VH>(),
        EventListener<QuerySnapshot> { // Add this implements
    
    // ...

    // Add this method
    override fun onEvent(documentSnapshots: QuerySnapshot?, e: FirebaseFirestoreException?) {
        
        // Handle errors
        if (e != null) {
            Log.w(TAG, "onEvent:error", e)
            return
        }

        // Dispatch the event
        if (documentSnapshots != null) {
            for (change in documentSnapshots.documentChanges) {
                // snapshot of the changed document
                when (change.type) {
                    DocumentChange.Type.ADDED -> {
                        // TODO: handle document added
                    }
                    DocumentChange.Type.MODIFIED -> {
                        // TODO: handle document changed
                    }
                    DocumentChange.Type.REMOVED -> {
                        // TODO: handle document removed
                    }
                }
            }
        }

        onDataChanged()
    }
    
    // ...
}

Przy pierwszym załadowaniu słuchacz otrzyma jedno zdarzenie ADDED dla każdego nowego dokumentu. Ponieważ zestaw wyników zapytania zmienia się w czasie, słuchacz otrzyma więcej zdarzeń zawierających zmiany. Teraz zakończmy implementację odbiornika. Najpierw dodaj trzy nowe metody: onDocumentAdded , onDocumentModified i onDocumentRemoved :

    private fun onDocumentAdded(change: DocumentChange) {
        snapshots.add(change.newIndex, change.document)
        notifyItemInserted(change.newIndex)
    }

    private fun onDocumentModified(change: DocumentChange) {
        if (change.oldIndex == change.newIndex) {
            // Item changed but remained in same position
            snapshots[change.oldIndex] = change.document
            notifyItemChanged(change.oldIndex)
        } else {
            // Item changed and changed position
            snapshots.removeAt(change.oldIndex)
            snapshots.add(change.newIndex, change.document)
            notifyItemMoved(change.oldIndex, change.newIndex)
        }
    }

    private fun onDocumentRemoved(change: DocumentChange) {
        snapshots.removeAt(change.oldIndex)
        notifyItemRemoved(change.oldIndex)
    }

Następnie wywołaj te nowe metody z onEvent :

    override fun onEvent(documentSnapshots: QuerySnapshot?, e: FirebaseFirestoreException?) {

        // Handle errors
        if (e != null) {
            Log.w(TAG, "onEvent:error", e)
            return
        }

        // Dispatch the event
        if (documentSnapshots != null) {
            for (change in documentSnapshots.documentChanges) {
                // snapshot of the changed document
                when (change.type) {
                    DocumentChange.Type.ADDED -> {
                        onDocumentAdded(change) // Add this line
                    }
                    DocumentChange.Type.MODIFIED -> {
                        onDocumentModified(change) // Add this line
                    }
                    DocumentChange.Type.REMOVED -> {
                        onDocumentRemoved(change) // Add this line
                    }
                }
            }
        }

        onDataChanged()
    }

Na koniec zaimplementuj metodę startListening() aby dołączyć słuchacza:

    fun startListening() {
        if (registration == null) {
            registration = query.addSnapshotListener(this)
        }
    }

Teraz aplikacja jest w pełni skonfigurowana do odczytu danych z Firestore. Uruchom ponownie aplikację i powinieneś zobaczyć restauracje dodane w poprzednim kroku:

9e45f40faefce5d0.png

Teraz wróć do interfejsu użytkownika emulatora w przeglądarce i edytuj jedną z nazw restauracji. Powinieneś zobaczyć, jak zmienia się w aplikacji niemal natychmiast!

8. Sortuj i filtruj dane

Obecnie aplikacja wyświetla najwyżej oceniane restauracje w całej kolekcji, ale w prawdziwej aplikacji restauracyjnej użytkownik chciałby sortować i filtrować dane. Aplikacja powinna na przykład wyświetlać „Najlepsze restauracje serwujące owoce morza w Filadelfii” lub „Najtańsza pizza”.

Kliknięcie białego paska u góry aplikacji powoduje wyświetlenie okna dialogowego filtrów. W tej sekcji użyjemy zapytań Firestore, aby to okno dialogowe działało:

67898572a35672a5.png

Edytujmy metodę onFilter() pliku MainFragment.kt . Ta metoda akceptuje obiekt Filters , który jest obiektem pomocniczym, który utworzyliśmy w celu przechwytywania danych wyjściowych okna dialogowego filtrów. Zmienimy tę metodę, aby skonstruować zapytanie z filtrów:

    override fun onFilter(filters: Filters) {
        // Construct query basic query
        var query: Query = firestore.collection("restaurants")

        // Category (equality filter)
        if (filters.hasCategory()) {
            query = query.whereEqualTo(Restaurant.FIELD_CATEGORY, filters.category)
        }

        // City (equality filter)
        if (filters.hasCity()) {
            query = query.whereEqualTo(Restaurant.FIELD_CITY, filters.city)
        }

        // Price (equality filter)
        if (filters.hasPrice()) {
            query = query.whereEqualTo(Restaurant.FIELD_PRICE, filters.price)
        }

        // Sort by (orderBy with direction)
        if (filters.hasSortBy()) {
            query = query.orderBy(filters.sortBy.toString(), filters.sortDirection)
        }

        // Limit items
        query = query.limit(LIMIT.toLong())

        // Update the query
        adapter.setQuery(query)

        // Set header
        binding.textCurrentSearch.text = HtmlCompat.fromHtml(
            filters.getSearchDescription(requireContext()),
            HtmlCompat.FROM_HTML_MODE_LEGACY
        )
        binding.textCurrentSortBy.text = filters.getOrderDescription(requireContext())

        // Save filters
        viewModel.filters = filters
    }

W powyższym fragmencie budujemy obiekt Query , dołączając klauzule where i orderBy , aby dopasować podane filtry.

Uruchom ponownie aplikację i wybierz następujący filtr, aby wyświetlić najpopularniejsze tanie restauracje:

7a67a8a400c80c50.png

Powinieneś teraz zobaczyć przefiltrowaną listę restauracji zawierającą tylko opcje z niskimi cenami:

a670188398c3c59.png

Jeśli dotarłeś tak daleko, zbudowałeś teraz w Firestore w pełni działającą aplikację do przeglądania rekomendacji restauracji! Możesz teraz sortować i filtrować restauracje w czasie rzeczywistym. W kilku następnych sekcjach dodamy recenzje do restauracji i dodamy zasady bezpieczeństwa do aplikacji.

9. Organizuj dane w podkolekcjach

W tej sekcji dodamy oceny do aplikacji, aby użytkownicy mogli przeglądać swoje ulubione (lub najmniej lubiane) restauracje.

Kolekcje i podkolekcje

Do tej pory wszystkie dane restauracji były przechowywane w zbiorze najwyższego poziomu o nazwie „restauracje”. Kiedy użytkownik ocenia restaurację, chcemy dodać nowy obiekt Rating do restauracji. Do tego zadania użyjemy podkolekcji. O podkolekcji można myśleć jako o kolekcji dołączonej do dokumentu. Tak więc każdy dokument restauracji będzie miał podkolekcję ocen pełną dokumentów oceny. Podkolekcje pomagają organizować dane bez zapełniania naszych dokumentów lub wymagania skomplikowanych zapytań.

Aby uzyskać dostęp do podkolekcji, wywołaj .collection() w dokumencie nadrzędnym:

val subRef = firestore.collection("restaurants")
        .document("abc123")
        .collection("ratings")

Możesz uzyskiwać dostęp do podkolekcji i wysyłać do niej zapytania, tak jak w przypadku kolekcji najwyższego poziomu, nie ma ograniczeń rozmiaru ani zmian wydajności. Możesz przeczytać więcej o modelu danych Firestore tutaj .

Zapisywanie danych w transakcji

Dodanie Rating do odpowiedniej podkolekcji wymaga jedynie wywołania metody .add() , ale musimy również zaktualizować średnią ocenę i liczbę ocen obiektu Restaurant , aby odzwierciedlić nowe dane. Jeśli użyjemy oddzielnych operacji do wprowadzenia tych dwóch zmian, istnieje szereg warunków wyścigu, które mogą spowodować nieaktualne lub nieprawidłowe dane.

Aby upewnić się, że oceny są dodawane prawidłowo, użyjemy transakcji, aby dodać oceny do restauracji. Ta transakcja wykona kilka działań:

  • Przeczytaj aktualną ocenę restauracji i oblicz nową
  • Dodaj ocenę do podkolekcji
  • Zaktualizuj średnią ocenę restauracji i liczbę ocen

Otwórz RestaurantDetailFragment.kt i zaimplementuj funkcję addRating :

    private fun addRating(restaurantRef: DocumentReference, rating: Rating): Task<Void> {
        // Create reference for new rating, for use inside the transaction
        val ratingRef = restaurantRef.collection("ratings").document()

        // In a transaction, add the new rating and update the aggregate totals
        return firestore.runTransaction { transaction ->
            val restaurant = transaction.get(restaurantRef).toObject<Restaurant>()
                ?: throw Exception("Restaurant not found at ${restaurantRef.path}")

            // Compute new number of ratings
            val newNumRatings = restaurant.numRatings + 1

            // Compute new average rating
            val oldRatingTotal = restaurant.avgRating * restaurant.numRatings
            val newAvgRating = (oldRatingTotal + rating.rating) / newNumRatings

            // Set new restaurant info
            restaurant.numRatings = newNumRatings
            restaurant.avgRating = newAvgRating

            // Commit to Firestore
            transaction.set(restaurantRef, restaurant)
            transaction.set(ratingRef, rating)

            null
        }
    }

Funkcja addRating() zwraca Task reprezentujące całą transakcję. W funkcji onRating() do zadania dodawane są słuchacze, którzy odpowiadają na wynik transakcji.

Teraz ponownie uruchom aplikację i kliknij jedną z restauracji, która powinna wyświetlić ekran szczegółów restauracji. Kliknij przycisk + , aby rozpocząć dodawanie recenzji. Dodaj recenzję, wybierając liczbę gwiazdek i wprowadzając tekst.

78fa16cdf8ef435a.png

Naciśnięcie Prześlij rozpocznie transakcję. Po zakończeniu transakcji zobaczysz poniżej swoją recenzję i aktualizację liczby recenzji restauracji:

f9e670f40bd615b0.png

Gratulacje! Masz teraz społecznościową, lokalną, mobilną aplikację do recenzji restauracji opartą na Cloud Firestore. Słyszałem, że są one obecnie bardzo popularne.

10. Zabezpiecz swoje dane

Do tej pory nie rozważaliśmy bezpieczeństwa tej aplikacji. Skąd wiemy, że użytkownicy mogą tylko odczytywać i zapisywać poprawne własne dane? Bazy danych Firestore są zabezpieczone plikiem konfiguracyjnym o nazwie Security Rules .

Otwórz plik firestore.rules , powinieneś zobaczyć:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /{document=**} {
      //
      // WARNING: These rules are insecure! We will replace them with
      // more secure rules later in the codelab
      //
      allow read, write: if request.auth != null;
    }
  }
}

Zmieńmy te reguły, aby zapobiec niepożądanemu dostępowi lub zmianom danych, otwórzmy plik firestore.rules i zamieńmy zawartość na następującą:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    // Determine if the value of the field "key" is the same
    // before and after the request.
    function isUnchanged(key) {
      return (key in resource.data)
        && (key in request.resource.data)
        && (resource.data[key] == request.resource.data[key]);
    }

    // Restaurants
    match /restaurants/{restaurantId} {
      // Any signed-in user can read
      allow read: if request.auth != null;

      // Any signed-in user can create
      // WARNING: this rule is for demo purposes only!
      allow create: if request.auth != null;

      // Updates are allowed if no fields are added and name is unchanged
      allow update: if request.auth != null
                    && (request.resource.data.keys() == resource.data.keys())
                    && isUnchanged("name");

      // Deletes are not allowed.
      // Note: this is the default, there is no need to explicitly state this.
      allow delete: if false;

      // Ratings
      match /ratings/{ratingId} {
        // Any signed-in user can read
        allow read: if request.auth != null;

        // Any signed-in user can create if their uid matches the document
        allow create: if request.auth != null
                      && request.resource.data.userId == request.auth.uid;

        // Deletes and updates are not allowed (default)
        allow update, delete: if false;
      }
    }
  }
}

Te reguły ograniczają dostęp, aby upewnić się, że klienci dokonują tylko bezpiecznych zmian. Na przykład aktualizacje dokumentu restauracji mogą zmienić tylko oceny, a nie nazwę lub inne niezmienne dane. Oceny można tworzyć tylko wtedy, gdy identyfikator użytkownika jest zgodny z zalogowanym użytkownikiem, co zapobiega podszywaniu się.

Aby dowiedzieć się więcej o regułach bezpieczeństwa, zapoznaj się z dokumentacją .

11. Wniosek

Utworzyłeś teraz w pełni funkcjonalną aplikację na Firestore. Poznałeś najważniejsze funkcje Firestore, w tym:

  • Dokumenty i zbiory
  • Odczytywanie i zapisywanie danych
  • Sortowanie i filtrowanie za pomocą zapytań
  • Podkolekcje
  • Transakcje

Ucz się więcej

Aby dalej uczyć się o Firestore, oto kilka dobrych miejsc, od których możesz zacząć:

Aplikacja restauracji w tym laboratorium kodowym została oparta na przykładowej aplikacji „Friendly Eats”. Możesz przeglądać kod źródłowy tej aplikacji tutaj .

Opcjonalnie: Wdróż w środowisku produkcyjnym

Do tej pory ta aplikacja korzystała tylko z Firebase Emulator Suite. Jeśli chcesz dowiedzieć się, jak wdrożyć tę aplikację w prawdziwym projekcie Firebase, przejdź do następnego kroku.

12. (Opcjonalnie) Wdróż swoją aplikację

Do tej pory ta aplikacja była całkowicie lokalna, wszystkie dane są zawarte w Firebase Emulator Suite. W tej sekcji dowiesz się, jak skonfigurować swój projekt Firebase, aby ta aplikacja działała produkcyjnie.

Uwierzytelnianie Firebase

W konsoli Firebase przejdź do sekcji Uwierzytelnianie i kliknij Rozpocznij . Przejdź do karty Metoda logowania i wybierz opcję Adres e-mail/hasło u dostawców natywnych .

Włącz metodę logowania za pomocą adresu e-mail/hasła i kliknij przycisk Zapisz .

dostawcy-logowania.png

Ognisko

Utwórz bazę danych

Przejdź do sekcji Baza danych Firestore w konsoli i kliknij Utwórz bazę danych :

  1. Po wyświetleniu monitu o wybranie zasad bezpieczeństwa, aby rozpocząć w trybie produkcyjnym , wkrótce zaktualizujemy te reguły.
  2. Wybierz lokalizację bazy danych, której chcesz użyć dla swojej aplikacji. Pamiętaj, że wybór lokalizacji bazy danych jest decyzją stałą i aby ją zmienić, będziesz musiał stworzyć nowy projekt. Więcej informacji na temat wyboru lokalizacji projektu zawiera dokumentacja .

Zasady wdrażania

Aby wdrożyć reguły bezpieczeństwa, które napisałeś wcześniej, uruchom następujące polecenie w katalogu codelab:

$ firebase deploy --only firestore:rules

Spowoduje to wdrożenie zawartości firestore.rules do Twojego projektu, co możesz potwierdzić, przechodząc do karty Reguły w konsoli.

Wdróż indeksy

Aplikacja FriendlyEats ma złożone sortowanie i filtrowanie, które wymaga wielu niestandardowych indeksów złożonych. Można je utworzyć ręcznie w konsoli Firebase, ale łatwiej jest zapisać ich definicje w pliku firestore.indexes.json i wdrożyć je za pomocą Firebase CLI.

Jeśli otworzysz plik firestore.indexes.json zobaczysz, że wymagane indeksy zostały już dostarczone:

{
  "indexes": [
    {
      "collectionId": "restaurants",
      "queryScope": "COLLECTION",
      "fields": [
        { "fieldPath": "city", "mode": "ASCENDING" },
        { "fieldPath": "avgRating", "mode": "DESCENDING" }
      ]
    },
    {
      "collectionId": "restaurants",
      "queryScope": "COLLECTION",
      "fields": [
        { "fieldPath": "category", "mode": "ASCENDING" },
        { "fieldPath": "avgRating", "mode": "DESCENDING" }
      ]
    },
    {
      "collectionId": "restaurants",
      "queryScope": "COLLECTION",
      "fields": [
        { "fieldPath": "price", "mode": "ASCENDING" },
        { "fieldPath": "avgRating", "mode": "DESCENDING" }
      ]
    },
    {
      "collectionId": "restaurants",
      "queryScope": "COLLECTION",
      "fields": [
        { "fieldPath": "city", "mode": "ASCENDING" },
        { "fieldPath": "numRatings", "mode": "DESCENDING" }
      ]
    },
    {
      "collectionId": "restaurants",
      "queryScope": "COLLECTION",
      "fields": [
        { "fieldPath": "category", "mode": "ASCENDING" },
        { "fieldPath": "numRatings", "mode": "DESCENDING" }
      ]
    },
    {
      "collectionId": "restaurants",
      "queryScope": "COLLECTION",
      "fields": [
        { "fieldPath": "price", "mode": "ASCENDING" },
        { "fieldPath": "numRatings", "mode": "DESCENDING" }
      ]
    },
    {
      "collectionId": "restaurants",
      "queryScope": "COLLECTION",
      "fields": [
        { "fieldPath": "city", "mode": "ASCENDING" },
        { "fieldPath": "price", "mode": "ASCENDING" }
      ]
    },
    {
      "collectionId": "restaurants",
      "fields": [
        { "fieldPath": "category", "mode": "ASCENDING" },
        { "fieldPath": "price", "mode": "ASCENDING" }
      ]
    }
  ],
  "fieldOverrides": []
}

Aby wdrożyć te indeksy, uruchom następujące polecenie:

$ firebase deploy --only firestore:indexes

Pamiętaj, że tworzenie indeksu nie jest natychmiastowe, możesz monitorować postęp w konsoli Firebase.

Skonfiguruj aplikację

W plikach util/FirestoreInitializer.kt i util/AuthInitializer.kt skonfigurowaliśmy Firebase SDK do łączenia się z emulatorami w trybie debugowania:

    override fun create(context: Context): FirebaseFirestore {
        val firestore = Firebase.firestore
        // Use emulators only in debug builds
        if (BuildConfig.DEBUG) {
            firestore.useEmulator(FIRESTORE_EMULATOR_HOST, FIRESTORE_EMULATOR_PORT)
        }
        return firestore
    }

Jeśli chcesz przetestować swoją aplikację z prawdziwym projektem Firebase, możesz:

  1. Skompiluj aplikację w trybie wydania i uruchom ją na urządzeniu.
  2. Tymczasowo zastąp BuildConfig.DEBUG wartością false i ponownie uruchom aplikację.

Pamiętaj, że może być konieczne wylogowanie się z aplikacji i ponowne zalogowanie w celu prawidłowego połączenia z produkcją.