Google is committed to advancing racial equity for Black communities. See how.
Questa pagina è stata tradotta dall'API Cloud Translation.
Switch to English

Migra la tua app Android Parse su Firebase

Se sei un utente Parse alla ricerca di una soluzione alternativa di back-end come servizio, Firebase potrebbe essere la scelta ideale per la tua app Android.

Questa guida descrive come integrare servizi specifici nella tua app. Per le istruzioni di base sulla configurazione di Firebase, consultare la Guida alla configurazione di Android .

statistiche di Google

Google Analytics è una soluzione di misurazione delle app gratuita che fornisce informazioni sull'utilizzo delle app e il coinvolgimento degli utenti. Analytics si integra tra le funzionalità di Firebase e offre rapporti illimitati per un massimo di 500 eventi distinti che è possibile definire utilizzando Firebase SDK.

Consulta i documenti di Google Analytics per saperne di più.

Strategia di migrazione suggerita

L'uso di diversi fornitori di analisi è uno scenario comune che si applica facilmente a Google Analytics. Aggiungilo alla tua app per beneficiare degli eventi e delle proprietà dell'utente che Analytics raccoglie automaticamente, come prima apertura, aggiornamento dell'app, modello del dispositivo, età.

Per eventi personalizzati e proprietà dell'utente, è possibile utilizzare una doppia strategia di scrittura utilizzando sia Parse Analytics che Google Analytics per registrare eventi e proprietà, che consente di implementare gradualmente la nuova soluzione.

Confronto del codice

Analisi di analisi

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

statistiche di Google

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

Firebase Realtime Database è un database ospitato su cloud NoSQL. I dati vengono archiviati come JSON e sincronizzati in tempo reale su ogni client connesso.

Consulta i documenti del database in tempo reale di Firebase per ulteriori informazioni.

Differenze con dati di analisi

Oggetti

In Parse viene archiviato un ParseObject o una sua sottoclasse che contiene coppie chiave-valore di dati compatibili con JSON. I dati sono schematici, il che significa che non è necessario specificare quali chiavi esistono su ciascun ParseObject .

Tutti i dati del database in tempo reale di Firebase sono archiviati come oggetti JSON e non esiste un equivalente per ParseObject ; scrivi semplicemente all'albero JSON valori di tipi che corrispondono ai tipi JSON disponibili. È possibile utilizzare oggetti Java per semplificare la lettura e la scrittura dal database.

Di seguito è riportato un esempio di come è possibile salvare i punteggi più alti per una partita.

Parse
 @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);
 
Per maggiori dettagli, consulta la guida di lettura e scrittura dei dati su Android .

Relazioni tra i dati

Un ParseObject può avere una relazione con un altro ParseObject : qualsiasi oggetto può usare altri oggetti come valori.

Nel database in tempo reale di Firebase, le relazioni sono meglio espresse utilizzando strutture di dati semplici che dividono i dati in percorsi separati, in modo che possano essere scaricati in modo efficiente in chiamate separate.

Di seguito è riportato un esempio di come è possibile strutturare la relazione tra i post in un'app di blog e i loro autori.

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

Il seguente layout di dati è il risultato.

{
  // 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"
    }
    ...
  }
}
Per ulteriori dettagli, consultare la guida Struttura del database .

Lettura dei dati

In Parse leggi i dati utilizzando l'ID di un oggetto Parse specifico o eseguendo query utilizzando ParseQuery .

In Firebase, i dati vengono recuperati collegando un listener asincrono a un riferimento al database. Il listener viene attivato una volta per lo stato iniziale dei dati e di nuovo quando i dati cambiano, quindi non sarà necessario aggiungere alcun codice per determinare se i dati sono cambiati.

Di seguito è riportato un esempio di come recuperare i punteggi per un determinato giocatore, in base all'esempio presentato nella sezione "Oggetti" .

Parse
 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());
    }
});
 
Per maggiori dettagli sui tipi disponibili di listener di eventi e su come ordinare e filtrare i dati, consultare la guida di lettura e scrittura dei dati su Android .

Strategia di migrazione suggerita

Ripensare i tuoi dati

Firebase Realtime Database è ottimizzato per sincronizzare i dati in millisecondi su tutti i client connessi e la struttura dei dati risultante è diversa dai dati core Parse. Ciò significa che il primo passo della migrazione è considerare le modifiche richieste dai dati, tra cui:

  • Come devono essere mappati i tuoi oggetti Parse sui dati di Firebase
  • Se hai relazioni genitore-figlio, come suddividere i tuoi dati su percorsi diversi in modo che possano essere scaricati in modo efficiente in chiamate separate.

Migra i tuoi dati

Dopo aver deciso come strutturare i dati in Firebase, è necessario pianificare come gestire il periodo durante il quale l'app deve scrivere in entrambi i database. Le tue scelte sono:

Sincronizzazione in background

In questo scenario, hai due versioni dell'app: la vecchia versione che utilizza Parse e una nuova versione che utilizza Firebase. Le sincronizzazioni tra i due database sono gestite da Parse Cloud Code (da Parse a Firebase), con il codice che ascolta le modifiche su Firebase e sincronizza tali modifiche con Parse. Prima di poter iniziare a utilizzare la nuova versione, è necessario:

  • Converti i tuoi dati di analisi esistenti nella nuova struttura di Firebase e scrivili nel database in tempo reale di Firebase.
  • Scrivere le funzioni del codice cloud di analisi che utilizzano l'API REST di Firebase per scrivere nel database in tempo reale di Firebase le modifiche apportate nei dati di analisi dai vecchi client.
  • Scrivi e distribuisci il codice che ascolta le modifiche su Firebase e le sincronizza con il database Parse.

Questo scenario garantisce una netta separazione tra vecchio e nuovo codice e semplifica i client. Le sfide di questo scenario sono la gestione di grandi set di dati nell'esportazione iniziale e la garanzia che la sincronizzazione bidirezionale non generi ricorsione infinita.

Doppia scrittura

In questo scenario, scrivi una nuova versione dell'app che utilizza sia Firebase che Parse, utilizzando Parse Cloud Code per sincronizzare le modifiche apportate dai vecchi client dai dati di analisi al database in tempo reale di Firebase. Quando un numero sufficiente di persone è migrato dalla versione di solo analisi dell'app, è possibile rimuovere il codice di analisi dalla versione doppia scrittura.

Questo scenario non richiede alcun codice lato server. Gli svantaggi sono che i dati a cui non si accede non vengono migrati e che la dimensione della tua app viene aumentata dall'uso di entrambi gli SDK.

Autenticazione Firebase

Firebase Authentication può autenticare gli utenti utilizzando password e provider di identità federati popolari come Google, Facebook e Twitter. Fornisce inoltre librerie dell'interfaccia utente per risparmiare gli investimenti significativi richiesti per implementare e mantenere un'esperienza di autenticazione completa per la tua app su tutte le piattaforme.

Consulta i documenti di autenticazione Firebase per ulteriori informazioni.

Differenze con Parse Auth

Parse fornisce una classe utente specializzata chiamata ParseUser che gestisce automaticamente la funzionalità richiesta per la gestione dell'account utente. ParseUser è una sottoclasse di ParseObject , il che significa che i dati utente sono disponibili in Parse Data e possono essere estesi con campi aggiuntivi come qualsiasi altro ParseObject .

Un FirebaseUser ha una serie fissa di proprietà di base - un ID univoco, un indirizzo e-mail primario, un nome e un URL di foto - memorizzati in un database utente separato di un progetto; tali proprietà possono essere aggiornate dall'utente. Non è possibile aggiungere direttamente altre proprietà all'oggetto FirebaseUser ; invece, è possibile memorizzare le proprietà aggiuntive nel database in tempo reale di Firebase.

Di seguito è riportato un esempio di come è possibile iscriversi a un utente e aggiungere un campo di numero di telefono aggiuntivo.

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

Strategia di migrazione suggerita

Migrare gli account

Per migrare gli account utente da Parse a Firebase, esportare il database utente in un file JSON o CSV, quindi importare il file nel progetto Firebase utilizzando il comando auth:import della CLI di Firebase.

Innanzitutto, esporta il tuo database utente dalla console Parse o dal database self-hosted. Ad esempio, un file JSON esportato dalla console di Parse potrebbe essere simile al seguente:

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

Quindi, trasforma il file esportato nel formato richiesto dalla CLI di Firebase. Usa l' objectId dei tuoi utenti Parse come localId dei tuoi utenti Firebase. Inoltre, base64 codifica i valori di bcryptPassword da Parse e li utilizza nel campo passwordHash . Per esempio:

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

Infine, importa il file trasformato con l'interfaccia della riga di comando di Firebase, specificando bcrypt come algoritmo hash:

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

Migrare i dati utente

Se si memorizzano dati aggiuntivi per i propri utenti, è possibile migrarli nel database in tempo reale di Firebase utilizzando le strategie descritte nella sezione sulla migrazione dei dati . Se esegui la migrazione degli account utilizzando il flusso descritto nella sezione Migrazione degli account , i tuoi account Firebase hanno gli stessi ID dei tuoi account Parse, consentendoti di migrare e riprodurre facilmente qualsiasi relazione codificata dall'ID utente.

Firebase Cloud Messaging

Firebase Cloud Messaging (FCM) è una soluzione di messaggistica multipiattaforma che consente di recapitare in modo affidabile messaggi e notifiche gratuitamente. Il compositore di Notifiche è un servizio gratuito basato su Firebase Cloud Messaging che consente notifiche utente mirate per gli sviluppatori di app mobili.

Consulta i documenti di Firebase Cloud Messaging per saperne di più.

Differenze con le notifiche push di analisi

Ogni applicazione Parse installata su un dispositivo registrato per le notifiche ha un oggetto Installation associato, in cui vengono archiviati tutti i dati necessari per indirizzare le notifiche. Installation è una sottoclasse di ParseUser , il che significa che è possibile aggiungere eventuali dati aggiuntivi alle istanze di Installation .

Il compositore di Notifiche fornisce segmenti di utenti predefiniti in base a informazioni come app, versione dell'app e lingua del dispositivo. Puoi creare segmenti di utenti più complessi utilizzando eventi e proprietà di Google Analytics per creare segmenti di pubblico. Consulta la guida di aiuto del pubblico per saperne di più. Queste informazioni di targeting non sono visibili nel database in tempo reale di Firebase.

Strategia di migrazione suggerita

Migrazione di token dispositivo

Al momento in cui scrivo, l'analizzatore di Android Parse utilizza una versione precedente dei token di registrazione FCM, non compatibile con le funzionalità offerte dal compositore di Notifiche.

Puoi ottenere un nuovo token aggiungendo l'SDK FCM alla tua app; tuttavia, ciò potrebbe invalidare il token utilizzato da Parse SDK per ricevere le notifiche. Se si desidera evitarlo, è possibile configurare l'analizzatore di analisi dei dati di base per utilizzare sia l'ID mittente di Parse sia l'ID mittente. In questo modo non invalidi il token utilizzato da Parse SDK, ma tieni presente che questa soluzione alternativa smetterà di funzionare quando Parse arresta il suo progetto.

Migrazione dei canali agli argomenti FCM

Se si utilizzano i canali Parse per inviare notifiche, è possibile migrare agli argomenti FCM, che forniscono lo stesso modello editore-abbonato. Per gestire la transizione da Parse a FCM, è possibile scrivere una nuova versione dell'app che utilizza Parse SDK per annullare l'iscrizione ai canali Parse e FCM SDK per iscriversi agli argomenti FCM corrispondenti. In questa versione dell'app è necessario disabilitare la ricezione di notifiche su Parse SDK, rimuovendo quanto segue dal manifest dell'app:

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

Ad esempio, se il tuo utente è iscritto all'argomento "Giganti", faresti qualcosa del tipo:

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

Utilizzando questa strategia, è possibile inviare messaggi sia al canale Parse sia all'argomento FCM corrispondente, supportando gli utenti di versioni sia vecchie che nuove. Quando un numero sufficiente di utenti è migrato dalla versione solo analisi dell'app, è possibile annullare il tramonto di quella versione e iniziare a inviare utilizzando solo FCM.

Consulta i documenti degli argomenti FCM per saperne di più.

Firebase Remote Config

Firebase Remote Config è un servizio cloud che consente di modificare il comportamento e l'aspetto dell'app senza richiedere agli utenti di scaricare un aggiornamento dell'app. Quando si utilizza la configurazione remota, si creano valori predefiniti in-app che controllano il comportamento e l'aspetto dell'app. Quindi, in seguito puoi utilizzare la console di Firebase per sovrascrivere i valori predefiniti in-app per tutti gli utenti dell'app o per i segmenti della tua base utenti.

Firebase Remote Config può essere molto utile durante le migrazioni nei casi in cui si desidera testare soluzioni diverse ed essere in grado di spostare dinamicamente più client su un altro provider. Ad esempio, se si dispone di una versione dell'app che utilizza sia Firebase che Parse per i dati, è possibile utilizzare una regola di percentile casuale per determinare quali client leggono da Firebase e aumentare gradualmente la percentuale.

Per ulteriori informazioni su Firebase Remote Config, consultare l' introduzione di Remote Config .

Differenze con Parse Config

Con Parse config è possibile aggiungere coppie chiave / valore all'app nella Dashboard di Parse Config, quindi recuperare ParseConfig sul client. Ogni istanza di ParseConfig che ottieni è sempre immutabile. Quando in futuro recupererai un nuovo ParseConfig dalla rete, non modificherà alcuna istanza ParseConfig esistente, ma ne creerà una nuova e la renderà disponibile tramite getCurrentConfig() .

Con Firebase Remote Config crei impostazioni predefinite in-app per coppie chiave / valore che puoi ignorare dalla console di Firebase e puoi utilizzare regole e condizioni per fornire variazioni sull'esperienza utente della tua app a diversi segmenti della tua base utenti. Firebase Remote Config implementa una classe singleton che rende le coppie chiave / valore disponibili per la tua app. Inizialmente il singleton restituisce i valori predefiniti definiti in-app. Puoi recuperare un nuovo set di valori dal server in qualsiasi momento comodo per la tua app; dopo che il nuovo set è stato recuperato correttamente, puoi scegliere quando attivarlo per rendere disponibili i nuovi valori all'app.

Strategia di migrazione suggerita

Puoi passare a Firebase Remote Config copiando le coppie chiave / valore della tua configurazione Parse sulla console di Firebase, quindi distribuendo una nuova versione dell'app che utilizza Firebase Remote Config.

Se si desidera sperimentare sia Parse Config che Firebase Remote Config, è possibile distribuire una nuova versione dell'app che utilizza entrambi gli SDK fino a quando un numero sufficiente di utenti è migrato dalla versione solo Parse.

Confronto del codice

Parse

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