Статьи

Начало работы с Cloud Firestore для Android

Cloud Firestore является недавним дополнением к семейству продуктов Firebase. Хотя он все еще находится в бета-версии, он уже представлен Google как более гибкая и многофункциональная альтернатива базе данных Firebase Realtime.

Если вы когда-либо использовали базу данных реального времени, вы, вероятно, знаете, что это по сути большой документ JSON, который лучше всего подходит для хранения только простых пар ключ-значение. Эффективное и безопасное хранение иерархических данных на них, хотя и возможно, довольно громоздко и требует хорошо продуманной стратегии, которая обычно включает в себя максимально возможное выравнивание данных или их денормализацию. Без такой стратегии запросы к базе данных реального времени, вероятно, потребуют чрезмерно большую пропускную способность.

Облачный Firestore, более похожий на документно-ориентированные базы данных, такие как MongoDB и CouchDB, не имеет таких проблем. Более того, он поставляется с большим количеством очень удобных функций, таких как поддержка пакетных операций, атомарных записей и индексированных запросов.

В этом руководстве я помогу вам начать работу с Cloud Firestore на платформе Android.

Чтобы следовать этому руководству, вам понадобится:

  • последняя версия Android Studio
  • учетная запись Firebase
  • и устройство или эмулятор под управлением Android 4.4 или выше

Прежде чем использовать продукты Firebase в своем приложении для Android, вы должны создать новый проект для него в консоли Firebase. Для этого войдите в консоль и нажмите кнопку « Добавить проект» на экране приветствия.

Экран приветствия консоли Firebase

В появившемся диалоговом окне дайте значимое имя проекту, при необходимости присвойте ему значимый идентификатор и нажмите кнопку « Создать проект» .

Добавить диалог проекта

После того, как проект создан, вы можете установить Firestore в качестве его базы данных, перейдя в Develop> Database и нажав кнопку Try Firestore Beta .

Страница выбора базы данных

На следующем экране убедитесь, что вы выбрали опцию « Запуск в тестовом режиме» и нажмите кнопку « Включить» .

Страница конфигурации безопасности Firestore

На этом этапе у вас будет пустая база данных Firestore, готовая для использования в вашем приложении.

Пустая база данных Firestore

Ваш проект Android Studio до сих пор ничего не знает о проекте Firebase, который вы создали на предыдущем шаге. Самый простой способ установить связь между ними — использовать Firebase Assistant в Android Studio.

Перейдите в Инструменты> Firebase, чтобы открыть Ассистент.

Окно Firebase Assistant

Поскольку Firestore все еще находится в бета-версии, Помощник пока не поддерживает его. Тем не менее, добавив Firebase Analytics в свое приложение, вы сможете автоматизировать большинство необходимых шагов настройки.

Начните с нажатия на ссылку «Записать событие в Google Analytics» в разделе « Аналитика » и нажмите кнопку « Подключиться к Firebase» . Теперь должно появиться новое окно браузера с вопросом, хотите ли вы разрешить Android Studio, помимо прочего, управлять данными Firebase.

Android Studio запрашивает разрешения

Нажмите кнопку Разрешить , чтобы продолжить.

Вернувшись в Android Studio, в появившемся диалоговом окне выберите вариант « Выбрать существующий проект Firebase или Google» , выберите созданный ранее проект Firebase и нажмите кнопку « Подключиться к Firebase» .

Диалог подключения к Firebase

Затем нажмите кнопку Add Analytics to your app , чтобы добавить основные зависимости Firebase в ваш проект.

Наконец, чтобы добавить Firestore в качестве зависимости implementation , добавьте следующую строку в файл build.gradle модуля app :

1
implementation ‘com.google.firebase:firebase-firestore:11.8.0’

Не забудьте нажать кнопку « Синхронизировать сейчас» , чтобы завершить настройку. Если вы столкнулись с ошибками конфликта версий во время процесса синхронизации, убедитесь, что версии зависимостей Firestore и Firebase Core идентичны, и повторите попытку.

Firestore — это база данных NoSQL, которая позволяет хранить данные в форме JSON-подобных документов. Однако документ, хранящийся в нем, не может существовать независимо. Он всегда должен принадлежать коллекции. Как следует из названия, коллекция — это не что иное, как набор документов.

Документы в коллекции — это, очевидно, братья и сестры. Если вы хотите установить отношения между родителями и детьми, вы должны использовать вложенные коллекции. Подколлекция — это просто коллекция, которая принадлежит документу. По умолчанию документ автоматически становится родительским для всех документов, принадлежащих его вложенным коллекциям.

Также стоит отметить, что Firestore самостоятельно управляет созданием и удалением как коллекций, так и вложенных коллекций. Всякий раз, когда вы пытаетесь добавить документ в несуществующую коллекцию, он создает коллекцию. Аналогично, после удаления всех документов из коллекции он удаляется.

Чтобы иметь возможность записи в базу данных Firestore из вашего приложения Android, вы должны сначала получить ссылку на нее, вызвав метод getInstance() класса FirebaseFirestore .

1
val myDB = FirebaseFirestore.getInstance()

Затем вы должны либо создать новую коллекцию, либо получить ссылку на существующую коллекцию, вызвав метод collection() . Например, в пустой базе данных следующий код создает новую коллекцию с именем solar_system :

1
val solarSystem = myDB.collection(«solar_system»)

Получив ссылку на коллекцию, вы можете начать добавлять в нее документы, вызвав ее метод add() , который ожидает карту в качестве аргумента.

01
02
03
04
05
06
07
08
09
10
11
12
13
// Add a document
solarSystem.add(mapOf(
        «name» to «Mercury»,
        «number» to 1,
        «gravity» to 3.7
))
 
// Add another document
solarSystem.add(mapOf(
        «name» to «Venus»,
        «number» to 2,
        «gravity» to 8.87
))

Метод add() автоматически генерирует и присваивает уникальный буквенно-цифровой идентификатор каждому документу, который он создает. Если вы хотите, чтобы ваши документы имели собственные пользовательские идентификаторы, вы должны сначала вручную создать эти документы, вызвав метод document() , который принимает уникальную строку идентификатора в качестве входных данных. Затем вы можете заполнить документы, вызвав метод set() , который, как и метод add , ожидает карту в качестве единственного аргумента.

Например, следующий код создает и заполняет новый документ с именем PLANET_EARTH :

1
2
3
4
5
6
solarSystem.document(«PLANET_EARTH»)
       .set(mapOf(
               «name» to «Earth»,
               «number» to 3,
               «gravity» to 9.807
       ))

Если вы зайдете в консоль Firebase и посмотрите на содержимое базы данных, вы сможете легко определить пользовательский идентификатор.

Новые записи в базе данных Firestore

Помните, что если пользовательский идентификатор, который вы передаете методу document() уже существует в базе данных, метод set() перезапишет содержимое связанного документа.

Поддержка вложенных коллекций является одной из самых мощных функций Firestore, и именно этим она заметно отличается от базы данных Firebase Realtime. Используя подколлекции, вы можете не только легко добавлять вложенные структуры в ваши данные, но и быть уверенными, что ваши запросы будут использовать минимальную пропускную способность.

Создание вложенной коллекции очень похоже на создание коллекции. Все, что вам нужно сделать, это вызвать метод collection() объекта DocumentReference и передать ему строку, которая будет использоваться в качестве имени вложенной коллекции.

Например, следующий код создает вложенную коллекцию под названием « satellites и связывает ее с документом PLANET_EARTH :

1
2
val satellitesOfEarth = solarSystem.document(«PLANET_EARTH»)
                                  .collection(«satellites»)

Если у вас есть ссылка на вложенную коллекцию, вы можете вызывать методы add() или set() для добавления документов в нее.

1
2
3
4
5
satellitesOfEarth.add(mapOf(
        «name» to «The Moon»,
        «gravity» to 1.62,
        «radius» to 1738
))

После запуска приведенного выше кода документ PLANET_EARTH будет выглядеть в консоли Firebase следующим образом:

Подборка документа

Выполнить операцию чтения в базе данных Firestore очень просто, если вы знаете идентификатор документа, который хотите прочитать. Почему? Потому что вы можете напрямую получить ссылку на документ, вызвав методы collection() и document() . Например, вот как вы можете получить ссылку на документ PLANET_EARTH который принадлежит коллекции solar_system :

1
2
val planetEarthDoc = myDB.collection(«solar_system»)
                        .document(«PLANET_EARTH»)

Чтобы действительно прочитать содержимое документа, вы должны вызвать асинхронный метод get() , который возвращает Task . Добавляя OnSuccessListener к нему, вы можете получать уведомления, когда операция чтения завершается успешно.

Результатом операции чтения является объект DocumentSnapshot , который содержит пары ключ-значение, присутствующие в документе. Используя его метод get() , вы можете получить значение любого допустимого ключа. В следующем примере показано, как:

1
2
3
4
5
6
7
8
planetEarthDoc.get().addOnSuccessListener {
    println(
       «Gravity of ${it.get(«name»)} is ${it.get(«gravity»)} m/s/s»
    )
}
 
// OUTPUT:
// Gravity of Earth is 9.807 m/s/s

Если вы не знаете идентификатор документа, который хотите прочитать, вам придется выполнить традиционный запрос для всей коллекции. API Firestore предоставляет интуитивно понятные методы фильтрации, такие как whereEqualTo() , whereLessThan() и whereGreaterThan() . Поскольку методы фильтра могут возвращать несколько документов в качестве результатов, вам потребуется цикл внутри OnSuccessListener для обработки каждого результата.

Например, чтобы получить содержимое документа для планеты Венера, который мы добавили на предыдущем шаге, вы можете использовать следующий код:

01
02
03
04
05
06
07
08
09
10
11
12
myDB.collection(«solar_system»)
  .whereEqualTo(«name», «Venus»)
  .get().addOnSuccessListener {
      it.forEach {
        println(
          «Gravity of ${it.get(«name»)} is ${it.get(«gravity»)} m/s/s»
        )
      }
  }
 
// OUTPUT:
// Gravity of Venus is 8.87 m/s/s

Наконец, если вы заинтересованы в чтении всех документов, принадлежащих коллекции, вы можете напрямую вызвать метод get() для коллекции. Например, вот как вы можете перечислить все планеты, присутствующие в коллекции solar_system :

01
02
03
04
05
06
07
08
09
10
11
myDB.collection(«solar_system»)
        .get().addOnSuccessListener {
            it.forEach {
                println(it.get(«name»))
            }
        }
 
// OUTPUT:
// Earth
// Venus
// Mercury

Обратите внимание, что по умолчанию нет определенного порядка, в котором возвращаются результаты. Если вы хотите упорядочить их по ключу, который присутствует во всех результатах, вы можете использовать метод orderBy() . Следующий код упорядочивает результаты на основе значения number клавиши:

01
02
03
04
05
06
07
08
09
10
11
12
myDB.collection(«solar_system»)
                .orderBy(«number»)
                .get().addOnSuccessListener {
            it.forEach {
                println(it.get(«name»))
            }
        }
 
// OUTPUT:
// Mercury
// Venus
// Earth

Чтобы удалить документ с известным идентификатором, все, что вам нужно сделать, это получить ссылку на него и затем вызвать метод delete() .

1
2
3
myDB.collection(«solar_system»)
   .document(«PLANET_EARTH»)
   .delete()

Удаление нескольких документов — документов, которые вы получаете в результате запроса — немного сложнее, поскольку для этого нет встроенного метода. Есть два разных подхода, которым вы можете следовать.

Самый простой и интуитивно понятный подход, хотя он подходит только для очень небольшого количества документов, состоит в том, чтобы просмотреть результаты, получить ссылку на каждый документ и затем вызвать метод delete() . Вот как вы можете использовать этот подход для удаления всех документов из коллекции solar_system :

1
2
3
4
5
6
myDB.collection(«solar_system»)
       .get().addOnSuccessListener {
           it.forEach {
               it.reference.delete()
           }
       }

Более эффективный и масштабируемый подход заключается в использовании пакетной операции. Пакетные операции могут не только удалять несколько документов атомарно, но и значительно сократить количество необходимых сетевых подключений.

Чтобы создать новый пакет, вы должны вызвать метод batch() вашей базы данных, который возвращает экземпляр класса WriteBatch . Затем вы можете просмотреть все результаты запроса и отметить их для удаления, передав их методу delete() объекта WriteBatch . Наконец, чтобы действительно запустить процесс удаления, вы можете вызвать метод commit() . Следующий код показывает вам, как:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
myDB.collection(«solar_system»)
        .get().addOnSuccessListener {
 
    // Create batch
    val myBatch = myDB.batch()
 
    // Add documents to batch
    it.forEach {
        myBatch.delete(it.reference)
    }
 
    // Run batch
    myBatch.commit()
}

Обратите внимание, что попытка добавить слишком много документов в одну пакетную операцию может привести к ошибкам нехватки памяти. Поэтому, если ваш запрос может вернуть большое количество документов, вы должны убедиться, что вы разбили их на несколько пакетов

Из этого вводного руководства вы узнали, как выполнять операции чтения и записи в Google Cloud Firestore. Я предлагаю вам начать использовать его в своих проектах Android прямо сейчас. Есть большая вероятность, что в будущем она заменит базу данных реального времени. На самом деле, Google уже заявляет, что к моменту выхода бета-версии она станет гораздо более надежной и масштабируемой, чем база данных в реальном времени.

Чтобы узнать больше о Cloud Firestore, вы можете обратиться к его официальной документации .

И пока вы здесь, ознакомьтесь с некоторыми другими нашими учебниками по разработке приложений для Firebase и Android!

  • Android SDK
    Как создать приложение для Android-чата с помощью Firebase
  • Android SDK
    Начните с Firebase для Android
  • Мобильная разработка
    Правила безопасности Firebase
    Чике Мгбемена
  • Android SDK
    Как загрузить изображения в Firebase из приложения для Android
    Чинеду Изучукву