Using multiple Firebase projects in your Android App

Mehmed Mert
5 min readOct 18, 2018

Firebase Android SDK is designed to work with one Firebase project by default. But it is quite possible to access multiple projects in a single App due to different reasons. One common case in the industry is, for example, the requirement for having multiple development environments or delivery stages. In this article, I want to share my experience with you and summarize the different ways how to achieve this with the Firebase Android SDK.

Using different config files per build variant

As described in the official Firebase documentation, it is possible to use different configuration files for each build type. The only thing you need to do is to put each google-services.json file into the root of the regarding build variant folder. Assume that you have 3 environments — development, alpha and release— and for each a build variant in your project. Then you have to place the google-services.json files like this:

app/
google-services.json
src/development/google-services.json
src/alpha/google-services.json
src/release/google-services.json

Depending on the build variant you’ve chosen for building your App, the corresponding configuration will be loaded by the SDK for initializing the default FirebaseApp instance. The main advantage of this approach is that you don’t need to change anything in your code, you just can get the necessary Firebase services by calling the default getInstance() method.

Using string resources for configuration

Referring again to the official Firebase documentation, it is possible to use string resources instead of putting google-services.json files in your project, in order to get Firebase configured properly for the desired build variant. In that case, you need to initialize the default FirebaseApp instance programmatically calling the FirebaseApp.initializeApp() method once at app start (in onCreate() method of your Application class, for example):

FirebaseOptions options = new FirebaseOptions.Builder()
.setApplicationId(getString(R.string.firebaseApplicationId))
.setApiKey(getString(R.string.firebaseApiKey))
.setDatabaseUrl(getString(R.string.firebaseDatabaseUrl))
.build();
FirebaseApp.initializeApp(this /* Context */, options);

You need to define, of course, the necessary strings in your string resource files in advance. Assuming again, that you have the 3 environments from the previous section, you could define these strings in firebase_strings.xml file in the /res/values folder of the regarding build variant folder, like the following:

app/
src/development/res/values/firebase_strings.xml
src/alpha/res/values/firebase_strings.xml
src/release/res/values/firebase_strings.xml

In each firebase_strings.xml file you need to define then the necessary configuration info:

<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="firebaseApplicationId" translatable="false">1:530266078999:android:481c4ecf3253701e</string>
<string name="firebaseApiKey" translatable="false">AIzaSyBRxOyIj5dJkKgAVPXRLYFkdZwh2Xxq51k</string>
<string name="firebaseDatabaseUrl" translatable="false">https://project-1765055333176374514.firebaseio.com/</string>
</resources>

Here you may ask where do we get the application id, API key, and the database URL. You can get this information simply from your google-services.json file. The application id is the value of the mobilesdk_app_id element, the API key is the value of the current_key element, and the database URL is the value of the firebase_url element of the JSON object. Alternatively, you could obtain this information also from the Firebase console.

Configuring Firebase dynamically for different environments

The previous two approaches have the advantage, that you don’t need to touch your code, you just build your application for the desired environment and that is it. But imagine that for testing purposes, you have the requirement that it should be possible to change the environment within the app at runtime. This won’t work with the previous two approaches. Even killing the app and starting it again won’t help, you have to install the necessary build variant in order to switch the environment.

For that purpose, you have to initialize and maintain the for each environment a FirebaseApp instance respectively in your app and I want to show you how I achieved this functionality in my recent project.

The basic approach

The simplest way to achieve this functionality is to initialize necessary FirebaseApp instances once at app start:

FirebaseApp development = Firebase.initializeApp(context, developmentOptions, Environment.DEVELOPMENT);
FirebaseApp alpha = Firebase.initializeApp(context, alphaOptions, Environment.ALPHA);
FirebaseApp release = Firebase.initializeApp(context, releaseOptions, Environment.RELEASE);

Then you use depending on your current environment the regarding FirbaseApp instance. Depending on your UI logic, you will need to call the same routine when the user changes the environment:

Firebase currentApp;
switch(environment) {
case Environment.DEVELOPMENT:
currentApp = development;
break;
case Environment.ALPHA:
currentApp = alpha;
break;
case Environment.RELEASE:
currentApp = release;
break;
default:
throw new RuntimeException();
break;
}

Before using any Firebase service, you need to get its instance with that FirebaseApp instance:

FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(currentApp);
FirebaseDatabase firebaseDatabase = FirebaseDatabase.getInstance(currentApp);

Of course, depending on your architecture, you need to find the best way to integrate the routines for selecting the correct FirebaseApp instance and getting the needed Firebase services and encapsulate them in a proper way.

On-Demand initialization

As a next step, in order to save some memory resources, you can think of on-demand initialization of the FirebaseApp instances. But this is a little bit tricky task because an attempt for checking if a particular FirebaseApp instance was initialized previously or not using FirebaseApp.getInstance() will fail:

// This will throw an exception, if the FirebaseApp
// for that environment wasn't initialized previously:
FirebaseApp currentApp = FirebaseApp.getInstance(environment);

In order to avoid this, an extra mechanism is needed for knowing, if a particular FirebaseApp instance was initialized or not. One possibility, which I want to propose, is to use a Map holding the FirebaseApp instances for the different environments. We need to adjust the basic approach, then, like this:

Map<String, FirebaseApp> firebaseApps = new HashMap<>();...if (!firebaseApps.containsKey(environment)) {
FirebaseApp firebaseApp = FirebaseApp.initializeApp(context, environmentOptions, environment);
firebaseApps.put(environment, firebaseApp);
}
...FirebaseApp currentApp = firebaseApps.get(environment);...FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(currentApp);

This way, you don’t even need the ugly switch statement. The only thing you have to think about is how to provide the correct configuration info with the environmentOptions object. For that, you could use enum ‘s instead of String constants, with a field ofFirebaseOptions , which you can access directly while calling FirebaseApp.initializeApp() method. Here how the enum class could look like:

public enum Environment {
development(new FirebaseOptions.Builder()
.setApplicationId("1:530266078999:android:481c4ecf3253701e")
.setApiKey("AIzaSyBRxOyIj5dJkKgAVPXRLYFkdZwh2Xxq51k")
.setDatabaseUrl("https://project-1765055333176374514.firebaseio.com/")
.build()
),
alpha(new FirebaseOptions.Builder()
.setApplicationId("1:530266078999:android:481c4ecf3253701e")
.setApiKey("AIzaSyBRxOyIj5dJkKgAVPXRLYFkdZwh2Xxq51k")
.setDatabaseUrl("https://project-1765055333176374514.firebaseio.com/")
.build()
),
release(new FirebaseOptions.Builder()
.setApplicationId("1:530266078999:android:481c4ecf3253701e")
.setApiKey("AIzaSyBRxOyIj5dJkKgAVPXRLYFkdZwh2Xxq51k")
.setDatabaseUrl("https://project-1765055333176374514.firebaseio.com/")
.build()
);
@NonNull
public final FirebaseOptions firebaseOptions;
Environment(@NonNull FirebaseOptions.Builder optionsBuilder) {
firebaseOptions = optionsBuilder.build();
}
}

And here the adjusted routine for on-demand initialization:

Map<Environment, FirebaseApp> firebaseApps = new HashMap<>();...if (!firebaseApps.containsKey(environment)) {
FirebaseApp firebaseApp = FirebaseApp.initializeApp(context, environment.firebaseOptions, environment.name());
firebaseApps.put(environment, firebaseApp);
}
...FirebaseApp currentApp = firebaseApps.get(environment);...FirebaseAuth firebaseAuth = FirebaseAuth.getInstance(currentApp);

Summary

In most of the cases, a simple configuration using google-services.json will be enough for having different Firebase environments in your App. But as we saw, things can get complex very quickly, and depending on the requirements custom ways for configuration and initialization of the Firebase services can be needed. I this article I tried to summarize the different approaches for achieving this goal and share my personal experience. I hope that it was helpful to you and that you enjoyed it.

--

--