Migrer votre application Parse pour Android vers Firebase

Si vous êtes un utilisateur Parse à la recherche d'un autre backend en tant qu'utilisateur solution de service, Firebase peut être la solution idéale pour votre application Android.

Ce guide explique comment intégrer des services spécifiques dans votre application. Pour instructions de configuration de base de Firebase, reportez-vous à la page de configuration d'Android. .

Google Analytics

Google Analytics est une solution sans frais de mesure des applications, qui fournit des insights sur l'utilisation des applications et l'engagement utilisateur. Analytics s'intègre aux fonctionnalités Firebase et vous fournit des rapports illimités sur jusqu'à 500 événements distincts que vous pouvez définir à l'aide du SDK Firebase.

Pour en savoir plus, consultez la documentation sur Google Analytics.

Stratégie de migration suggérée

L'utilisation de différents fournisseurs de solutions d'analyse est un scénario courant qui s'applique facilement Google Analytics Il vous suffit de l'ajouter à votre application pour bénéficier d'événements et de propriétés utilisateur Analytics collecte automatiquement des données comme la première ouverture, les mises à jour d'applis, le modèle de l'appareil et l'âge.

Pour les événements personnalisés et les propriétés utilisateur, vous pouvez appliquer une stratégie de double écriture à l'aide de analyser et Google Analytics pour consigner les événements et les propriétés, ce qui vous permet de : pour déployer progressivement la nouvelle solution.

Comparaison de code

Analyse des données analytiques

// 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);

Google Analytics

// 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 Realtime Database

Le Firebase Realtime Database est une base de données NoSQL hébergée dans le cloud. Les données sont stockées au format JSON synchronisées en temps réel à chaque client connecté.

Pour en savoir plus, consultez la documentation sur Firebase Realtime Database.

Différences avec les données analysées

Objets

Dans Parse, vous stockez un ParseObject, ou une sous-classe de celui-ci, qui contient des paires clé/valeur. de données compatibles JSON. Les données sont dépourvues de schéma, ce qui signifie que vous n'avez pas besoin de spécifier les clés existe sur chaque ParseObject.

Toutes les données Firebase Realtime Database sont stockées en tant qu'objets JSON, et il n'y a pas d'équivalent pour ParseObject; il suffit d'écrire dans les valeurs de l'arborescence JSON des types qui correspondent aux types JSON disponibles. Vous pouvez utiliser des objets Java pour simplifier la lecture et l'écriture depuis le base de données.

Voici un exemple de la façon dont vous pouvez enregistrer les meilleurs scores d'un match.

Analyser
@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);
Pour en savoir plus, consultez les guide Lire et écrire des données sur Android.

Relations entre les données

Un ParseObject peut avoir une relation avec un autre ParseObject : n'importe quel objet peut utiliser d'autres objets comme valeurs.

Dans le Firebase Realtime Database, les relations sont mieux exprimées à l'aide de structures de données plates qui Diviser les données en chemins d'accès distincts, de sorte qu'elles puissent être efficacement téléchargées dans des appels distincts

L'exemple suivant montre comment vous pouvez structurer la relation entre les messages d'une et leurs auteurs.

Analyser
// 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);

La mise en page des données suivante est obtenue.

{
  // 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"
    }
    ...
  }
}
Pour en savoir plus, consultez les Structurer votre base de données .

Lire les données

Dans Parse, vous lisez des données en utilisant l'ID d'un objet Parse spécifique, ou en exécutant des requêtes à l'aide de ParseQuery.

Dans Firebase, vous récupérez des données en joignant un écouteur asynchrone à une référence de base de données. La est déclenché une fois pour l'état initial des données, puis une autre fois lorsque celles-ci changent. vous n'aurez donc pas besoin d'ajouter de code pour déterminer si les données ont changé.

Voici un exemple de la façon dont vous pouvez récupérer les scores d'un joueur particulier, en fonction de l'exemple présenté dans la section Objets.

Analyser
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());
    }
});
Pour en savoir plus sur les types d'écouteurs d'événements disponibles et sur la façon d'organiser et de filtrer les données, consultez Lire et écrire des données sur Android .

Stratégie de migration suggérée

Redécouvrir vos données

Le Firebase Realtime Database est optimisé pour synchroniser les données en quelques millisecondes sur toutes les et que la structure des données qui en résulte est différente de celle de l'analyse des données principales. Cela signifie que La première étape de votre migration consiste à réfléchir aux modifications requises par vos données, y compris:

  • Correspondance entre les objets Parse et les données Firebase
  • Si vous avez des relations parent-enfant, comment répartir vos données sur différents chemins vous pouvez le télécharger dans des appels distincts.

Migration de vos données

Une fois que vous avez décidé de la structure de vos données dans Firebase, vous devez planifier la gestion de la période pendant laquelle votre application doit écrire dans les deux bases de données. Vous disposez des options suivantes :

Synchronisation en arrière-plan

Dans ce scénario, vous disposez de deux versions de l'application: l'ancienne qui utilise Parse et la nouvelle utilisant Firebase. Les synchronisations entre les deux bases de données sont gérées par le code cloud Parse (Parse vers Firebase). Votre code écoute les modifications sur Firebase et les synchronise avec Parse. Avant de pouvoir utiliser la nouvelle version, vous devez:

  • Convertissez vos données Parse existantes en nouvelle structure Firebase, puis écrivez-les dans Firebase Realtime Database.
  • Écrire des fonctions Cloud Code d'analyse qui utilisent l'API REST Firebase pour écrire dans le Modifications apportées à Firebase Realtime Database dans "Parse Data" (Analyser les données) par d'anciens clients.
  • Rédiger et déployer du code qui écoute les modifications effectuées sur Firebase et les synchronise avec l'API Parse base de données.

Ce scénario garantit une séparation claire entre l'ancien et le nouveau code, et simplifie la tâche des clients. La les défis de ce scénario sont la gestion de grands ensembles de données lors de l'exportation initiale et la garantie que la synchronisation bidirectionnelle ne génère pas de récursion infinie.

Double écriture

Dans ce scénario, vous écrivez une nouvelle version de l'application qui utilise à la fois Firebase et Parse, en utilisant le code Cloud Parse pour synchroniser les modifications apportées par les anciens clients à partir des données Parse avec le Firebase Realtime Database. Lorsqu'un nombre suffisant d'utilisateurs est passé de la version "Analyse uniquement" de l'application, peut supprimer le code d'analyse de la version en double écriture.

Ce scénario ne nécessite aucun code côté serveur. L'inconvénient est que les données n'est pas migrée et que la taille de votre application est augmentée par l'utilisation des deux SDK.

Firebase Authentication

Firebase Authentication peut authentifier les utilisateurs à l'aide de mots de passe et de fournisseurs d'identité fédérés populaires tels que Google, Facebook et Twitter. Il fournit également des bibliothèques d'UI pour vous éviter nécessaire pour implémenter et gérer une expérience d'authentification complète pour votre application sur toutes les plates-formes.

Pour en savoir plus, consultez la documentation sur Firebase Authentication.

Différences avec l'authentification Parse

Parse fournit une classe d'utilisateur spécialisée appelée ParseUser qui gère automatiquement les fonctionnalités requises pour la gestion des comptes utilisateur. ParseUser est une sous-classe de ParseObject, ce qui signifie que les données utilisateur sont disponibles dans les données d'analyse et peuvent être étendues avec des champs supplémentaires comme n'importe quel autre ParseObject.

Un FirebaseUser possède un ensemble fixe de propriétés de base : un identifiant unique, une adresse e-mail principale, un nom et une URL de photo, stockés dans la base de données utilisateur d'un projet distinct ; ces propriétés peuvent être mises à jour l'utilisateur. Vous ne pouvez pas ajouter directement d'autres propriétés à l'objet FirebaseUser. À la place, vous pouvez stocker les propriétés supplémentaires dans Firebase Realtime Database.

L'exemple suivant montre comment vous pouvez inscrire un utilisateur et ajouter un champ de numéro de téléphone.

Analyser
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());
            }
        }
    });

Stratégie de migration suggérée

Migrer les comptes

Pour migrer des comptes utilisateur de Parse vers Firebase, exportez votre base de données utilisateur vers un fichier JSON ou CSV, puis importez-le dans votre projet Firebase à l'aide de la méthode auth:import de la CLI Firebase .

Commencez par exporter votre base de données d'utilisateurs depuis la console Parse ou votre système auto-hébergé base de données. Par exemple, un fichier JSON exporté depuis la console Parse peut ressembler à ceci :

{ // 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",
  ...
}

Transformez ensuite le fichier exporté au format requis par la CLI Firebase. Utilisez le objectId de vos utilisateurs Parse en tant que localId de vos utilisateurs Firebase. Vous pouvez aussi encoder en base64 Valeurs bcryptPassword issues de Parse et utilisez-les dans passwordHash . Exemple :

{
  "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
        }
      ]
    }
  ]
}

Enfin, importez le fichier transformé avec la CLI Firebase, en spécifiant bcrypt. que l'algorithme de hachage:

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

Migrer les données utilisateur

Si vous stockez des données supplémentaires pour vos utilisateurs, vous pouvez les migrer vers Firebase Realtime Database à l'aide des stratégies décrites dans la section Migration de données. Si vous migrez en suivant la procédure décrite dans la section Migration des comptes, Les comptes Firebase ont les mêmes identifiants que vos comptes Parse, ce qui vous permet de migrer et de reproduire facilement toute relation associée à l'identifiant de l'utilisateur.

Firebase Cloud Messaging

Firebase Cloud Messaging (FCM) est une solution de messagerie multiplate-forme qui vous permet envoyer des messages et des notifications sans frais. Le compositeur de notifications est un service sans frais basé sur Firebase Cloud Messaging qui permet aux développeurs d'applications mobiles d'envoyer des notifications ciblées aux utilisateurs.

Pour en savoir plus, consultez la documentation sur Firebase Cloud Messaging .

Différences avec l'analyse des notifications push

Chaque application Parse installée sur un appareil enregistré pour les notifications dispose d'un Installation, dans lequel vous stockez toutes les données nécessaires pour cibler les notifications. Installation est une sous-classe de ParseUser, ce qui signifie que vous pouvez ajouter toutes les données supplémentaires souhaitées sur vos instances Installation.

L'outil de création de notifications fournit des segments d'utilisateurs prédéfinis en fonction d'informations telles que l'application, la version de l'application et l'appareil. langue. Vous pouvez créer des segments d'utilisateurs plus complexes à l'aide d'événements et de propriétés Google Analytics pour créer des audiences. Voir les audiences pour en savoir plus. Ces informations de ciblage ne sont pas visibles dans le Firebase Realtime Database.

Stratégie de migration suggérée

Migrer des jetons d'appareil

Au moment de la rédaction de ce document, le SDK Parse Android utilise une ancienne version de FCM. jetons d'enregistrement, et non compatibles avec les fonctionnalités proposées par l'outil de création de notifications.

Vous pouvez obtenir un nouveau jeton en ajoutant le SDK FCM à votre application. Toutefois, cela risque d'invalider le jeton utilisé par le SDK Parse pour recevoir des notifications. Pour éviter cela, vous pouvez configurer le SDK Parse afin qu'il utilise à la fois l'ID d'expéditeur de Parse et votre ID d'expéditeur. De cette façon, vous n'invalidez pas le jeton utilisé par le SDK Parse, Sachez toutefois que cette solution de contournement cessera de fonctionner lorsque Parse arrêtera son projet.

Migration des chaînes vers FCM sujets

Si vous utilisez des canaux Parse pour envoyer des notifications, vous pouvez migrer vers des sujets FCM, qui fournissent le même modèle éditeur-abonné. Pour gérer la transition de Parse vers FCM, vous pouvez écrire une nouvelle version de l'application qui utilise le SDK Parse pour se désabonner des chaînes Parse et le SDK FCM pour s'abonner à les sujets FCM correspondants. Dans cette version de l'application, vous devriez désactiver la réception de notifications sur le SDK Parse, en supprimant les éléments suivants du fichier manifeste de votre application:

<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" />;

Par exemple, si votre utilisateur est abonné au groupe "Giants" vous ferez quelque chose comme:

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

Cette stratégie vous permet d'envoyer des messages à la fois au canal Parse et au canal FCM, pour aider les utilisateurs de l'ancienne et de la nouvelle version. Lorsque suffisamment d'utilisateurs ont migré depuis Vous pouvez arrêter la version de l'application analysée uniquement et commencer à l'envoyer à l'aide de FCM uniquement.

Consultez le Documentation sur FCM sujets pour en savoir plus.

Firebase Remote Config

Firebase Remote Config est un service cloud qui vous permet de modifier le comportement et l'apparence de votre application sans que les utilisateurs aient à télécharger une mise à jour. Lorsque vous utilisez Remote Config, vous créez des valeurs par défaut dans l'application qui contrôlent son comportement et son apparence. Vous pouvez ensuite utiliser la console Firebase pour remplacer ces valeurs par défaut pour tous les utilisateurs de l'application ou pour certains segments de la base d'utilisateurs.

Firebase Remote Config peut être très utile lors de la migration si vous souhaitez tester différentes solutions et pouvoir transférer dynamiquement plus de clients vers un autre fournisseur. Par exemple, Si une version de votre application utilise à la fois Firebase et Parse pour les données, vous pouvez utiliser un de centile aléatoire afin de déterminer quels clients lisent des données depuis Firebase, puis augmentez progressivement le pourcentage.

Pour en savoir plus sur Firebase Remote Config, consultez les Remote Config

Différences avec la configuration Parse

Avec la configuration Parse, vous pouvez ajouter des paires clé/valeur à votre application dans le tableau de bord de configuration de l'analyse, puis récupérer le ParseConfig sur le client. Chaque instance ParseConfig que vous "get" est toujours immuable. Lorsque vous récupérez un nouveau ParseConfig à partir du réseau, il ne modifie aucune instance ParseConfig existante, mais créez-en une et rendez-la disponible via getCurrentConfig().

Avec Firebase Remote Config, vous créez des valeurs par défaut dans l'application pour les paires clé-valeur que vous pouvez remplacer depuis la console Firebase. Vous pouvez également utiliser des règles et des conditions pour proposer des variations de l'expérience utilisateur de votre application à différents segments de votre base d'utilisateurs. Firebase Remote Config implémente un singleton qui rend les paires clé/valeur disponibles pour votre application. Initialement, le singleton renvoie les valeurs par défaut que vous définissez dans l'application. Vous pouvez récupérer un nouvel ensemble de valeurs sur le serveur à tout le moment opportun pour votre application ; une fois le nouvel ensemble récupéré, vous pouvez choisir quand l'activer pour que les nouvelles valeurs soient disponibles pour l'application.

Stratégie de migration suggérée

Vous pouvez passer à Firebase Remote Config en copiant les paires clé/valeur de votre configuration Parse à la console Firebase, puis déployer une nouvelle version de l'application qui utilise Firebase Remote Config.

Si vous souhaitez tester à la fois Parse Config et Firebase Remote Config, vous pouvez déployer Une nouvelle version de l'application qui utilise les deux SDK jusqu'à ce qu'un nombre suffisant d'utilisateurs aient migré depuis la version "Analyse uniquement".

Comparaison de code

Analyser

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");