Firebase Remote Config Android Example

Suppose its Christmas and you want to display your app in a new Christmas theme for all the users. You might simply think of sending an app update with the new theme but its not certain that all your users will download the update. Also sending an update for a theme change would be an unnecessary effort, considering that you have to do it for all the festivals. It is for scenarios like these that Firebase Remote Config was built.

Firebase Remote Config is a cloud service that lets you change the behavior and appearance of your app without requiring users to download an app update. Remote config basically allows you to maintain parameters on cloud which can be fetched from your application. These parameters are then used to define the changes you want to see in your application. Like in the above Christmas theme scenario, we can define a parameters with the text, color, images for the new theme which can again be fetched using Remote Config library from your app.

How Firebase Remote Config works ?

Remote Config library helps your app to fetch parameters from the firebase remote config server. It will be responsible for fetching the values and caching them in your app. To handle situations in which the server is not accessible or the parameters are not found we have to define default parameter values in the app. Once fetched from server these values are stored in cache for the next 12 hours.Which means any request in this period will return the cached values.

Remote Config implementation will just take very few lines of code because it handles all the complicated logic of fetching and caching on its own.This is mainly done by have three different configs as explained below

  • Default Config – This config contains default values defined in your app. If no matching keys are found on remote config server these default parameters are copied to the active config and returned to the client
  • Fetched Config – This contains most recently fetched  parameters from the remote config server. But this config cant be directly accessed from your app. You need to activate these config parameters which will copy parameter values from this config to the active config from where they can be directly accessed.
  • Active Config – This is the only config directly accessible from your app and it contains values from either Default or Fetched config.

Caching in Firebase Remote Config

Remote Config library implements parameter caching on its own. After the first fetch the parameters are saved into local cache. By default the cache expires in 12 hours but you can set a custom expiry for the cache using the fetch(time) method. For every fetch request it checks if the cached values are older than the specified expiry time. If yes it fetches for the new parameters. If not the cached values are only returned. Though you can change the cache expiry time for parameter values you are not allowed to call fetch API more than 5 times in an hour.

Don’t panic if you are still not clear how Remote config library works. Its implementation is very simple and the  following example will clear all your confusion

In this Firebase Remote Config example we will display a welcome message in our app using three parameters from the remote config- Welcome string, text size , text color.

Adding Firebase to your App

To start this integration first you need to register your app with Firebase. Go to Firebase Console  and follow the steps mentioned. Once the project is registered download the google-service.json file and add it to your Android project as shown in the screenshot below

Adding Gradle Dependencies

After registering your app with firebase you are provided with a set of dependencies which you need to add in your Android application. Apart from these you also need to add dependency for Remote Config library given below

implementation 'com.google.firebase:firebase-config:16.1.0'

In the end your app’s build.gradle should have the following

dependencies {
    implementation 'com.google.firebase:firebase-config:16.1.0' // Remote Config gradle dependency
    implementation 'com.google.firebase:firebase-core:16.0.4'   // Firebase gradle dependency
}
apply plugin: 'com.google.gms.google-services'   // Google Services plugin

Similarly your project’s build.gradle should have the following

buildscript {
    
    repositories {
        google()
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.2.1'
        classpath 'com.google.gms:google-services:4.0.1'  // Project Level Google Services dependency
       
    }
}

Note that you should always use the latest version of the gradle dependencies. Versions used in this example are latest at the time of publishing this post but it might not be the case always. You can find all the latest versions here.

Designing the Layout

  • In order to show the welcome message we need to define a layout and initialize it in an activity. For this we will have a simple layout with TextView– to display the welcome message and Button to fetch the latest parameters.
    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
    
        <TextView
            android:layout_width="wrap_content"
            tools:text="Welcome to the app"
            android:id="@+id/text"
            android:layout_marginTop="32dp"
            android:layout_centerHorizontal="true"
            android:layout_height="wrap_content" />
    
        <Button
            android:layout_width="wrap_content"
            android:text="FETCH"
            android:id="@+id/fetch"
            android:layout_centerInParent="true"
            android:layout_height="wrap_content" />
    
    </RelativeLayout>

Initializing FirebaseRemoteConfig

  • We just need one object to work with Remote Config library i.e. FirebaseRemoteConfig. The same object is used to set defaults parameters, fetch new parameters or activate the fetched parameters. It is a singleton object and can be obtained using FirebaseRemoteConfig.getInstance() API as shown below
    public class MainActivity extends AppCompatActivity {
    
        private FirebaseRemoteConfig firebaseRemoteConfig;
    
        private Button button;
    
        private TextView textView;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            button = findViewById(R.id.fetch);
            textView= findViewById(R.id.text);
         
            // Fetch singleton FirebaseRemoteConfig object
            firebaseRemoteConfig = FirebaseRemoteConfig.getInstance();
  • Once initialized you can use the setConfigSettings() API to enable developer mode for remote config. Developer mode allows us to refresh the parameter cache any number of times. By default Firebase doesn’t allow us to fetch new parameters for more than 5 times in an hour as explained in the last section. For this example we enable developer mode as shown below
    firebaseRemoteConfig.setConfigSettings(new FirebaseRemoteConfigSettings.Builder()
                   .setDeveloperModeEnabled(true)
                   .build());

Defining Default Values

  • As already mentioned, in this example we will have a welcome message in our app based on three parameters from remote config. Each parameter needs to have unique key and value and it is mandatory for us to define default values for all the parameters locally in our application. This can be achieved  by having a resource XML file or defining a map as shown below
    <?xml version="1.0" encoding="utf-8"?>
    <defaultsMap>
        <entry> 
            <key>text_str</key>
            <value>Welcome to the app</value>
        </entry>
        <entry>
            <key>text_str</key>
            <value>14</value>
        </entry>
        <entry>
            <key>text_color</key>
            <value>#FF0000</value>
        </entry>
    </defaultsMap>

    Instead of a XML resource file you can instead define default values in a map as shown below. Either of them will work fine

    Map<String,Object> map =new HashMap<>();
    map.put("text_str","Welcome to the app");
    map.put("text_size",14);
    
    map.put("text_color","#FF0000");

    Keep note of the parameter keys used in your default map. You will have to use the same when defining parameters on Firebase Console

  • Now we set the default parameters for remote config using the setDefaults() API. We set the map from our XML file as default. This will ensure that if any parameter key is not found on cloud its value will be fetched from this map
    firebaseRemoteConfig.setDefaults(R.xml.default_map)
    

    Getting Parameter Values

  • firebaseRemoteConfig.get<>() is used to get parameter values from the config. As already explained this can be either be from your default config or can be from the remote server. This call doesn’t actually send request for the parameter values from server but instead gets the values stored in the active config. If we have not fetched the parameters from server it returns the default in-app parameter values instead
  • In the example we try to get three parameter values using the corresponding get APIs and the parameter keys. We use the three parameter values to define the string, text size and text color of our TextView.
public class MainActivity extends AppCompatActivity {

    private FirebaseRemoteConfig firebaseRemoteConfig;

    private Button button;

    private TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
       
        button = findViewById(R.id.fetch);
        textView= findViewById(R.id.text);
        firebaseRemoteConfig = FirebaseRemoteConfig.getInstance();
        firebaseRemoteConfig.setConfigSettings(new FirebaseRemoteConfigSettings.Builder()
                .setDeveloperModeEnabled(true)
                .build());
        firebaseRemoteConfig.setDefaults(R.xml.default_map);
        firebaseRemoteConfig.setDefaults(map);
    
        textView.setTextColor(Color.parseColor(firebaseRemoteConfig.getString("text_color")));
        textView.setTextSize((float) firebaseRemoteConfig.getValue("text_size").asDouble());
        textView.setText(firebaseRemoteConfig.getString("text_str"));
    }
  • As seen in the above code snippet as soon as the activity is created (onCreate) we try to get the parameter values using the corresponding get<>() APIs and display the welcome text.

Fetching Latest Parameters

  • firebaseRemoteConfig.fetch() call initiates request to remote config server for the latest parameters and once fetched these are stored in local cache. By default these parameters expire in 12 hours but we can set custom expiry time using the fetch(long) method (the long argument denoting time in seconds). Within the expiry time, 12 hour by default, if we try calling fetch() again no request to server is sent instead the values stored in cache are returned. When requesting using fetch(long) the remote config client checks if the cached values are older than the seconds specified in method argument. If yes new parameters are requested else the cached value is returned.
  • In this example we initiate the fetch request on button click using the fetch(long) method. We set the custom expiry time as 0 so that every time our app gets the latest values from the config.
    button.setOnClickListener(new View.OnClickListener() {
               @Override
               public void onClick(View v) {
                   /*
                    This will initiate fetching of parameters. We have set the expiry time as 0
                    which will ensure we get fresh parameters every time
                    */
                   firebaseRemoteConfig.fetch(0).addOnCompleteListener(new OnCompleteListener<Void>() {
                       @Override
                       public void onComplete(@NonNull Task<Void> task) {
                           if (task.isSuccessful()){
                               Toast.makeText(MainActivity.this, "Activated", Toast.LENGTH_SHORT).show();
                           }else {
                               Toast.makeText(MainActivity.this, "Not Activated", Toast.LENGTH_SHORT).show();
                           }
                       }
                   });
               }
           });

Activating Fetched Parameters

  • Even after a successful fetch request when you try to get the values using get<>() APIs old values are returned. Remember how they are three configs (Active, Fetched and Default) and only active config is accessible from your app. Therefore in order for the fetched config parameters to be available you need to activate them using firebaseRemoteConfig.activateFetched(). This will move all the parameters from the fetched config to active config thereby making them accessible from the app.
  • This concept of parameter activation is to improve user experience and avoid any abrupt changes in behavior or design of the app when you are still navigating the app.
  • In the following example we activate the fetched parameters in the OnClick listener as shown below. After activation if you try to retrieve values using get() APIs the new parameter values will be returned. In this example we just activate the new values but don’t “get” them immediately in OnClick listener
    button.setOnClickListener(new View.OnClickListener() {
               @Override
               public void onClick(View v) {
                   /*
                    This will initiate fetching of parameters. We have set the expiry time as 0
                    which will ensure we get fresh parameters every time
                    */
                   firebaseRemoteConfig.fetch(60*5).addOnCompleteListener(new OnCompleteListener<Void>() {
                       @Override
                       public void onComplete(@NonNull Task<Void> task) {
                           if (task.isSuccessful()){
                               Toast.makeText(MainActivity.this, "Activated", Toast.LENGTH_SHORT).show();
                               /*
                               Activiting fetched parameters. The new parameters will now be available to your app
                                */
                               firebaseRemoteConfig.activateFetched();
                           }else {
                               Toast.makeText(MainActivity.this, "Not Activated", Toast.LENGTH_SHORT).show();
                           }
                       }
                   });
               }
           });

Just be reminded that so far we have not created any parameters on the Firebase Console. Since we only have default in app parameters defined these will only be returned even if we try to calling fetch() or get()

Complete Activity

This finishes our client side implementation for Firebase remote config. Our complete activity will now look as the code snippet below. 

public class MainActivity extends AppCompatActivity {

    private FirebaseRemoteConfig firebaseRemoteConfig;

    private Button button;

    private TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
       
        button = findViewById(R.id.fetch);
        textView= findViewById(R.id.text);
        firebaseRemoteConfig = FirebaseRemoteConfig.getInstance();
        firebaseRemoteConfig.setConfigSettings(new FirebaseRemoteConfigSettings.Builder()
                .setDeveloperModeEnabled(true)
                .build());
        firebaseRemoteConfig.setDefaults(R.xml.default_map);

        /*
        Setting color, size and string for TextView using parameters returned from
        remote config server
         */
        textView.setTextColor(Color.parseColor(firebaseRemoteConfig.getString("text_color")));
        textView.setTextSize((float) firebaseRemoteConfig.getValue("text_size").asDouble());
        textView.setText(firebaseRemoteConfig.getString("text_str"));

        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                /*
                 This will initiate fetching of parameters. We have set the expiry time as 0
                 which will ensure we get fresh parameters every time
                 */
                firebaseRemoteConfig.fetch(60*5).addOnCompleteListener(new OnCompleteListener<Void>() {
                    @Override
                    public void onComplete(@NonNull Task<Void> task) {
                        if (task.isSuccessful()){
                            Toast.makeText(MainActivity.this, "Activated", Toast.LENGTH_SHORT).show();
                            /*
                            Activiting fetched parameters. The new parameters will now be available to your app
                             */
                            firebaseRemoteConfig.activateFetched();
                        }else {
                            Toast.makeText(MainActivity.this, "Not Activated", Toast.LENGTH_SHORT).show();
                        }
                    }
                });
            }
        });

    }
}
  • Note that we initiate fetching latest params from server only on click of button. Getting parameters before that will return values from the default map. Therefore in first launch of app we see the default in app values displayed (Screenshot below)
  • Also note that we have not defined any parameters for this app on remote config server yet. So even clicking the button and requesting using fetch() would return the default in-app values only.

Adding Parameters to Console

In this section we will add the parameters to the remote config server. So far we had only defined the parameters in app

  • Go to Firebase Console and select the project your created in step 1.
  • Once the project is selected click Grow > Remote Config in the left pane. Look at screenshot for reference.
  • Now click the “Add your first parameter” button and then add a string as shown in the screenshot below. Keep the parameter key same as the default one defined in your app map. I created a parameter with string value “Hello Android Clarified! Its working”.
  • Similarly add parameters to define your text size and and text colour. Again the parameter key should be same as the one defined in your app
  • After adding you need to publish this parameters to make them accessible from your app. Just click the Publish Changes button on the upper right corner of your console. Look at the screenshot above reference

Fetching from Server

  • Now clicking “Fetch” button in your app will request the parameters values from server. But notice that even after clicking fetch the text immediately doesn’t change. This is because after the request is successful we just activate the latest parameters but don’t retrieve them to update the text. So in order for the changes to reflect you need to restart the app. This will ensure on onCreate() is called again and latest parameters are retrieved from active config using get<>() APIs.

Conclusion

This finishes our Firebase Remote Config example. If properly implemented Firebase remote config can give developers complete control over app behaviour across all the versions. I hope this tutorials helps you in understanding Firebase. Feel free to comment any questions.

If you liked this tutorial you can find more at Android Examples