Google is committed to advancing racial equity for Black communities. See how.
Эта страница была переведа с помощью Cloud Translation API.
Switch to English

Перенесите приложение Parse для Android в Firebase

Если вы являетесь пользователем Parse и ищете альтернативное решение Backend as Service, Firebase может стать идеальным выбором для вашего приложения для Android.

В этом руководстве описывается, как интегрировать определенные службы в ваше приложение. Основные инструкции по настройке Firebase см. В руководстве по установке Android .

Гугл Аналитика

Google Analytics - это бесплатное решение для измерения приложений, которое дает представление об использовании приложений и вовлеченности пользователей. Аналитика интегрируется с функциями Firebase и предоставляет вам неограниченные отчеты по 500 различным событиям, которые вы можете определить с помощью Firebase SDK.

См. Документы Google Analytics, чтобы узнать больше.

Предлагаемая миграционная стратегия

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

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

Сравнение кода

Parse Analytics

 // Start collecting data
ParseAnalytics.trackAppOpenedInBackground(getIntent());

Map<String, String> dimensions = new HashMap<String, String>();
// Define ranges to bucket data points into meaningful segments
dimensions.put("priceRange", "1000-1500");
// Did the user filter the query?
dimensions.put("source", "craigslist");
// Do searches happen more often on weekdays or weekends?
dimensions.put("dayType", "weekday");

// Send the dimensions to Parse along with the 'search' event
ParseAnalytics.trackEvent("search", dimensions);
 

Гугл Аналитика

 // Obtain the FirebaseAnalytics instance and start collecting data
mFirebaseAnalytics = FirebaseAnalytics.getInstance(this);

Bundle params = new Bundle();
// Define ranges to bucket data points into meaningful segments
params.putString("priceRange", "1000-1500");
// Did the user filter the query?
params.putString("source", "craigslist");
// Do searches happen more often on weekdays or weekends?
params.putString("dayType", "weekday");

// Send the event
mFirebaseAnalytics.logEvent("search", params);
 

База данных Firebase в реальном времени

База данных Firebase Realtime - это облачная база данных NoSQL. Данные хранятся в формате JSON и синхронизируются в режиме реального времени с каждым подключенным клиентом.

Посмотрите документы базы данных Firebase Realtime, чтобы узнать больше.

Различия с данными разбора

Объекты

В Parse вы храните ParseObject или его подкласс, который содержит пары ключ-значение JSON-совместимых данных. Данные не содержат схем, что означает, что вам не нужно указывать, какие ключи существуют в каждом ParseObject .

Все данные базы данных Firebase Realtime хранятся в виде объектов JSON, и для ParseObject нет эквивалента; вы просто записываете в дерево JSON значения типов, которые соответствуют доступным типам JSON. Вы можете использовать объекты Java для упрощения чтения и записи из базы данных.

Ниже приведен пример того, как вы можете сохранить рекорды для игры.

Анализировать
 @ParseClassName("GameScore")
public class GameScore {
        public GameScore() {}
        public GameScore(Long score, String playerName, Boolean cheatMode) {
            setScore(score);
            setPlayerName(playerName);
            setCheatMode(cheatMode);
        }

        public void setScore(Long score) {
            set("score", score);
        }

        public Long getScore() {
            return getLong("score");
        }

        public void setPlayerName(String playerName) {
            set("playerName", playerName);
        }

        public String getPlayerName() {
            return getString("playerName");
        }

        public void setCheatMode(Boolean cheatMode) {
            return set("cheatMode", cheatMode);
        }

        public Boolean getCheatMode() {
            return getBoolean("cheatMode");
        }
}

// Must call Parse.registerSubclass(GameScore.class) in Application.onCreate
GameScore gameScore = new GameScore(1337, "Sean Plott", false);
gameScore.saveInBackground();
 
Firebase
 // Assuming we defined the GameScore class as:
public class GameScore {
        private Long score;
        private String playerName;
        private Boolean cheatMode;

        public GameScore() {}
        public GameScore(Long score, String playerName, Boolean cheatMode) {
            this.score = score;
            this.playerName = playerName;
            this.cheatMode = cheatMode;
        }

        public Long getScore() {
            return score;
        }

        public String getPlayerName() {
            return playerName;
        }

        public Boolean getCheatMode() {
            return cheatMode;
        }
}

// We would save it to our list of high scores as follows:
DatabaseReference mFirebaseRef = FirebaseDatabase.getInstance().getReference();
GameScore score = new GameScore(1337, "Sean Plott", false);
mFirebaseRef.child("scores").push().setValue(score);
 
Для получения более подробной информации обратитесь к руководству « Чтение и запись данных на Android»

Отношения между данными

ParseObject может иметь связь с другим ParseObject : любой объект может использовать другие объекты в качестве значений.

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

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

Анализировать
 // Create the author
ParseObject myAuthor = new ParseObject("Author");
myAuthor.put("name", "Grace Hopper");
myAuthor.put("birthDate", "December 9, 1906");
myAuthor.put("nickname", "Amazing Grace");

// Create the post
ParseObject myPost = new ParseObject("Post");
myPost.put("title", "Announcing COBOL, a New Programming Language");

// Add a relation between the Post and the Author
myPost.put("parent", myAuthor);

// This will save both myAuthor and myPost
myPost.saveInBackground();
 
Firebase
 DatabaseReference firebaseRef = FirebaseDatabase.getInstance().getReference();
// Create the author
Map<String, String> myAuthor = new HashMap<String, String>();
myAuthor.put("name", "Grace Hopper");
myAuthor.put("birthDate", "December 9, 1906");
myAuthor.put("nickname", "Amazing Grace");

// Save the author
String myAuthorKey = "ghopper";
firebaseRef.child('authors').child(myAuthorKey).setValue(myAuthor);

// Create the post
Map<String, String> post = new HashMap<String, String>();
post.put("author", myAuthorKey);
post.put("title", "Announcing COBOL, a New Programming Language");
firebaseRef.child('posts').push().setValue(post);
 

Следующий макет данных является результатом.

{
  // Info about the authors
  "authors": {
    "ghopper": {
      "name": "Grace Hopper",
      "date_of_birth": "December 9, 1906",
      "nickname": "Amazing Grace"
    },
    ...
  },
  // Info about the posts: the "author" fields contains the key for the author
  "posts": {
    "-JRHTHaIs-jNPLXOQivY": {
      "author": "ghopper",
      "title": "Announcing COBOL, a New Programming Language"
    }
    ...
  }
}
Для получения более подробной информации обратитесь к руководству « Структурируйте свою базу данных» .

Чтение данных

В Parse вы читаете данные, используя либо идентификатор конкретного объекта Parse, либо выполняете запросы, используя ParseQuery .

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

Ниже приведен пример того, как вы можете получить оценки для конкретного игрока на основе примера, представленного в разделе «Объекты» .

Анализировать
 ParseQuery<ParseObject> query = ParseQuery.getQuery("GameScore");
query.whereEqualTo("playerName", "Dan Stemkoski");
query.findInBackground(new FindCallback<ParseObject>() {
    public void done(List<ParseObject> scoreList, ParseException e) {
        if (e == null) {
            for (ParseObject score: scoreList) {
                Log.d("score", "Retrieved: " + Long.toString(score.getLong("score")));
            }
        } else {
            Log.d("score", "Error: " + e.getMessage());
        }
    }
});
 
Firebase
 DatabaseReference mFirebaseRef = FirebaseDatabase.getInstance().getReference();
Query mQueryRef = mFirebaseRef.child("scores").orderByChild("playerName").equalTo("Dan Stemkoski");

// This type of listener is not one time, and you need to cancel it to stop
// receiving updates.
mQueryRef.addChildEventListener(new ChildEventListener() {
    @Override
    public void onChildAdded(DataSnapshot snapshot, String previousChild) {
        // This will fire for each matching child node.
        GameScore score = snapshot.getValue(GameScore.class);
        Log.d("score", "Retrieved: " + Long.toString(score.getScore());
    }
});
 
Для получения дополнительной информации о доступных типах прослушивателей событий и о том, как упорядочивать и фильтровать данные, см. Руководство « Чтение и запись данных на Android» .

Предлагаемая миграционная стратегия

Переосмыслите ваши данные

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

  • Как ваши объекты Parse должны отображаться в данные Firebase
  • Если у вас есть отношения родитель-ребенок, как разделить ваши данные по разным путям, чтобы их можно было эффективно загружать в отдельных вызовах.

Перенос ваших данных

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

Фоновая синхронизация

В этом сценарии у вас есть две версии приложения: старая версия, которая использует Parse, и новая версия, которая использует Firebase. Синхронизация между двумя базами данных обрабатывается Parse Cloud Code (Parse to Firebase), при этом ваш код прослушивает изменения в Firebase и синхронизирует эти изменения с Parse. Прежде чем вы сможете начать использовать новую версию, вы должны:

  • Преобразуйте существующие данные анализа в новую структуру Firebase и запишите их в базу данных Firebase Realtime.
  • Напишите функции Parse Cloud Code, которые используют API-интерфейс Firebase REST для записи в базу данных Firebase Realtime изменений, внесенных в данные анализа старыми клиентами.
  • Напишите и разверните код, который прослушивает изменения в Firebase и синхронизирует их с базой данных Parse.

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

Двойная запись

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

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

Firebase Authentication

Аутентификация Firebase может аутентифицировать пользователей, используя пароли и популярные федеративные провайдеры идентификации, такие как Google, Facebook и Twitter. Он также предоставляет библиотеки пользовательского интерфейса, чтобы сэкономить значительные средства, необходимые для реализации и поддержки полной аутентификации вашего приложения на всех платформах.

Посмотрите документы Аутентификации Firebase, чтобы узнать больше.

Отличия от Parse Auth

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

FirebaseUser имеет фиксированный набор основных свойств - уникальный идентификатор, основной адрес электронной почты, имя и URL-адрес фотографии - хранящиеся в базе данных пользователей отдельного проекта; эти свойства могут быть обновлены пользователем. Вы не можете добавлять другие свойства к объекту FirebaseUser напрямую; вместо этого вы можете сохранить дополнительные свойства в вашей базе данных Firebase Realtime.

Ниже приведен пример того, как вы можете зарегистрировать пользователя и добавить дополнительное поле номера телефона.

Анализировать
 ParseUser user = new ParseUser();
user.setUsername("my name");
user.setPassword("my pass");
user.setEmail("email@example.com");

// other fields can be set just like with ParseObject
user.put("phone", "650-253-0000");

user.signUpInBackground(new SignUpCallback() {
    public void done(ParseException e) {
        if (e == null) {
            // Hooray! Let them use the app now.
        } else {
            // Sign up didn't succeed. Look at the ParseException
            // to figure out what went wrong
        }
    }
});
 
Firebase
 FirebaseAuth mAuth = FirebaseAuth.getInstance();

mAuth.createUserWithEmailAndPassword("email@example.com", "my pass")
    .continueWithTask(new Continuation<AuthResult, Task<Void>> {
        @Override
        public Task<Void> then(Task<AuthResult> task) {
            if (task.isSuccessful()) {
                FirebaseUser user = task.getResult().getUser();
                DatabaseReference firebaseRef = FirebaseDatabase.getInstance().getReference();
                return firebaseRef.child("users").child(user.getUid()).child("phone").setValue("650-253-0000");
            } else {
                // User creation didn't succeed. Look at the task exception
                // to figure out what went wrong
                Log.w(TAG, "signInWithEmail", task.getException());
            }
        }
    });
 

Предлагаемая миграционная стратегия

Перенос аккаунтов

Чтобы перенести учетные записи пользователей из Parse в Firebase, экспортируйте свою пользовательскую базу данных в файл JSON или CSV, затем импортируйте файл в свой проект Firebase с помощью командной строки Firebase auth:import .

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

{ // Username/password user
  "bcryptPassword": "$2a$10$OBp2hxB7TaYZgKyTiY48luawlTuYAU6BqzxJfpHoJMdZmjaF4HFh6",
  "email": "user@example.com",
  "username": "testuser",
  "objectId": "abcde1234",
  ...
},
{ // Facebook user
  "authData": {
    "facebook": {
      "access_token": "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
      "expiration_date": "2017-01-02T03:04:05.006Z",
      "id": "1000000000"
    }
  },
  "username": "wXyZ987654321StUv",
  "objectId": "fghij5678",
  ...
}

Затем преобразуйте экспортированный файл в формат, требуемый интерфейсом командной строки Firebase. Используйте objectId ваших пользователей Parse как localId ваших пользователей Firebase. Кроме того, base64 кодирует значения bcryptPassword из Parse и использует их в поле passwordHash . Например:

{
  "users": [
    {
      "localId": "abcde1234",  // Parse objectId
      "email": "user@example.com",
      "displayName": "testuser",
      "passwordHash": "JDJhJDEwJE9CcDJoeEI3VGFZWmdLeVRpWTQ4bHVhd2xUdVlBVTZCcXp4SmZwSG9KTWRabWphRjRIRmg2",
    },
    {
      "localId": "fghij5678",  // Parse objectId
      "displayName": "wXyZ987654321StUv",
      "providerUserInfo": [
        {
          "providerId": "facebook.com",
          "rawId": "1000000000",  // Facebook ID
        }
      ]
    }
  ]
}

Наконец, импортируйте преобразованный файл с помощью Firebase CLI, указав bcrypt в качестве алгоритма хеширования:

firebase auth:import account_file.json --hash-algo=BCRYPT

Перенос данных пользователя

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

Firebase Cloud Messaging

Firebase Cloud Messaging (FCM) - это кроссплатформенное решение для обмена сообщениями, которое позволяет надежно доставлять сообщения и уведомления бесплатно. Компоновщик уведомлений - это бесплатный сервис, основанный на Firebase Cloud Messaging, который позволяет целевым уведомлениям пользователей для разработчиков мобильных приложений.

Посмотрите документы Firebase Cloud Messaging, чтобы узнать больше.

Различия с парсингом push-уведомлений

Каждое приложение Parse, установленное на устройстве, зарегистрированном для уведомлений, имеет связанный объект Installation , в котором хранятся все данные, необходимые для целевых уведомлений. Installation является подклассом ParseUser , что означает, что вы можете добавить любые дополнительные данные, которые вы хотите, к своим экземплярам Installation .

Компоновщик уведомлений предоставляет предопределенные пользовательские сегменты на основе такой информации, как приложение, версия приложения и язык устройства. Вы можете создавать более сложные пользовательские сегменты, используя события и свойства Google Analytics для создания аудитории. См. Руководство помощи аудитории, чтобы узнать больше. Эта информация о таргетинге не отображается в базе данных Firebase Realtime.

Предлагаемая миграционная стратегия

Токены устройства миграции

На момент написания, Parse Android SDK использует более старую версию маркеров регистрации FCM, несовместимую с функциями, предлагаемыми композитором уведомлений.

Вы можете получить новый токен, добавив FCM SDK в свое приложение; однако это может сделать недействительным токен, используемый Parse SDK для получения уведомлений. Если вы хотите избежать этого, вы можете настроить Parse SDK на использование идентификатора отправителя Parse и идентификатора отправителя. Таким образом, вы не аннулируете токен, используемый Parse SDK, но помните, что этот обходной путь перестанет работать, когда Parse закроет свой проект.

Перенос каналов на темы FCM

Если вы используете каналы Parse для отправки уведомлений, вы можете перейти к темам FCM, которые предоставляют ту же модель издатель-подписчик. Чтобы обработать переход от Parse к FCM, вы можете написать новую версию приложения, которое использует Parse SDK для отмены подписки на каналы Parse и FCM SDK для подписки на соответствующие темы FCM. В этой версии приложения вы должны отключить получение уведомлений в Parse SDK, удалив из манифеста вашего приложения следующее:

 <service android:name="com.parse.PushService" />
<receiver android:name="com.parse.ParsePushBroadcastReceiver"
  android:exported="false">
<intent-filter>
<action android:name="com.parse.push.intent.RECEIVE" />
<action android:name="com.parse.push.intent.DELETE" />
<action android:name="com.parse.push.intent.OPEN" />
</intent-filter>
</receiver>
<receiver android:name="com.parse.GcmBroadcastReceiver"
  android:permission="com.google.android.c2dm.permission.SEND">
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />

<!--
IMPORTANT: Change "com.parse.starter" to match your app's package name.
-->
<category android:name="com.parse.starter" />
</intent-filter>
</receiver>

<!--
IMPORTANT: Change "YOUR_SENDER_ID" to your GCM Sender Id.
-->
<meta-data android:name="com.parse.push.gcm_sender_id"
  android:value="id:YOUR_SENDER_ID" />;
 

Например, если ваш пользователь подписан на тему «Гиганты», вы должны сделать что-то вроде:

 ParsePush.unsubscribeInBackground("Giants", new SaveCallback() {
    @Override
    public void done(ParseException e) {
        if (e == null) {
            FirebaseMessaging.getInstance().subscribeToTopic("Giants");
        } else {
            // Something went wrong unsubscribing
        }
    }
});
 

Используя эту стратегию, вы можете отправлять сообщения как на канал Parse, так и в соответствующую тему FCM, поддерживая пользователей как старых, так и новых версий. Когда достаточное количество пользователей мигрировало из версии приложения только для Parse, вы можете закрыть эту версию и начать отправку только с использованием FCM.

Посмотрите документы по темам FCM, чтобы узнать больше.

Firebase Remote Config

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

Firebase Remote Config может быть очень полезен во время ваших миграций в тех случаях, когда вы хотите протестировать различные решения и иметь возможность динамически переключать большее количество клиентов к другому поставщику. Например, если у вас есть версия вашего приложения, в которой для данных используются Firebase и Parse, вы можете использовать правило случайного процентиля, чтобы определить, какие клиенты читают из Firebase, и постепенно увеличивать процент.

Чтобы узнать больше о Firebase Remote Config, смотрите введение Remote Config .

Отличия от Parse Config

С помощью Parse config вы можете добавить пары ключ / значение в свое приложение на панели мониторинга Parse Config, а затем получить ParseConfig на клиенте. Каждый экземпляр ParseConfig который вы получаете, всегда неизменен. Когда вы в будущем ParseConfig новый ParseConfig из сети, он не изменит какой-либо существующий экземпляр ParseConfig , но вместо этого создаст новый и сделает его доступным через getCurrentConfig() .

С помощью Firebase Remote Config вы создаете в приложении значения по умолчанию для пар ключ / значение, которые вы можете переопределить из консоли Firebase, и вы можете использовать правила и условия, чтобы обеспечить различные варианты взаимодействия вашего приложения с различными сегментами вашей пользовательской базы. Firebase Remote Config реализует одноэлементный класс, который делает пары ключ / значение доступными для вашего приложения. Первоначально синглтон возвращает значения по умолчанию, которые вы определяете в приложении. Вы можете получить новый набор значений с сервера в любой момент, удобный для вашего приложения; после успешного получения нового набора вы можете выбрать, когда его активировать, чтобы новые значения стали доступны приложению.

Предлагаемая миграционная стратегия

Вы можете перейти к Firebase Remote Config, скопировав пары ключ / значение вашей конфигурации Parse в консоль Firebase, а затем развернув новую версию приложения, использующего Firebase Remote Config.

Если вы хотите поэкспериментировать с Parse Config и Firebase Remote Config, вы можете развернуть новую версию приложения, в которой используются оба SDK, до тех пор, пока достаточное количество пользователей не перейдут с версии Parse only.

Сравнение кода

Анализировать

 ParseConfig.getInBackground(new ConfigCallback() {
    @Override
    public void done(ParseConfig config, ParseException e) {
        if (e == null) {
            Log.d("TAG", "Yay! Config was fetched from the server.");
        } else {
            Log.e("TAG", "Failed to fetch. Using Cached Config.");
            config = ParseConfig.getCurrentConfig();
        }

        // Get the message from config or fallback to default value
        String welcomeMessage = config.getString("welcomeMessage", "Welcome!");
    }
});
 

Firebase

 mFirebaseRemoteConfig = FirebaseRemoteConfig.getInstance();
// Set defaults from an XML resource file stored in res/xml
mFirebaseRemoteConfig.setDefaults(R.xml.remote_config_defaults);

mFirebaseRemoteConfig.fetch()
    .addOnSuccessListener(new OnSuccessListener<Void>() {
        @Override
        public void onSuccess(Void aVoid) {
            Log.d("TAG", "Yay! Config was fetched from the server.");
            // Once the config is successfully fetched it must be activated before newly fetched
            // values are returned.
            mFirebaseRemoteConfig.activateFetched();
        }
    })
    .addOnFailureListener(new OnFailureListener() {
        @Override
        public void onFailure(@NonNull Exception exception) {
            Log.e("TAG", "Failed to fetch. Using last fetched or default.");
        }
    })

// ...

// When this is called, the value of the latest fetched and activated config is returned;
// if there's none, the default value is returned.
String welcomeMessage = mFirebaseRemoteConfig.getString("welcomeMessage");