HowTo: SDL on Android part 1 – Setup

This is part 1 of my HowTo: SDL on Android series.
(next)

I’ve been recently wanting to get into mobile development.  How does one do that?

Well, your main options are iOS (iPod, iPhone, or iPad), Windows Phone (7 or 8), BlackBerry (BlackBerry smartphones, PlayBook), and Android (most of the other devices on the market).  I had always leaned toward Android as it’s slightly more open than the others.  Also, we’ve been looking at getting a Kindle Fire HD sometime. So, we finally made the decision and bought a Kindle Fire HD 7″.  It’s a decent tablet, but I want to make games!

My experience and codebase is all based on SDL.  The SDL devs have done a lot of work recently getting the new version of SDL working nicely on mobile platforms.  I had barely messed with SDL 2.0 before this, so I really had to jump all in. This will be a work-in-progress tutorial-style post as I document the path I take to get started and get my existing code ported.

Setting Up for Android Dev

The first step is getting your dev environment ready for Android development.  Android dev is done in Java, but we’ll be using the Java Native Interface (JNI) to work in C and C++ instead.

The process here is not too bad but it takes some time and patience.  You just follow along with the official documentation.

The developer.android.com website has instructions and hosts the ADT bundle (Android Developer Tools), SDK (Software Development Kit – included in ADT bundle), and NDK (Native Development Kit) downloads and describes how to set them up.  The ADT bundle includes the SDK and the Eclipse IDE, which we’ll be using to do everything.  I use Code::Blocks normally, but it’s really not a big deal to try out Eclipse.  (I did write a little mouse/touch compatibility code so I can use Code::Blocks and test on Windows/Linux).

Some devices need a little extra setup, like the Kindle Fire HD.  Amazon has info on the details.

Either way, you need to also set up the JDK (Java Development Kit).  The instructions recommend JDK 6 (as of writing), I guess.  The only gotcha here is that you need to match the bitness of your OS, JDK, and Eclipse.  I didn’t manage to get the 64-bit (x64) stuff working on Windows before I got annoyed, so I just used 32-bit (x86) JDK and 32-bit ADT bundle.  On Linux I had no problems.

Once you get that stuff set up, you should be able to open Eclipse and go to Window > Android SDK Manager.  This is where you’ll download and install all of the components you need for the Android versions you’ll be targeting.  The Android SDK Tools and Android SDK Platform-tools are the first to select.  The Kindle Fire HD (old now) uses Android 4.0.3 (API level 15), so I expanded that category and chose the SDK Platform, ARM EABI v7a System Image (for the emulator).  The Fire also needs a few other packages (USB driver, device definitions, Android Support Library, etc. – see the Amazon page).

Now you have a fully-functional Java development environment for Android!  Hooray for those who use Java!  But I want more…

Setting up SDL2

You will need a copy of the SDL source code.  You can find a stable version on the libsdl.org download page or you can use Mercurial (hg) to get the bleeding edge (and only lightly tested) stuff.  Mercurial is a revision/version/source control software that the SDL developers use to store the source tree and history.  If you use Mercurial, clone the SDL tree:

 hg clone http://hg.libsdl.org/SDL 

For TortoiseHG, just use the Clone command and put in the URL (http://hg.libsdl.org/SDL).

SDL comes with a README.android that describes the way the Android port works.  SDL takes care of the Java stuff that loads up your native code and it comes with a project template (SO HELPFUL!), but you still need to do a little work to get everything linked up.

The project setup starts with inverting the android-project directory in the SDL folder.  Copy android-project to some place you want your project to live and rename it.  Then copy (or symlink) the whole SDL directory and put it into your project’s jni directory.  This jni directory and its src child are now the most important directories in your project.  ‘jni’ will hold all of the libraries that you need to compile and link in and ‘src’ has your project’s own source code.

Aside from those directories, three build files are important.  AndroidManifest.xml contains info that describes your app to the Android system, jni/Application.mk has special environment settings, and jni/src/Android.mk tells the NDK how to build your code.

Your Very Own Source

Time to start your native code.  Create a “Hello World” source file, like jni/src/main.c (main.cpp is cool too) and open jni/src/Android.mk.  Change YourSourceHere.c under LOCAL_SRC_FILES to main.c or whatever you named it. You can make sure that it’s all good now by running ndk-build (ndk-build.cmd on Windows) in your project’s base directory.  You downloaded the NDK already, right?  If you added the NDK directory to your PATH environment variable, it’s easy.  Otherwise you have to specify the whole (absolute or relative) path to ndk-build.  On Windows, you can modify PATH in something like Control Panel > System > Advanced System Settings > Environment Variables… (use semicolons to separate entries and be careful not to ruin it all).

The ndk-build command should start compiling SDL and give you no trouble at all (perfect world)!  We can’t really run the result on this system, so…

Now that you have the basic structure and you’re sure it works, you can load it up in Eclipse.  Go to File > New > Project… and choose Android > Android Project from Existing Code.  Tell it where your project’s base directory is, then click Finish.  Eclipse might pick up the bare project nested in the copied SDL directory, so uncheck that if it does.  There’s also an option to “Copy projects into workspace”, but that messed things up for me, so you probably shouldn’t do it.

After that finishes, you should have a nice, working project in Eclipse.  You can press the green “Run” button to test it (it won’t do much…) on the emulator or a plugged-in device.

An Example Program

I’m not about to write a whole tutorial on SDL 2.0 right here, so I’ll just drop a demo.  This will at least let you see something on-screen if everything is working right.

To get there, we have to load an image.  Android makes it a little tougher because all of our data is packed into a .apk that is installed on the device.  However, SDL2 has a nice feature for accessing assets.  If you put stuff into the assets directory of your project, SDL will look there (within the zipped package) when you use a SDL_RWops.  We can load all sorts of data that way, but SDL goes even further.  SDL_LoadBMP() looks in the assets directory too.  That makes loading images painless.

Here’s the code.

You can use this in main.c and it should work.  Don’t forget to put some sort of image.bmp in your assets directory.  Here’s one of those if you need it:

image

Important Settings in AndroidManifest.xml

When you’re satisfied that this stuff actually works, you might be interested in naming the project.  Well, you have to anyway if you want to install another one (Android requires a unique package name/ID for every app).  That process starts in AndroidManifest.xml in the base directory. AndroidManifest.xml is how the app describes itself to the Android system.  The app name, Android-specific settings, and permissions go here.  Android can tell if an app will work on a particular device by some of these settings.

First, we’ll individualize our project package.  Near the top of AndroidManifest.xml, you’ll see the package name “org.libsdl.app”.  Change that to something that represents your team and project.  My commercial DinoMage team might use something like “com.dinomage.test1”. Moving on down the file, a comment explains the next step.  Look for this:

 <activity android:name="SDLActivity" 

And change “SDLActivity” to some class name of your choice.  I’ll use “MyTest1”.  Now we have to make a Java activity with that name.  Grab the sample from SDL’s README.android (it might be different than my version) or here:

package com.gamemaker.game;
import org.libsdl.app.SDLActivity;
/*
 * A sample wrapper class that just calls SDLActivity
 */
public class MyGame extends SDLActivity { }

Change the package line to match what you chose for your package name and change the class name (MyGame) to yours.  This file will be nested in directories (in the base src directory) according to the package name.  My resulting file would be src/com/dinomage/test1/MyTest1.java. Another important thing in AndroidManifest.xml is the specification of the activity settings.  I have this attribute added to the activity element to enable orientation change events without crashing:

android:configChanges="keyboardHidden|orientation"

Android will, by default, shut down your app and try to restart it if one of these configChanges occurs and you didn’t specify in this way that your app can handle it.  Thanks to Jesse for pointing out that for targeting Android API level 13 or higher, you need to add screenSize to that OR’ed configChanges combo.

And lastly, there are the resource references like @string/app_name.  These are pulled from the res directory (res/values/strings.xml for @string).  You can mess with those too.

What is Android.mk?

Android.mk is used to drive the build process for your app.  In short, it’s a Makefile.  This is where you’ll be making a mess if you need to set your compilation flags, libs, include directories, source file list, etc.  I just have one tip.  In your jni/src/Android.mk, do something like this to pull in all of your source files:

LOCAL_SRC_FILES := $(SDL_PATH)/src/main/android/SDL_android_main.c \
$(patsubst $(LOCAL_PATH)/%, %, $(wildcard $(LOCAL_PATH)/src/*.cpp)) \
$(patsubst $(LOCAL_PATH)/%, %, $(wildcard $(LOCAL_PATH)/src/*.c))

Application.mk

Application.mk has special settings for the build environment.  Mine just looks like this:

APP_STL := gnustl_static
APP_ABI := armeabi x86

That lets me use the C++ Standard Template Library (STL) and builds two copies of my app, one for ARM and one for x86 (Intel).  You should probably have armeabi-v7a in there too (v7 has FPU support and stuff), but I ran into a bug in Android 4.0 that makes my apps fail to link properly at runtime when armeabi and armeabi-v7a binaries are both in the apk.  That pretty much ruined it for me.

Running ndk-build

ndk-build is the “make” command for Android.  When run on your project’s base directory, it will compile all of your native code and pump out the binaries we need.  You can run ndk-build from the command line or set it up as an external tool in Eclipse.  When you use adb (via command line or Eclipse’s “Run” or “Run As…  Android application”) to push the app to a device or emulator, these binaries will get wrapped up into the apk.

If you have anything to add, please let me know in the comments!

45 thoughts on “HowTo: SDL on Android part 1 – Setup

    1. @rosen, this missing symbol error happens when you use ndk of a different architecture than your phone.. if your phone is 32 bit, make sure ndk toolkit is the 32 bit platform version.

    1. I’m not using ES 2.0 personally.  I’m sticking with ES 1.1 until I have a real need for shaders, then I’ll go whole-hog into the programmable pipeline.  The default SDL 2.0 AndroidManifest.xml specifies ES 2.0, so if you already are using OpenGL 3+, then it should be no problem getting going.

  1. Really good tutorial!! I want a tutorial in Japanese like this for more people to know this way of mobile develop . Would I be able to translate this? If possible, I’d like to publish it on my blog… Anyway, I appreciate you and your tutorial:)

  2. Hi Jonny, great tutorial, thanks !

    I have written another SDL on Android tutorial that’s more focussed on the build process itself and making a reusable project template that links libSDL as a static library and with fixes for some of the most problematic with SDL on Android (screen rotation, etc.).

    http://en.wildservices.net/2013/10/making-libsdl-2-apps-on-android.html

    For the user code (i.e. main.c) I have reused your code almost verbatim (with credits!). Please let me know if this is a problem.

  3. Your main.c doesn’t work for me. Just a black screen and then it quits. Can’t get printf to return anything in LogCat, so I have no idea wether the code is even running or not. It compiles alright, though.

    1. I just updated the logging section in the next part of this series.  Perhaps you can use some of that info to track down the issue.

      You’ll notice that I used a non-black color to clear the screen.  So if you’re not getting that color, there’s something wrong with the renderer that is created.  You can add some logging to the error checking here.  Use SDL_GetError() when there’s an error to get more description of what went wrong.  If that doesn’t help, I’d suggest that there’s a problem with your SDL2 or graphics drivers.  Does logcat say anything at all?

      1. Hi

        cheers for the tutorial. I managed to build with the ndk and compile an APK and deploy to the emulator…..but I get the same problem as wolfie; the app just exits. I tried your suggestion of SDL_Log but nothing gest sent to logcat. Any other debuggin tips?

        Cheers

        Dan

        1. Hmmm…. oddly enough it runs fine on my phone. Is there a specific setting for the AVD that needs to be set?

          Cheers

          Dan

          1. I can’t be certain…  The emulator and devices have in the past differed in several significant ways.  I’m not sure what the current state of the emulator is, but it was once very, very sad (e.g. no support for GL ES 2.0).  My best recommendation for Android dev is to test on multiple devices, if you can, and don’t ever use the emulator.

  4. Have you tried C4Droid? It is totally awesome, and implements SDL2. It is so worth the $3. I can code anywhere! I’m learning SDL2 now because of it.
    Thank you!

    1. I’m trying to learn SDL2, but i’m having a heck of a time finding example code that i can drop in and run. It doesn’t say it can’t find SDL.h, but all the functions and classes of SDL are undeclared according to the compiler. Could you drop me a line and show me something you got working?

  5. I can’t manage to make my program to run on android. It just start,then stop. I have 4 headers, and 5 .cpp(1 = main.cpp), they are all in src(is that correct?). In main i am initializing everythin thru SDL_Init(||);
    I am creating a window, and a surface from that window. A loop. What could be the problem?

    1. There are several ways things could get messed up.  All I can suggest is to follow through the directions again and try a minimal program before getting into anything else.  Native (JNI) source files would be in the jni directory.  The src directory (not in jni/) is for Java code.

  6. FATAL EXCEPTION: main
    Process: org.libsdl.app, PID: 7192
    java.lang.UnsatisfiedLinkError: dlopen failed: cannot locate symbol “signal” referenced by “libSDL2.so”…
    at java.lang.Runtime.loadLibrary(Runtime.java:364)
    at java.lang.System.loadLibrary(System.java:526)
    at org.libsdl.app.SDLActivity.(SDLActivity.java:49)
    at java.lang.Class.newInstanceImpl(Native Method)
    at java.lang.Class.newInstance(Class.java:1208)
    at android.app.Instrumentation.newActivity(Instrumentation.java:1061)
    at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2101)
    at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2233)
    at android.app.ActivityThread.access$800(ActivityThread.java:135)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1196)
    at android.os.Handler.dispatchMessage(Handler.java:102)
    at android.os.Looper.loop(Looper.java:136)
    at android.app.ActivityThread.main(ActivityThread.java:5001)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:515)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:785)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:601)
    at dalvik.system.NativeStart.main(Native Method)

    1. I also get the same error message whenever I try to execute the program in Eclipse. Anyone who’s got an answer on what’s causes this error?

    2. This error happens when you are using ndk toolkit from the wrong platform architecture.  If your phone is a 32 bit phone, make sure you have the 32bit platform version of ndk.

  7. 09-15 20:35:15.481: D/dalvikvm(1898): Trying to load lib /data/app-lib/org.libsdl.app-1/libSDL2.so 0xb406ede8
    09-15 20:35:15.481: E/dalvikvm(1898): dlopen(“/data/app-lib/org.libsdl.app-1/libSDL2.so”) failed: Cannot load library: soinfo_relocate(linker.cpp:975): cannot locate symbol “signal” referenced by “libSDL2.so”…
    09-15 20:35:15.491: W/dalvikvm(1898): Exception Ljava/lang/UnsatisfiedLinkError; thrown while initializing Lorg/libsdl/app/SDLActivity;
    09-15 20:35:15.491: W/dalvikvm(1898): Class init failed in newInstance call (Lorg/libsdl/app/SDLActivity;)
    09-15 20:35:15.491: D/AndroidRuntime(1898): Shutting down VM
    09-15 20:35:15.491: W/dalvikvm(1898): threadid=1: thread exiting with uncaught exception (group=0xb3e0f908)
    09-15 20:35:15.491: E/AndroidRuntime(1898): FATAL EXCEPTION: main
    09-15 20:35:15.491: E/AndroidRuntime(1898): java.lang.ExceptionInInitializerError
    09-15 20:35:15.491: E/AndroidRuntime(1898): at java.lang.Class.newInstanceImpl(Native Method)
    09-15 20:35:15.491: E/AndroidRuntime(1898): at java.lang.Class.newInstance(Class.java:1319)
    09-15 20:35:15.491: E/AndroidRuntime(1898): at android.app.Instrumentation.newActivity(Instrumentation.java:1054)
    09-15 20:35:15.491: E/AndroidRuntime(1898): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2097)
    09-15 20:35:15.491: E/AndroidRuntime(1898): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2230)
    09-15 20:35:15.491: E/AndroidRuntime(1898): at android.app.ActivityThread.access$600(ActivityThread.java:141)
    09-15 20:35:15.491: E/AndroidRuntime(1898): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1234)
    09-15 20:35:15.491: E/AndroidRuntime(1898): at android.os.Handler.dispatchMessage(Handler.java:99)
    09-15 20:35:15.491: E/AndroidRuntime(1898): at android.os.Looper.loop(Looper.java:137)
    09-15 20:35:15.491: E/AndroidRuntime(1898): at android.app.ActivityThread.main(ActivityThread.java:5039)
    09-15 20:35:15.491: E/AndroidRuntime(1898): at java.lang.reflect.Method.invokeNative(Native Method)
    09-15 20:35:15.491: E/AndroidRuntime(1898): at java.lang.reflect.Method.invoke(Method.java:511)
    09-15 20:35:15.491: E/AndroidRuntime(1898): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793)
    09-15 20:35:15.491: E/AndroidRuntime(1898): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560)
    09-15 20:35:15.491: E/AndroidRuntime(1898): at dalvik.system.NativeStart.main(Native Method)
    09-15 20:35:15.491: E/AndroidRuntime(1898): Caused by: java.lang.UnsatisfiedLinkError: Cannot load library: soinfo_relocate(linker.cpp:975): cannot locate symbol “signal” referenced by “libSDL2.so”…
    09-15 20:35:15.491: E/AndroidRuntime(1898): at java.lang.Runtime.loadLibrary(Runtime.java:371)
    09-15 20:35:15.491: E/AndroidRuntime(1898): at java.lang.System.loadLibrary(System.java:535)
    09-15 20:35:15.491: E/AndroidRuntime(1898): at org.libsdl.app.SDLActivity.(SDLActivity.java:49)
    09-15 20:35:15.491: E/AndroidRuntime(1898): … 15 more

  8. When you say, ““Copy projects into workspace”, but that messed things up for me, so you probably shouldn’t do it.” how messed up was it? I found that copying from outside the workspace was the only way to get it in. When I try to import project already in workspace, with Android > Android Project from Existing Code, it fails with “Invalid project description.” details->”mypath/MyProj overlaps the location of another project: ‘MyProj'”, despite it wanting to name it after the only Activity I implement, that which extends SDL_Activity.

    I had to copy/move it outside of the workspace and then import it as a copy. I should’ve looked at other ways to import existing code, but I like the Android way best because last time I couldn’t ctrl-tab between header and body. So this way works this far and I right click my project -> Add Android NDK Support. Now I can ctrl-tab but when I open a cpp file all references to SDL functions become red-underlined errors, despite NDK build having no worries with them.

    So it works but eclipse is not a good devlopment environment as things stand.

    Does this all sound like the consequences of copying the project in and/or using 64 bit software?

    1. It’s been a while since then, so I don’t remember the details.  It might not have copied some files (e.g. SDL source) as I expected?  You might have to rename your project files by hand before importing it if it gives you a name collision error.

      Regardless, Eclipse is kinda messy.  If you do get it working in some way, then congrats!  Once that part is over, you should be fine.  In my experience, it is normal for some things (like error highlighting in native code) not to work quite right.  You can always use command-line tools instead, and some IDEs can be configured to interface somewhat with those.

  9. When building sdl with ndk-build i get undefined reference to SDL_main in SDL_android_main.c :32. Is this a bug in SDL?

    1. Be careful of this.  SDL’s whole point is to abstract the differences between platforms.  SDL_main is not meant to be defined by you so that your code works everywhere (SDL redefines your main() for a good reason, especially on Windows).  Some platforms (like Android) don’t technically need this, but it’s a good idea to stick to main() for consistency.  If you find yourself using SDL_main, then there’s a problem that should be solved.

  10. If you get the undefined reference to SDL_main linker error you probably didn’t include SDL.h header

    so

    #include “SDL.h” in a file where main is declared (e.g. main.cpp)

  11. Hey anybody help me.
    I am new to Android programs and SDL2 I tried your example code main.c in c4droid. But nothing shows up. I put an image.bmp file also in the resources folder.

    1. You’ll have to give more info for anyone to figure it out.  SDL version, error messages, changes you’ve tried, does other rendering work, etc.  Send me an email if you still can’t get it.

  12. Android Studio was only compiling when I ran it from a shell, it turned out it wasn’t getting the ANDROID_NDK environment variable, I’d put ndk.dir in local.properties, but this wasn’t being picked up –

    Added this to the top of build.gradle and it got the settings from local.properties

    // TODO Stop using this manual property stuff when ubuntu gradle up to date
    Properties properties = new Properties()
    properties.load(project.rootProject.file(‘local.properties’).newDataInputStream())
    def sdkDir = properties.getProperty(‘sdk.dir’)
    def ndkDir = properties.getProperty(‘ndk.dir’)

    // TODO – Use these versions instead when ubuntu gradle up to date
    //def ndkDir = project.plugins.findPlugin(‘com.android.application’).getNdkFolder()
    //def sdkDir = project.plugins.findPlugin(‘com.android.application’).getSdkFolder()

  13. Anybody help me.
    Is it possible to access WiFi of our android device via SDL (SDL_net or anything) ,so that I can use it to make a group chat application.

Leave a Reply

Your email address will not be published. Required fields are marked *