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

Прежде чем начать

Прежде чем использовать Realtime Database , вам необходимо:

  • Зарегистрируйте свой проект Unity и настройте его для использования Firebase.

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

    • Если у вас нет проекта Unity, вы можете скачать пример приложения .

  • Добавьте Firebase Unity SDK (в частности, FirebaseDatabase.unitypackage ) в свой проект Unity.

Обратите внимание, что добавление Firebase в ваш проект Unity включает в себя действия как в консоли Firebase , так и в открытом проекте Unity (например, вы загружаете файлы конфигурации Firebase из консоли, а затем перемещаете их в свой проект Unity).

Сохранение данных

Существует пять способов записи данных в Firebase Realtime Database :

Метод Распространенные способы применения
SetValueAsync() Запись или замена данных по указанному пути, например, users/<user-id>/<username> .
SetRawJsonValueAsync() Записывайте или заменяйте данные в формате JSON, например, users/<user-id>/<username> .
Push() Добавить в список данных. Каждый раз, когда вы вызываете Push() , Firebase генерирует уникальный ключ, который также может использоваться в качестве уникального идентификатора, например, user-scores/<user-id>/<unique-score-id> .
UpdateChildrenAsync() Обновить некоторые ключи для заданного пути, не заменяя все данные.
RunTransaction() Обновите сложные данные, которые могут быть повреждены в результате одновременных обновлений.

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

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

using Firebase;
using Firebase.Database;

public class MyScript: MonoBehaviour {
  void Start() {
    // Get the root reference location of the database.
    DatabaseReference reference = FirebaseDatabase.DefaultInstance.RootReference;
  }
}

Запись, обновление или удаление данных в справочном режиме.

Основные операции записи

Для выполнения базовых операций записи можно использовать SetValueAsync() , чтобы сохранить данные по указанной ссылке, заменив существующие данные по этому пути. Этот метод позволяет передавать типы, соответствующие доступным типам JSON, следующим образом:

  • string
  • long
  • double
  • bool
  • Dictionary<string, Object>
  • List<Object>

Если вы используете типизированный объект C#, вы можете воспользоваться встроенной функцией JsonUtility.ToJson() для преобразования объекта в необработанный JSON и вызвать SetRawJsonValueAsync() . Например, у вас может быть класс User, который выглядит следующим образом:

public class User {
    public string username;
    public string email;

    public User() {
    }

    public User(string username, string email) {
        this.username = username;
        this.email = email;
    }
}

Добавить пользователя с помощью SetRawJsonValueAsync() можно следующим образом:

private void writeNewUser(string userId, string name, string email) {
    User user = new User(name, email);
    string json = JsonUtility.ToJson(user);

    mDatabaseRef.Child("users").Child(userId).SetRawJsonValueAsync(json);
}

Использование SetValueAsync() или SetRawJsonValueAsync() таким образом перезаписывает данные в указанном месте, включая любые дочерние узлы. Однако вы все еще можете обновить дочерний узел, не перезаписывая весь объект. Если вы хотите разрешить пользователям обновлять свои профили, вы можете обновить имя пользователя следующим образом:

mDatabaseRef.Child("users").Child(userId).Child("username").SetValueAsync(name);

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

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

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

Обновить определенные поля

Для одновременной записи в определенные дочерние узлы без перезаписи других дочерних узлов используйте метод UpdateChildrenAsync() .

При вызове UpdateChildrenAsync() вы можете обновить значения дочерних элементов нижнего уровня, указав путь к ключу. Если данные хранятся в нескольких местах для лучшего масштабирования, вы можете обновить все экземпляры этих данных, используя механизм веерного распределения данных . Например, в игре может быть класс LeaderboardEntry следующего вида:

public class LeaderboardEntry {
    public string uid;
    public int score = 0;

    public LeaderboardEntry() {
    }

    public LeaderboardEntry(string uid, int score) {
        this.uid = uid;
        this.score = score;
    }

    public Dictionary<string, Object> ToDictionary() {
        Dictionary<string, Object> result = new Dictionary<string, Object>();
        result["uid"] = uid;
        result["score"] = score;

        return result;
    }
}

Для создания записи в таблице лидеров и одновременного обновления её с учётом последних результатов и списка результатов пользователя игра использует следующий код:

private void WriteNewScore(string userId, int score) {
    // Create new entry at /user-scores/$userid/$scoreid and at
    // /leaderboard/$scoreid simultaneously
    string key = mDatabase.Child("scores").Push().Key;
    LeaderBoardEntry entry = new LeaderBoardEntry(userId, score);
    Dictionary<string, Object> entryValues = entry.ToDictionary();

    Dictionary<string, Object> childUpdates = new Dictionary<string, Object>();
    childUpdates["/scores/" + key] = entryValues;
    childUpdates["/user-scores/" + userId + "/" + key] = entryValues;

    mDatabase.UpdateChildrenAsync(childUpdates);
}

В этом примере используется Push() для создания записи в узле, содержащей записи для всех пользователей, по адресу /scores/$key , и одновременного получения ключа с помощью Key . Затем этот ключ можно использовать для создания второй записи в списке оценок пользователя по адресу /user-scores/$userid/$key .

Используя эти пути, вы можете одновременно обновлять несколько мест в дереве JSON одним вызовом метода UpdateChildrenAsync() , как, например, в этом примере создается новая запись в обоих местах. Одновременные обновления, выполненные таким образом, являются атомарными: либо все обновления завершаются успешно, либо все обновления завершаются неудачей.

Удалить данные

Простейший способ удалить данные — вызвать метод RemoveValue() для ссылки на местоположение этих данных.

Удаление также можно выполнить, указав null в качестве значения для другой операции записи, например, SetValueAsync() или UpdateChildrenAsync() . Этот метод можно использовать с UpdateChildrenAsync() для удаления нескольких дочерних элементов за один вызов API.

Узнайте, когда ваши данные будут зафиксированы.

Чтобы узнать, когда ваши данные зафиксированы на сервере Firebase Realtime Database , вы можете добавить продолжение. Методы SetValueAsync() и UpdateChildrenAsync() возвращают Task , который позволяет узнать, когда операция завершена. Если вызов по какой-либо причине не удался, свойство IsFaulted объекта Task будет иметь значение true, а свойство Exception укажет причину сбоя.

Сохраняйте данные в виде транзакций.

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

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

private void AddScoreToLeaders(string email, 
                               long score,
                               DatabaseReference leaderBoardRef) {

    leaderBoardRef.RunTransaction(mutableData => {
      List<object> leaders = mutableData.Value as List<object>

      if (leaders == null) {
        leaders = new List<object>();
      } else if (mutableData.ChildrenCount >= MaxScores) {
        long minScore = long.MaxValue;
        object minVal = null;
        foreach (var child in leaders) {
          if (!(child is Dictionary<string, object>)) continue;
          long childScore = (long)
                      ((Dictionary<string, object>)child)["score"];
          if (childScore < minScore) {
            minScore = childScore;
            minVal = child;
          }
        }
        if (minScore > score) {
          // The new score is lower than the existing 5 scores, abort.
          return TransactionResult.Abort();
        }

        // Remove the lowest score.
        leaders.Remove(minVal);
      }

      // Add the new high score.
      Dictionary<string, object> newScoreMap =
                       new Dictionary<string, object>();
      newScoreMap["score"] = score;
      newScoreMap["email"] = email;
      leaders.Add(newScoreMap);
      mutableData.Value = leaders;
      return TransactionResult.Success(mutableData);
    });
}

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

Запись данных в автономном режиме

Если клиент потеряет сетевое соединение, ваше приложение продолжит корректно работать.

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

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

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

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