Accessing Android Resources from C++

Posted on 28 April 2011

If you’re writing an Android game in C++ you quickly hit the problem of how to load resources (unless you are targeting Android 2.3). There are already a number of ways to do it.

I came up with a different method that I haven’t found elsewhere, so I thought I should document it. Here’s the gist of it:

This has the advantage that we don’t have to bloat our game code with extra copies of libraries that are already part of Android.

On to the details…

1. Resource IDs

This is an ugly preprocessor job, so if this disgusts you, you can bail out right here ;-) However once it’s done, you can forget about it. (Alternatively you could write a script to do it instead.)

The following assumes that our Java package is com.example.mygame.

We need to include the gen/com/example/mygame/R.java file in our C++ code, and make it compile. This requires a few syntax tweaks. The easy ones can be dealt with by #defining class to become namespace and defining other keywords, such as final, to become nothing.

The hard bit is dealing with the package declaration, as dots can not be used in preprocessor macro names. My approach was to define a structure that matches the package name so that com.example.mygame is a variable that exists within an unused function.

Finally, we need the values to be const, so that we don’t get link errors, so we replace int with const int.

Here it is in all its ugly glory:

#ifndef RESLIST_INCLUDED
#define RESLIST_INCLUDED

// Convert Java to compilable C++:

// Handle "package com.example.mygame;"
#define package inline void dummyfunc() { \
    struct Com {struct Example {char mygamevar;} example;} com;
#define mygame mygamevar=1;}

// Handle keywords
#define public
#define static
#define final
#define class namespace

// Avoid link errors:
#define int const int

#include "../../gen/com/example/mygame/R.java"

#undef package
#undef mygame

#undef int
#undef public
#undef static
#undef final
#undef class

#endif /* RESLIST_INCLUDED */

2. Use the Android API to load resources

The following shows part of a Java wrapper around a C++ Game class. When the C++ implementation of the create() function is called, it will be passed the associated Java object. We’ll store that in our C++ code and use it later to look up and call the loadRawResource() function defined at the bottom. This function is fundamentally simple, but complicated by mandatory exception handling boilerplate.

// This is a wrapper for the C++ game class.
// It holds the C++ pointer in the gamePtr variable.
public class Game {
    static {
        System.loadLibrary("mygamelib");
    }
    
    private native long create();    
    private native void destroy(long gamePtr);
    // ... more native functions: update, render, etc...
        
    private long gamePtr = 0;
    private Context context;
    
    Game(Context context) {
        this.context = context;
        gamePtr = create();
    }

    // ... clean-up code, etc...

        
    //-----------------------------------------------------------
    // called from C++
            
    public byte[] loadRawResource(int id) {
        try {
            InputStream stream = context.getResources().openRawResource(id);
            try {
                byte[] data = new byte[stream.available()];
                stream.read(data, 0, data.length);
                return data;
            } 
            catch (IOException e) {
                Log.e("mygame", e.toString());
            } 
            finally {
                try {
                    stream.close();
                } 
                catch (IOException e) {
                    Log.e("mygame", e.toString());
                }
            }
            return null;
        } 
        catch (NotFoundException e) {
            Log.e("mygame", "raw resource not found.");
            return null;
        }
    }
}

3. Call loadRawResource() from C++

First we have to grab the Java environment and Game object in our create() function:

jlong Java_com_example_mygame_Game_create(JNIEnv* env, jobject jobj) {
    Game* g = new Game(env, jobj);
    return reinterpret_cast<jlong>(g);
}

Then later, using the values that were passed at creation time, we can get our resource data…

void loadResource(int resId, JNIEnv* env, jobject jobj) {
    jclass cls = env->GetObjectClass(jobj);
    // The last parameter here asks for a function taking 
    // an integer and returning an array of bytes:
    jmethodID mid = env->GetMethodID(cls, "loadRawResource", "(I)[B");
    if (mid == 0) {
        // Error: Can't find method
    }
    else {
        env->ExceptionClear();
        jbyteArray byteArray = (jbyteArray)env->CallObjectMethod(jobj, mid, resId);
        if (env->ExceptionOccurred()) {
            env->ExceptionDescribe();
            env->ExceptionClear();
        }
        else if (byteArray) {
            jint len = env->GetArrayLength(byteArray);
            jboolean isCopy;
            jbyte* data = env->GetByteArrayElements(byteArray, &isCopy);
            // *** Do something with data ***
            // ...
        }
        else {
            // Java returned a null array
        }
    }
}

What about drawable resources?

My original code loaded drawables using the same approach, by opening a BitmapDrawable resource: getDrawable(id) instead of openRawResource(id). Then getBitmap().copyPixelsToBuffer() gets the image data. The problem with this is that Android bitmaps use pre-multiplied alpha internally. When you use copyPixelsToBuffer(), the alpha multiplication is reversed, but if your alpha value is zero, your colour information is already gone for good. Some of my textures have zero alpha channels in order to give me additive blending (via the pre-multiplied regime described in the link above), so this resulted in blank textures. If you’re using conventional blending (or none) this may not be a problem and you can use a BitmapDrawable to load PNGs and JPEGs. Instead, I pre-process my textures into a simple uncompressed format, store them in res/raw and load them with the above loadRawResource() function.

Refinements

Recently I developed a PC build of my game, for debugging and video capture, which uses a script to generate a mapping from resource ID to filename. I am starting to think that a script may be the way to go for all platforms, replacing step (1) above. In addition to generating a nice clean header file for resource IDs, it could generate the resource lists that I currently use to pre-load all my textures at start-up, which are currently hand coded. Parts (2) and (3) would continue to do the actual loading on Android.