How to integrate Firebase into your native C++ Android game.

Patrick Martin
Android Developers
Published in
8 min readFeb 3, 2020

--

Many games have a core thing in common when you really dig under the surface: there’s almost always a soft nougaty core of C++. Firebase offers native feeling SDKs for Android, iOS, Unity, and the Web which makes it super easy to connect your game or application to its globe spanning infrastructure. Unfortunately these SDKs are fairly difficult to integrate if you live in C++, making them very inconvenient to use for many game developers. Fret not, as Firebase has a cross platform C++ SDK that abstracts away all the platform specific constructs to get your game talking to the cloud as fast as possible!

The Firebase C++ SDK has five platform targets: Android, iOS, and desktop (Windows, Linux, and MacOS). The APIs exposed are identical through all five, but there are some platform specific nuances that need to be covered to successfully compile a game with Firebase and initialize the library. In this post, I’ll cover Android.

C++

Cocos 2dx 4.0 uses CMake as its default build system, which is also the preferred way of integrating the C++ Firebase SDK. Everything I cover here should be easily adaptable to other build systems or engines. If you’d like to follow along, now would be a great time to type cocos new:

cocos new CocosFirebase -p <com.your_company.your_game> -l cpp -d .

This states:

  • cocos new CocosFirebase: create a new project named CocosFirebase
  • -p <com.your_company.your_game>: the package name for your game, without the “<>” I threw in.
  • -l cpp: the language is C++
  • -d .: create the game in my current working directory

If CMake is new to you, I highly recommend that you read my brief intro article. Similarly, I’m going to use the binary Firebase SDK, but most of what I cover will work with the open sourced one.

Now I need to download the latest version of the Firebase C++ SDK. I chose to place this in third_party/firebase_cpp_sdk for the purposes of this tutorial. Unless you’re planning on distributing your game on Linux, I would recommend using the binary Firebase SDK for now. I do explain the pros and cons of the binary and open source SDKs here.

Android Integration

The first thing to do is to update your project-level build.gradle file. For a Cocos project, this is located in proj.android/build.gradle. You have to add classpath 'com.google.gms:google-services:4.3.3' under buildscript.dependencies. My buildscript { block now looks like this:

What this does is make sure that you can call apply plugin with google-services in a little bit. This is a Google provided plugin that simplifies much of the build process.

Next, you need to include the Firebase module. I like to add a variable to point to my Firebase directory in my settings.gradle file in case I reference Firebase in multiple Gradle modules. Add the following line to your settings.gradle file, located in proj.android/settings.gradle for Cocos projects:

If you’re using the open source SDK, you’ll also want to add this to settings.gradle:

includeBuild has no negative effects on the binary build either, so feel free to leave that in.

gradle.ext.firebase_cpp_sdk_dir creates a variable that I can reference later as $gradle.firebase_cpp_sdk_dir in the various modules that make up my project. $settingsDir is the directory where this settings.gradle resides, so this path becomes an absolute path when referenced later on.

Finally, update your module-level build.gradle file, this is proj.android/app/build.gradle for a Cocos project. First add apply plugin: 'com.google.gms.google-services' at the top:

Then at the end of my file (after the dependencies { section), I add:

apply from: "$gradle.firebase_cpp_sdk_dir/Android/firebase_dependencies.gradle" is where the real magic of the Android SDK is happening. Any dependency you add to firebaseCpp.dependencies is automatically expanded and added to your project. For example, when you add a dependency like analytics, it expands to 'com.google.firebase:firebase-analytics:17.2.1'. This way you always match your C++ library with the Java library it was built for, and the Java dependencies are automatically updated when you update your Firebase C++ SDK.

Note, if you’re cross referencing against the C++ setup guide, you may wonder why I didn’t add:

This passes a variable from Gradle into C++, which is really convenient for an Android based game. Since iOS and desktop targets will not use Gradle, I will instead craft my CMakeLists.txt file in a more cross-platform manner at the risk of this firebase_cpp_sdk_dir variable getting out of sync between Java and C++ build systems.

Finally, if you haven’t already, create a new Android application in your Firebase Console. Download the corresponding google-services.json file, and place this under proj.android/app just like you would any other Android project (you want it co-located with the .gradle file where you call apply plugin: 'com.google.gms.google-services' if you have a more esoteric application architecture).

The C++ Build System

At this point, the managed portion of my project knows about Firebase, but the C++ portion of my game is still in the dark. So next you should open your project’s root CMakeLists.txt file.

First you need to add_subdirectory the directory where you installed the Firebase C++ SDK. If you recall before, this is third_party/firebase_cpp_sdk in my project:

This will make CMake aware of the various Firebase targets.

Next you need to locate the executable or library that will depend on Firebase in your CMake project. By default, Cocos places this in the ${APP_NAME} variable. If you’re not using Cocos, remember that on Android this is almost always a library (via add_library) rather than an executable since even native activities are spawned from a managed process.

Once you know the target name of your game, you need to look up the required Firebase targets for the Firebase products you wish to use. Since I’m using Firebase Analytics, this means that I need firebase_analtyics and firebase_app.

Finally, update your game’s target_link_libraries with the Firebase dependencies you need:

Calling Firebase from C++

I chose Analytics as it’s the simplest Firebase product to get working, and is the best way to test your Firebase integration with Android. To start getting analytics events, you just have to call ::firebase::App::Create.

On Android, this does require two additional pieces of information:

First you need the current JNIEnv* pointer with JniHelper::getEnv() (JniHelper is Cocos specific class that provides a set of methods to simplify calling Java from C++ via JNI).

Next you’ll need a reference to your game’s Activity as a jobject. Cocos makes this available with JniHelper::getActivity().

To initialize Firebase in my Cocos project, I first add a ::firebase::App to my AppDelegate.h:

then I initialize it with:

On the other platforms, Create() doesn’t take any arguments. So it’s not uncommon to conditionally compile this with #ifdef __ANDROID__.

If you’ve done everything right, you should see a user appear in your Analytics Dashboard:

What next?

Now would be a perfect time to start adding a few custom events for your game. I would also recommend Crashlytics as your next Firebase integration. Its NDK support can be vital for tracking down those esoteric native issues that can crop up in games. Once you have Analytics integrated, it’s also really easy to pull in Remote Config to either better tune your game to specific groups of users or even give your designers a way to tweak gameplay values on the fly during development without having to go through a full build/deploy cycle.

If you’re like me, one of the biggest pain points of native C++ game development is just interfacing with web backends. Since most games now have some sort of service component, I recommend checking out Firebase Cloud Functions, Realtime Database, and Authentication to really start feeling Firebase’s power.

What if I don’t use Cocos or CMake?

The Firebase integration does still support the older ndk-build process, with accompanying documentation. Additionally, the binary distribution of Firebase stores its various compiled library files under libs/ organized by operating system and CPU architecture (even STL version for Android).

Retrieving the JNIEnv can be a little tricky without Cocos’ JniHelper class. JNIEnv is passed with most JNI callbacks, but it is not safe to cache this beyond your current call stack. Instead you’ll probably want to cache the JavaVM* pointer from your JNI_OnLoad callback and invoke GetEnv from that when you wish to initialize Firebase.

Getting a jobject activity reference can also be a bit more challenging without Cocos’ convenience libraries since there is no universal way of retrieving the current Activity. Many Java developers may feel inclined to cache their game activity in a static field to make it easier to access from C++, but this will likely cause you to leak your current Activity if you’re not careful. I will typically architect my games so that an Activity “owns” my native application and starts it via some “initialize” call (again, remembering not to cache it unless I’m prepared to handle cleanup in my Activity’s onDestroy callback). If you’re using android_native_app_glue.h, the activity is stored in the android_app struct associated with most callbacks, although it’s confusingly accessed via the activity->clazz field (this is misnamed as documented in the corresponding native_activity.h header file).

Finally, you may not have a way to apply plugin: 'com.google.gms.google-services'. The Firebase plugin includes a script to extract the contents into .xml resources for your game to make it easier to integrate. I would recommend reading this article on what the google-services plugin is actually doing before attempting a gradle-less integration.

Resources

--

--

Patrick Martin
Android Developers

I’ve been a software engineer on everything from games to connected toys. I’m now a developer advocate for Google’s Firebase.