123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432 |
- ================================================================================
- Simple DirectMedia Layer for Android
- ================================================================================
- Requirements:
- Android SDK (version 10 or later)
- http://developer.android.com/sdk/index.html
- Android NDK r7 or later
- http://developer.android.com/sdk/ndk/index.html
- Minimum API level supported by SDL: 10 (Android 2.3.3)
- ================================================================================
- How the port works
- ================================================================================
- - Android applications are Java-based, optionally with parts written in C
- - As SDL apps are C-based, we use a small Java shim that uses JNI to talk to
- the SDL library
- - This means that your application C code must be placed inside an Android
- Java project, along with some C support code that communicates with Java
- - This eventually produces a standard Android .apk package
- The Android Java code implements an "Activity" and can be found in:
- android-project/src/org/libsdl/app/SDLActivity.java
- The Java code loads your game code, the SDL shared library, and
- dispatches to native functions implemented in the SDL library:
- src/SDL_android.c
- Your project must include some glue code that starts your main() routine:
- src/main/android/SDL_android_main.c
- ================================================================================
- Building an app
- ================================================================================
- For simple projects you can use the script located at build-scripts/androidbuild.sh
- There's two ways of using it:
- androidbuild.sh com.yourcompany.yourapp < sources.list
- androidbuild.sh com.yourcompany.yourapp source1.c source2.c ...sourceN.c
- sources.list should be a text file with a source file name in each line
- Filenames should be specified relative to the current directory, for example if
- you are in the build-scripts directory and want to create the testgles.c test, you'll
- run:
-
- ./androidbuild.sh org.libsdl.testgles ../test/testgles.c
- One limitation of this script is that all sources provided will be aggregated into
- a single directory, thus all your source files should have a unique name.
- Once the project is complete the script will tell you where the debug APK is located.
- If you want to create a signed release APK, you can use the project created by this
- utility to generate it.
- Finally, a word of caution: re running androidbuild.sh wipes any changes you may have
- done in the build directory for the app!
- For more complex projects, follow these instructions:
-
- 1. Copy the android-project directory wherever you want to keep your projects
- and rename it to the name of your project.
- 2. Move or symlink this SDL directory into the <project>/jni directory
- 3. Edit <project>/jni/src/Android.mk to include your source files
- 4. Run 'ndk-build' (a script provided by the NDK). This compiles the C source
- If you want to use the Eclipse IDE, skip to the Eclipse section below.
- 5. Create <project>/local.properties and use that to point to the Android SDK directory, by writing a line with the following form:
- sdk.dir=PATH_TO_ANDROID_SDK
- 6. Run 'ant debug' in android/project. This compiles the .java and eventually
- creates a .apk with the native code embedded
- 7. 'ant debug install' will push the apk to the device or emulator (if connected)
- Here's an explanation of the files in the Android project, so you can customize them:
- android-project/
- AndroidManifest.xml - package manifest. Among others, it contains the class name
- of the main Activity and the package name of the application.
- build.properties - empty
- build.xml - build description file, used by ant. The actual application name
- is specified here.
- default.properties - holds the target ABI for the application, android-10 and up
- project.properties - holds the target ABI for the application, android-10 and up
- local.properties - holds the SDK path, you should change this to the path to your SDK
- jni/ - directory holding native code
- jni/Android.mk - Android makefile that can call recursively the Android.mk files
- in all subdirectories
- jni/SDL/ - (symlink to) directory holding the SDL library files
- jni/SDL/Android.mk - Android makefile for creating the SDL shared library
- jni/src/ - directory holding your C/C++ source
- jni/src/Android.mk - Android makefile that you should customize to include your
- source code and any library references
- res/ - directory holding resources for your application
- res/drawable-* - directories holding icons for different phone hardware. Could be
- one dir called "drawable".
- res/layout/main.xml - Usually contains a file main.xml, which declares the screen layout.
- We don't need it because we use the SDL video output.
- res/values/strings.xml - strings used in your application, including the application name
- shown on the phone.
- src/org/libsdl/app/SDLActivity.java - the Java class handling the initialization and binding
- to SDL. Be very careful changing this, as the SDL library relies
- on this implementation.
- ================================================================================
- Build an app with static linking of libSDL
- ================================================================================
- This build uses the Android NDK module system.
- Instructions:
- 1. Copy the android-project directory wherever you want to keep your projects
- and rename it to the name of your project.
- 2. Rename <project>/jni/src/Android_static.mk to <project>/jni/src/Android.mk
- (overwrite the existing one)
- 3. Edit <project>/jni/src/Android.mk to include your source files
- 4. create and export an environment variable named NDK_MODULE_PATH that points
- to the parent directory of this SDL directory. e.g.:
- export NDK_MODULE_PATH="$PWD"/..
- 5. Edit <project>/src/org/libsdl/app/SDLActivity.java and remove the call to
- System.loadLibrary("SDL2") line 42.
- 6. Run 'ndk-build' (a script provided by the NDK). This compiles the C source
- ================================================================================
- Customizing your application name
- ================================================================================
- To customize your application name, edit AndroidManifest.xml and replace
- "org.libsdl.app" with an identifier for your product package.
- Then create a Java class extending SDLActivity and place it in a directory
- under src matching your package, e.g.
- src/com/gamemaker/game/MyGame.java
- Here's an example of a minimal class file:
- --- MyGame.java --------------------------
- package com.gamemaker.game;
- import org.libsdl.app.SDLActivity;
- /*
- * A sample wrapper class that just calls SDLActivity
- */
- public class MyGame extends SDLActivity { }
- ------------------------------------------
- Then replace "SDLActivity" in AndroidManifest.xml with the name of your
- class, .e.g. "MyGame"
- ================================================================================
- Customizing your application icon
- ================================================================================
- Conceptually changing your icon is just replacing the "ic_launcher.png" files in
- the drawable directories under the res directory. There are four directories for
- different screen sizes. These can be replaced with one dir called "drawable",
- containing an icon file "ic_launcher.png" with dimensions 48x48 or 72x72.
- You may need to change the name of your icon in AndroidManifest.xml to match
- this icon filename.
- ================================================================================
- Loading assets
- ================================================================================
- Any files you put in the "assets" directory of your android-project directory
- will get bundled into the application package and you can load them using the
- standard functions in SDL_rwops.h.
- There are also a few Android specific functions that allow you to get other
- useful paths for saving and loading data:
- SDL_AndroidGetInternalStoragePath()
- SDL_AndroidGetExternalStorageState()
- SDL_AndroidGetExternalStoragePath()
- See SDL_system.h for more details on these functions.
- The asset packaging system will, by default, compress certain file extensions.
- SDL includes two asset file access mechanisms, the preferred one is the so
- called "File Descriptor" method, which is faster and doesn't involve the Dalvik
- GC, but given this method does not work on compressed assets, there is also the
- "Input Stream" method, which is automatically used as a fall back by SDL. You
- may want to keep this fact in mind when building your APK, specially when large
- files are involved.
- For more information on which extensions get compressed by default and how to
- disable this behaviour, see for example:
-
- http://ponystyle.com/blog/2010/03/26/dealing-with-asset-compression-in-android-apps/
- ================================================================================
- Pause / Resume behaviour
- ================================================================================
- If SDL is compiled with SDL_ANDROID_BLOCK_ON_PAUSE defined (the default),
- the event loop will block itself when the app is paused (ie, when the user
- returns to the main Android dashboard). Blocking is better in terms of battery
- use, and it allows your app to spring back to life instantaneously after resume
- (versus polling for a resume message).
- Upon resume, SDL will attempt to restore the GL context automatically.
- In modern devices (Android 3.0 and up) this will most likely succeed and your
- app can continue to operate as it was.
- However, there's a chance (on older hardware, or on systems under heavy load),
- where the GL context can not be restored. In that case you have to listen for
- a specific message, (which is not yet implemented!) and restore your textures
- manually or quit the app (which is actually the kind of behaviour you'll see
- under iOS, if the OS can not restore your GL context it will just kill your app)
- ================================================================================
- Threads and the Java VM
- ================================================================================
- For a quick tour on how Linux native threads interoperate with the Java VM, take
- a look here: http://developer.android.com/guide/practices/jni.html
- If you want to use threads in your SDL app, it's strongly recommended that you
- do so by creating them using SDL functions. This way, the required attach/detach
- handling is managed by SDL automagically. If you have threads created by other
- means and they make calls to SDL functions, make sure that you call
- Android_JNI_SetupThread before doing anything else otherwise SDL will attach
- your thread automatically anyway (when you make an SDL call), but it'll never
- detach it.
- ================================================================================
- Using STL
- ================================================================================
- You can use STL in your project by creating an Application.mk file in the jni
- folder and adding the following line:
- APP_STL := stlport_static
- For more information check out CPLUSPLUS-SUPPORT.html in the NDK documentation.
- ================================================================================
- Additional documentation
- ================================================================================
- The documentation in the NDK docs directory is very helpful in understanding the
- build process and how to work with native code on the Android platform.
- The best place to start is with docs/OVERVIEW.TXT
- ================================================================================
- Using Eclipse
- ================================================================================
- First make sure that you've installed Eclipse and the Android extensions as described here:
- http://developer.android.com/sdk/eclipse-adt.html
- Once you've copied the SDL android project and customized it, you can create an Eclipse project from it:
- * File -> New -> Other
- * Select the Android -> Android Project wizard and click Next
- * Enter the name you'd like your project to have
- * Select "Create project from existing source" and browse for your project directory
- * Make sure the Build Target is set to Android 2.0
- * Click Finish
- ================================================================================
- Using the emulator
- ================================================================================
- There are some good tips and tricks for getting the most out of the
- emulator here: http://developer.android.com/tools/devices/emulator.html
- Especially useful is the info on setting up OpenGL ES 2.0 emulation.
- Notice that this software emulator is incredibly slow and needs a lot of disk space.
- Using a real device works better.
- ================================================================================
- Troubleshooting
- ================================================================================
- You can create and run an emulator from the Eclipse IDE:
- * Window -> Android SDK and AVD Manager
- You can see if adb can see any devices with the following command:
- adb devices
- You can see the output of log messages on the default device with:
- adb logcat
- You can push files to the device with:
- adb push local_file remote_path_and_file
- You can push files to the SD Card at /sdcard, for example:
- adb push moose.dat /sdcard/moose.dat
- You can see the files on the SD card with a shell command:
- adb shell ls /sdcard/
- You can start a command shell on the default device with:
- adb shell
- You can remove the library files of your project (and not the SDL lib files) with:
- ndk-build clean
- You can do a build with the following command:
- ndk-build
- You can see the complete command line that ndk-build is using by passing V=1 on the command line:
- ndk-build V=1
- If your application crashes in native code, you can use addr2line to convert the
- addresses in the stack trace to lines in your code.
- For example, if your crash looks like this:
- I/DEBUG ( 31): signal 11 (SIGSEGV), code 2 (SEGV_ACCERR), fault addr 400085d0
- I/DEBUG ( 31): r0 00000000 r1 00001000 r2 00000003 r3 400085d4
- I/DEBUG ( 31): r4 400085d0 r5 40008000 r6 afd41504 r7 436c6a7c
- I/DEBUG ( 31): r8 436c6b30 r9 435c6fb0 10 435c6f9c fp 4168d82c
- I/DEBUG ( 31): ip 8346aff0 sp 436c6a60 lr afd1c8ff pc afd1c902 cpsr 60000030
- I/DEBUG ( 31): #00 pc 0001c902 /system/lib/libc.so
- I/DEBUG ( 31): #01 pc 0001ccf6 /system/lib/libc.so
- I/DEBUG ( 31): #02 pc 000014bc /data/data/org.libsdl.app/lib/libmain.so
- I/DEBUG ( 31): #03 pc 00001506 /data/data/org.libsdl.app/lib/libmain.so
- You can see that there's a crash in the C library being called from the main code.
- I run addr2line with the debug version of my code:
- arm-eabi-addr2line -C -f -e obj/local/armeabi/libmain.so
- and then paste in the number after "pc" in the call stack, from the line that I care about:
- 000014bc
- I get output from addr2line showing that it's in the quit function, in testspriteminimal.c, on line 23.
- You can add logging to your code to help show what's happening:
- #include <android/log.h>
- __android_log_print(ANDROID_LOG_INFO, "foo", "Something happened! x = %d", x);
- If you need to build without optimization turned on, you can create a file called
- "Application.mk" in the jni directory, with the following line in it:
- APP_OPTIM := debug
- ================================================================================
- Memory debugging
- ================================================================================
- The best (and slowest) way to debug memory issues on Android is valgrind.
- Valgrind has support for Android out of the box, just grab code using:
- svn co svn://svn.valgrind.org/valgrind/trunk valgrind
- ... and follow the instructions in the file README.android to build it.
- One thing I needed to do on Mac OS X was change the path to the toolchain,
- and add ranlib to the environment variables:
- export RANLIB=$NDKROOT/toolchains/arm-linux-androideabi-4.4.3/prebuilt/darwin-x86/bin/arm-linux-androideabi-ranlib
- Once valgrind is built, you can create a wrapper script to launch your
- application with it, changing org.libsdl.app to your package identifier:
- --- start_valgrind_app -------------------
- #!/system/bin/sh
- export TMPDIR=/data/data/org.libsdl.app
- exec /data/local/Inst/bin/valgrind --log-file=/sdcard/valgrind.log --error-limit=no $*
- ------------------------------------------
- Then push it to the device:
- adb push start_valgrind_app /data/local
- and make it executable:
- adb shell chmod 755 /data/local/start_valgrind_app
- and tell Android to use the script to launch your application:
- adb shell setprop wrap.org.libsdl.app "logwrapper /data/local/start_valgrind_app"
- If the setprop command says "could not set property", it's likely that
- your package name is too long and you should make it shorter by changing
- AndroidManifest.xml and the path to your class file in android-project/src
- You can then launch your application normally and waaaaaaaiiittt for it.
- You can monitor the startup process with the logcat command above, and
- when it's done (or even while it's running) you can grab the valgrind
- output file:
- adb pull /sdcard/valgrind.log
- When you're done instrumenting with valgrind, you can disable the wrapper:
- adb shell setprop wrap.org.libsdl.app ""
- ================================================================================
- Why is API level 10 the minimum required?
- ================================================================================
- API level 10 is required because SDL requires some functionality for running not
- available on older devices and some for building which is not in older NDK/SDKs.
- Support for native OpenGL ES and ES2 applications was introduced in the NDK for
- API level 4 and 8. EGL was made a stable API in the NDK for API level 9, which
- has since then been obsoleted, with the recommendation to developers to bump the
- required API level to 10.
- As of this writing, according to http://developer.android.com/about/dashboards/index.html
- about 90% of the Android devices accessing Google Play support API level 10 or
- higher (March 2013).
- ================================================================================
- A note regarding the use of the "dirty rectangles" rendering technique
- ================================================================================
- If your app uses a variation of the "dirty rectangles" rendering technique,
- where you only update a portion of the screen on each frame, you may notice a
- variety of visual glitches on Android, that are not present on other platforms.
- This is caused by SDL's use of EGL as the support system to handle OpenGL ES/ES2
- contexts, in particular the use of the eglSwapBuffers function. As stated in the
- documentation for the function "The contents of ancillary buffers are always
- undefined after calling eglSwapBuffers".
- Setting the EGL_SWAP_BEHAVIOR attribute of the surface to EGL_BUFFER_PRESERVED
- is not possible for SDL as it requires EGL 1.4, available only on the API level
- 17+, so the only workaround available on this platform is to redraw the entire
- screen each frame.
- Reference: http://www.khronos.org/registry/egl/specs/EGLTechNote0001.html
- ================================================================================
- Known issues
- ================================================================================
- - TODO. I'm sure there's a bunch more stuff I haven't thought of
|