Эта страница была переведа с помощью Cloud Translation API.
Switch to English

Работа со списками данных на Android

Этот документ описывает работу со списками данных в Firebase. Чтобы узнать об основах чтения и записи данных Firebase, см. Чтение и запись данных на Android .

Получить ссылку на базу данных

Для чтения и записи данных из базы данных вам понадобится экземпляр DatabaseReference :

Ява

private DatabaseReference mDatabase;
// ...
mDatabase = FirebaseDatabase.getInstance().getReference();

Котлин + KTX

private lateinit var database: DatabaseReference
// ...
database = Firebase.database.reference

Читать и писать списки

Добавить в список данных

Используйте метод push() для добавления данных в список в многопользовательских приложениях. Метод push() генерирует уникальный ключ каждый раз, когда новый дочерний элемент добавляется к указанной ссылке Firebase. Используя эти автоматически сгенерированные ключи для каждого нового элемента в списке, несколько клиентов могут добавлять потомков в одно и то же место одновременно без конфликтов записи. Уникальный ключ, сгенерированный методом push() , основан на отметке времени, поэтому элементы списка автоматически упорядочиваются в хронологическом порядке.

Вы можете использовать ссылку на новые данные, возвращаемые методом push() чтобы получить значение автоматически сгенерированного ключа дочернего элемента или установить данные для дочернего элемента. Вызов getKey() по ссылке push() возвращает значение автоматически сгенерированного ключа.

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

Слушайте детские события

При работе со списками ваше приложение должно прослушивать дочерние события, а не события значений, используемые для отдельных объектов.

Дочерние события запускаются в ответ на определенные операции, которые происходят с дочерними элементами узла из операции, такой как новый дочерний элемент, добавленный с push() метода push() или дочерний элемент, обновляемый с помощью updateChildren() . Каждый из них вместе может быть полезен для прослушивания изменений конкретного узла в базе данных.

Чтобы прослушивать дочерние события в DatabaseReference , прикрепите ChildEventListener :

Слушатель Обратный вызов события Типичное использование
ChildEventListener onChildAdded() Получение списков элементов или прослушивание добавлений к списку элементов. Этот обратный вызов запускается один раз для каждого существующего дочернего элемента, а затем снова каждый раз, когда новый дочерний элемент добавляется к указанному пути. DataSnapshot переданный слушателю, содержит новые дочерние данные.
onChildChanged() Следите за изменениями элементов в списке. Это событие запускается каждый раз при изменении дочернего узла, включая любые изменения потомков дочернего узла. DataSnapshot переданный прослушивателю событий, содержит обновленные данные для дочернего элемента.
onChildRemoved() Прослушивайте элементы, удаляемые из списка. DataSnapshot переданный в обратный вызов события, содержит данные для удаленного дочернего элемента.
onChildMoved() Следите за изменениями порядка элементов в упорядоченном списке. Это событие запускается всякий раз, когда onChildChanged() вызов onChildChanged() запускается обновлением, которое вызывает изменение порядка дочернего элемента. Он используется с данными, упорядоченными с помощью orderByChild или orderByValue .

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

Ява

ChildEventListener childEventListener = new ChildEventListener() {
    @Override
    public void onChildAdded(DataSnapshot dataSnapshot, String previousChildName) {
        Log.d(TAG, "onChildAdded:" + dataSnapshot.getKey());

        // A new comment has been added, add it to the displayed list
        Comment comment = dataSnapshot.getValue(Comment.class);

        // ...
    }

    @Override
    public void onChildChanged(DataSnapshot dataSnapshot, String previousChildName) {
        Log.d(TAG, "onChildChanged:" + dataSnapshot.getKey());

        // A comment has changed, use the key to determine if we are displaying this
        // comment and if so displayed the changed comment.
        Comment newComment = dataSnapshot.getValue(Comment.class);
        String commentKey = dataSnapshot.getKey();

        // ...
    }

    @Override
    public void onChildRemoved(DataSnapshot dataSnapshot) {
        Log.d(TAG, "onChildRemoved:" + dataSnapshot.getKey());

        // A comment has changed, use the key to determine if we are displaying this
        // comment and if so remove it.
        String commentKey = dataSnapshot.getKey();

        // ...
    }

    @Override
    public void onChildMoved(DataSnapshot dataSnapshot, String previousChildName) {
        Log.d(TAG, "onChildMoved:" + dataSnapshot.getKey());

        // A comment has changed position, use the key to determine if we are
        // displaying this comment and if so move it.
        Comment movedComment = dataSnapshot.getValue(Comment.class);
        String commentKey = dataSnapshot.getKey();

        // ...
    }

    @Override
    public void onCancelled(DatabaseError databaseError) {
        Log.w(TAG, "postComments:onCancelled", databaseError.toException());
        Toast.makeText(mContext, "Failed to load comments.",
                Toast.LENGTH_SHORT).show();
    }
};
ref.addChildEventListener(childEventListener);

Котлин + KTX

val childEventListener = object : ChildEventListener {
    override fun onChildAdded(dataSnapshot: DataSnapshot, previousChildName: String?) {
        Log.d(TAG, "onChildAdded:" + dataSnapshot.key!!)

        // A new comment has been added, add it to the displayed list
        val comment = dataSnapshot.getValue<Comment>()

        // ...
    }

    override fun onChildChanged(dataSnapshot: DataSnapshot, previousChildName: String?) {
        Log.d(TAG, "onChildChanged: ${dataSnapshot.key}")

        // A comment has changed, use the key to determine if we are displaying this
        // comment and if so displayed the changed comment.
        val newComment = dataSnapshot.getValue<Comment>()
        val commentKey = dataSnapshot.key

        // ...
    }

    override fun onChildRemoved(dataSnapshot: DataSnapshot) {
        Log.d(TAG, "onChildRemoved:" + dataSnapshot.key!!)

        // A comment has changed, use the key to determine if we are displaying this
        // comment and if so remove it.
        val commentKey = dataSnapshot.key

        // ...
    }

    override fun onChildMoved(dataSnapshot: DataSnapshot, previousChildName: String?) {
        Log.d(TAG, "onChildMoved:" + dataSnapshot.key!!)

        // A comment has changed position, use the key to determine if we are
        // displaying this comment and if so move it.
        val movedComment = dataSnapshot.getValue<Comment>()
        val commentKey = dataSnapshot.key

        // ...
    }

    override fun onCancelled(databaseError: DatabaseError) {
        Log.w(TAG, "postComments:onCancelled", databaseError.toException())
        Toast.makeText(context, "Failed to load comments.",
                Toast.LENGTH_SHORT).show()
    }
}
databaseReference.addChildEventListener(childEventListener)

Слушайте важные события

Хотя использование ChildEventListener является рекомендуемым способом чтения списков данных, бывают ситуации, когда ValueEventListener присоединение ValueEventListener к ValueEventListener на список.

ValueEventListener к списку данных вернет весь список данных в виде одного DataSnapshot , который затем можно ValueEventListener для ValueEventListener к отдельным ValueEventListener .

Даже если для запроса есть только одно совпадение, снимок все равно остается списком; он просто содержит один элемент. Чтобы получить доступ к элементу, вам нужно перебрать результат:

Ява

// My top posts by number of stars
myTopPostsQuery.addValueEventListener(new ValueEventListener() {
    @Override
    public void onDataChange(DataSnapshot dataSnapshot) {
        for (DataSnapshot postSnapshot: dataSnapshot.getChildren()) {
            // TODO: handle the post
        }
    }

    @Override
    public void onCancelled(DatabaseError databaseError) {
        // Getting Post failed, log a message
        Log.w(TAG, "loadPost:onCancelled", databaseError.toException());
        // ...
    }
});

Котлин + KTX

// My top posts by number of stars
myTopPostsQuery.addValueEventListener(object : ValueEventListener {
    override fun onDataChange(dataSnapshot: DataSnapshot) {
        for (postSnapshot in dataSnapshot.children) {
            // TODO: handle the post
        }
    }

    override fun onCancelled(databaseError: DatabaseError) {
        // Getting Post failed, log a message
        Log.w(TAG, "loadPost:onCancelled", databaseError.toException())
        // ...
    }
})

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

Отключить слушателей

removeEventListener() удаляются путем вызова removeEventListener() в removeEventListener() на базу данных Firebase.

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

Вызов removeEventListener() для родительского слушателя не удаляет автоматически слушателей, зарегистрированных на его дочерних узлах; removeEventListener() также должен вызываться для всех дочерних слушателей, чтобы удалить обратный вызов.

Сортировка и фильтрация данных

Вы можете использовать класс Realtime Database Query для извлечения данных, отсортированных по ключу, по значению или по значению дочернего элемента. Вы также можете отфильтровать отсортированный результат по определенному количеству результатов или диапазону ключей или значений.

Сортировать данные

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

Метод использование
orderByChild() Упорядочивайте результаты по значению указанного дочернего ключа или вложенного дочернего пути.
orderByKey() Упорядочивайте результаты по дочерним ключам.
orderByValue() Упорядочивайте результаты по дочерним значениям.

Вы можете использовать только один метод заказа за раз. При многократном вызове метода упорядочивания в одном запросе возникает ошибка.

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

Ява

// My top posts by number of stars
String myUserId = getUid();
Query myTopPostsQuery = databaseReference.child("user-posts").child(myUserId)
        .orderByChild("starCount");
myTopPostsQuery.addChildEventListener(new ChildEventListener() {
    // TODO: implement the ChildEventListener methods as documented above
    // ...
});

Котлин + KTX

// My top posts by number of stars
val myUserId = uid
val myTopPostsQuery = databaseReference.child("user-posts").child(myUserId)
    .orderByChild("starCount")

myTopPostsQuery.addChildEventListener(object : ChildEventListener {
    // TODO: implement the ChildEventListener methods as documented above
    // ...
})

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

Вызов orderByChild() указывает дочерний ключ для упорядочивания результатов. В этом случае сообщения сортируются по значению их соответствующего "starCount" . Запросы также можно упорядочить по вложенным дочерним элементам, если у вас есть данные, которые выглядят следующим образом:

"posts": {
  "ts-functions": {
    "metrics": {
      "views" : 1200000,
      "likes" : 251000,
      "shares": 1200,
    },
    "title" : "Why you should use TypeScript for writing Cloud Functions",
    "author": "Doug",
  },
  "android-arch-3": {
    "metrics": {
      "views" : 900000,
      "likes" : 117000,
      "shares": 144,
    },
    "title" : "Using Android Architecture Components with Firebase Realtime Database (Part 3)",
    "author": "Doug",
  }
},

В этом примере мы можем упорядочить элементы списка по значениям, вложенным в ключ metrics , указав относительный путь к вложенному дочернему orderByChild() в нашем orderByChild() .

Ява

// Most viewed posts
Query myMostViewedPostsQuery = databaseReference.child("posts")
        .orderByChild("metrics/views");
myMostViewedPostsQuery.addChildEventListener(new ChildEventListener() {
    // TODO: implement the ChildEventListener methods as documented above
    // ...
});

Котлин + KTX

// Most viewed posts
val myMostViewedPostsQuery = databaseReference.child("posts")
        .orderByChild("metrics/views")
myMostViewedPostsQuery.addChildEventListener(object : ChildEventListener {
    // TODO: implement the ChildEventListener methods as documented above
    // ...
})

Дополнительные сведения о порядке упорядочения других типов данных см. В разделе Порядок упорядочения данных запроса .

Фильтрация данных

Чтобы фильтровать данные, вы можете комбинировать любой из методов limit или range с методом упорядочивания при построении запроса.

Метод использование
limitToFirst() Устанавливает максимальное количество элементов, возвращаемых с начала упорядоченного списка результатов.
limitToLast() Задает максимальное количество элементов, возвращаемых из конца упорядоченного списка результатов.
startAt() Возвращать элементы, которые больше или равны указанному ключу или значению в зависимости от выбранного метода упорядочивания.
endAt() Возвращать элементы меньше или равные указанному ключу или значению в зависимости от выбранного метода упорядочивания.
equalTo() Возвращать элементы, равные указанному ключу или значению, в зависимости от выбранного метода упорядочивания.

В отличие от методов упорядочивания, вы можете комбинировать несколько функций ограничения или диапазона. Например, вы можете комбинировать startAt() и endAt() чтобы ограничить результаты указанным диапазоном значений.

Даже если для запроса есть только одно совпадение, снимок все равно остается списком; он просто содержит один элемент. Чтобы получить доступ к элементу, вам нужно перебрать результат:

Ява

// My top posts by number of stars
myTopPostsQuery.addValueEventListener(new ValueEventListener() {
    @Override
    public void onDataChange(DataSnapshot dataSnapshot) {
        for (DataSnapshot postSnapshot: dataSnapshot.getChildren()) {
            // TODO: handle the post
        }
    }

    @Override
    public void onCancelled(DatabaseError databaseError) {
        // Getting Post failed, log a message
        Log.w(TAG, "loadPost:onCancelled", databaseError.toException());
        // ...
    }
});

Котлин + KTX

// My top posts by number of stars
myTopPostsQuery.addValueEventListener(object : ValueEventListener {
    override fun onDataChange(dataSnapshot: DataSnapshot) {
        for (postSnapshot in dataSnapshot.children) {
            // TODO: handle the post
        }
    }

    override fun onCancelled(databaseError: DatabaseError) {
        // Getting Post failed, log a message
        Log.w(TAG, "loadPost:onCancelled", databaseError.toException())
        // ...
    }
})

Ограничьте количество результатов

Вы можете использовать limitToFirst() и limitToLast() чтобы установить максимальное количество дочерних элементов, которые будут синхронизироваться для данного обратного вызова. Например, если вы используете limitToFirst() для установки лимита в 100, вы сначала получите только до 100 onChildAdded() вызовов onChildAdded() . Если в базе данных Firebase хранится менее 100 элементов, для каждого элемента срабатывает обратный вызов onChildAdded() .

По мере изменения элементов вы получаете onChildAdded() вызовы onChildAdded() для элементов, которые входят в запрос, и onChildRemoved() вызовы onChildRemoved() для элементов, которые выпадают из него, так что общее количество остается на уровне 100.

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

Ява

// Last 100 posts, these are automatically the 100 most recent
// due to sorting by push() keys
Query recentPostsQuery = databaseReference.child("posts")
        .limitToFirst(100);

Котлин + KTX

// Last 100 posts, these are automatically the 100 most recent
// due to sorting by push() keys.
return databaseReference.child("posts")
        .limitToFirst(100)

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

Фильтр по ключу или значению

Вы можете использовать startAt() , endAt() и equalTo() для выбора произвольных точек начала, окончания и эквивалентности для запросов. Это может быть полезно для разбивки данных на страницы или поиска элементов с дочерними элементами, имеющими определенное значение.

Как упорядочиваются данные запроса

В этом разделе объясняется, как данные сортируются каждым из методов упорядочивания в классе Query .

orderByChild

При использовании orderByChild() данные, содержащие указанный дочерний ключ, упорядочиваются следующим образом:

  1. Сначала идут потомки с null значением для указанного дочернего ключа.
  2. Следующими идут потомки со значением false для указанного дочернего ключа. Если несколько дочерних элементов имеют значение false , они лексикографически сортируются по ключу.
  3. Следующими идут потомки со значением true для указанного дочернего ключа. Если несколько дочерних элементов имеют значение true , они лексикографически сортируются по ключу.
  4. Далее идут дочерние элементы с числовым значением, отсортированные по возрастанию. Если несколько дочерних узлов имеют одинаковое числовое значение для указанного дочернего узла, они сортируются по ключу.
  5. Строки идут после чисел и сортируются лексикографически в порядке возрастания. Если несколько дочерних узлов имеют одинаковое значение для указанного дочернего узла, они упорядочиваются лексикографически по ключу.
  6. Объекты идут последними и лексикографически сортируются по ключам в порядке возрастания.

orderByKey

При использовании orderByKey() для сортировки данных данные возвращаются в порядке возрастания по ключу.

  1. Сначала идут дочерние элементы с ключом, который может быть проанализирован как 32-разрядное целое число, отсортированные в порядке возрастания.
  2. Далее следуют дочерние элементы со строковым значением в качестве ключа, лексикографически отсортированные по возрастанию.

orderByValue

При использовании orderByValue() дочерние элементы упорядочиваются по их значению. Критерии упорядочения такие же, как и в orderByChild() , за исключением того, что значение узла используется вместо значения указанного дочернего ключа.

Следующие шаги