README.android
author Ryan C. Gordon <icculus@icculus.org>
Wed, 03 Apr 2013 11:34:21 -0400
changeset 7050 aa1f44908402
parent 7016 24edf0189bc5
child 7166 8d17ec58f019
permissions -rw-r--r--
Fixed compiler warnings. "leftFingerDown" doesn't need to be an SDL_FingerID.
paul@4727
     1
================================================================================
paul@4726
     2
Simple DirectMedia Layer for Android
paul@4727
     3
================================================================================
paul@4725
     4
slouken@4965
     5
Requirements:
slouken@4965
     6
gabomdq@6962
     7
Android SDK (version 10 or later)
slouken@4965
     8
http://developer.android.com/sdk/index.html
slouken@4965
     9
slouken@6987
    10
Android NDK r7 or later
slouken@4965
    11
http://developer.android.com/sdk/ndk/index.html
paul@4725
    12
slouken@6987
    13
Minimum API level supported by SDL: 10 (Android 2.3.3)
slouken@4967
    14
paul@4727
    15
================================================================================
paul@4727
    16
 How the port works
paul@4727
    17
================================================================================
paul@4727
    18
paul@4727
    19
- Android applications are Java-based, optionally with parts written in C
paul@4727
    20
- As SDL apps are C-based, we use a small Java shim that uses JNI to talk to 
paul@4727
    21
the SDL library
slouken@6987
    22
- This means that your application C code must be placed inside an Android 
paul@4727
    23
Java project, along with some C support code that communicates with Java
paul@4727
    24
- This eventually produces a standard Android .apk package
paul@4727
    25
slouken@4967
    26
The Android Java code implements an "activity" and can be found in:
slouken@4967
    27
android-project/src/org/libsdl/app/SDLActivity.java
slouken@4967
    28
slouken@4967
    29
The Java code loads your game code, the SDL shared library, and
slouken@4967
    30
dispatches to native functions implemented in the SDL library:
slouken@4967
    31
src/SDL_android.cpp
slouken@4967
    32
slouken@4967
    33
Your project must include some glue code that starts your main() routine:
slouken@4967
    34
src/main/android/SDL_android_main.cpp
slouken@4967
    35
paul@4727
    36
paul@4727
    37
================================================================================
paul@4727
    38
 Building an app
paul@4727
    39
================================================================================
paul@4727
    40
paul@4725
    41
Instructions:
gabomdq@6678
    42
1. Copy the android-project directory wherever you want to keep your projects
gabomdq@6678
    43
   and rename it to the name of your project.
slouken@6631
    44
2. Move or symlink this SDL directory into the <project>/jni directory
slouken@6631
    45
3. Edit <project>/jni/src/Android.mk to include your source files
slouken@6631
    46
4. Run 'ndk-build' (a script provided by the NDK). This compiles the C source
slouken@4964
    47
slouken@4964
    48
If you want to use the Eclipse IDE, skip to the Eclipse section below.
slouken@4964
    49
slouken@7016
    50
5. Create <project>/local.properties and use that to point to the Android SDK directory, by writing a line with the following form:
slouken@7016
    51
sdk.dir=PATH_TO_ANDROID_SDK
slouken@6631
    52
6. Run 'ant debug' in android/project. This compiles the .java and eventually 
gabomdq@6678
    53
   creates a .apk with the native code embedded
slouken@6650
    54
7. 'ant debug install' will push the apk to the device or emulator (if connected)
paul@4725
    55
slouken@4965
    56
Here's an explanation of the files in the Android project, so you can customize them:
slouken@4965
    57
slouken@4965
    58
android-project/
gabomdq@6678
    59
	AndroidManifest.xml	- package manifest. Among others, it contains the class name
gabomdq@6678
    60
				  of the main activity.
slouken@4965
    61
	build.properties	- empty
gabomdq@6678
    62
	build.xml		- build description file, used by ant. The actual application name
gabomdq@6678
    63
				  is specified here.
slouken@6987
    64
	default.properties	- holds the target ABI for the application, android-10 and up
slouken@6987
    65
	project.properties	- holds the target ABI for the application, android-10 and up
slouken@4965
    66
	local.properties	- holds the SDK path, you should change this to the path to your SDK
slouken@4967
    67
	jni/			- directory holding native code
gabomdq@6678
    68
	jni/Android.mk		- Android makefile that can call recursively the Android.mk files
gabomdq@6678
    69
				  in all subdirectories
gabomdq@6678
    70
	jni/SDL/		- (symlink to) directory holding the SDL library files
slouken@4965
    71
	jni/SDL/Android.mk	- Android makefile for creating the SDL shared library
slouken@4967
    72
	jni/src/		- directory holding your C/C++ source
gabomdq@6678
    73
	jni/src/Android.mk	- Android makefile that you should customize to include your 
gabomdq@6678
    74
                                  source code and any library references
slouken@4965
    75
	res/			- directory holding resources for your application
gabomdq@6678
    76
	res/drawable-*		- directories holding icons for different phone hardware. Could be
gabomdq@6678
    77
				  one dir called "drawable".
gabomdq@6678
    78
	res/layout/main.xml	- Usually contains a file main.xml, which declares the screen layout.
gabomdq@6678
    79
				  We don't need it because we use the SDL video output.
gabomdq@6678
    80
	res/values/strings.xml	- strings used in your application, including the application name
gabomdq@6678
    81
				  shown on the phone.
gabomdq@6678
    82
	src/org/libsdl/app/SDLActivity.java - the Java class handling the initialization and binding
gabomdq@6678
    83
				  to SDL.  Be very careful changing this, as the SDL library relies
gabomdq@6678
    84
				  on this implementation.
slouken@4965
    85
slouken@4965
    86
slouken@4965
    87
================================================================================
slouken@6631
    88
 Customizing your application name
slouken@6631
    89
================================================================================
slouken@6631
    90
slouken@6631
    91
To customize your application name, edit AndroidManifest.xml and replace
slouken@6631
    92
"org.libsdl.app" with an identifier for your product package.
slouken@6631
    93
slouken@6631
    94
Then create a Java class extending SDLActivity and place it in a directory
slouken@6631
    95
under src matching your package, e.g.
slouken@6631
    96
	src/com/gamemaker/game/MyGame.java
slouken@6631
    97
slouken@6631
    98
Here's an example of a minimal class file:
slouken@6631
    99
--- MyGame.java --------------------------
slouken@6631
   100
package com.gamemaker.game;
slouken@6631
   101
slouken@6631
   102
import org.libsdl.app.SDLActivity; 
slouken@6631
   103
slouken@6631
   104
/* 
slouken@6631
   105
 * A sample wrapper class that just calls SDLActivity 
slouken@6631
   106
 */ 
slouken@6631
   107
gabomdq@6678
   108
public class MyGame extends SDLActivity { }
gabomdq@6678
   109
slouken@6631
   110
------------------------------------------
slouken@6631
   111
slouken@6631
   112
Then replace "SDLActivity" in AndroidManifest.xml with the name of your
slouken@6631
   113
class, .e.g. "MyGame"
slouken@6631
   114
slouken@6631
   115
================================================================================
slouken@6631
   116
 Customizing your application icon
slouken@6631
   117
================================================================================
slouken@6631
   118
slouken@6631
   119
Conceptually changing your icon is just replacing the icon.png files in the
gabomdq@6678
   120
drawable directories under the res directory. There are 3 directories for
gabomdq@6678
   121
different screen sizes. These can be replaced with 1 dir called 'drawable',
gabomdq@6678
   122
containing an icon file 'icon.png' with dimensions 48x48 or 72x72.
slouken@6631
   123
slouken@6631
   124
You may need to change the name of your icon in AndroidManifest.xml to match
gabomdq@6678
   125
this icon filename.
slouken@6631
   126
slouken@6631
   127
================================================================================
slouken@6646
   128
 Loading assets
slouken@6646
   129
================================================================================
slouken@6646
   130
slouken@6646
   131
Any files you put in the "assets" directory of your android-project directory
slouken@6646
   132
will get bundled into the application package and you can load them using the
slouken@6646
   133
standard functions in SDL_rwops.h.
slouken@6646
   134
slouken@6646
   135
There are also a few Android specific functions that allow you to get other
slouken@6646
   136
useful paths for saving and loading data:
slouken@6646
   137
SDL_AndroidGetInternalStoragePath()
slouken@6646
   138
SDL_AndroidGetExternalStorageState()
slouken@6646
   139
SDL_AndroidGetExternalStoragePath()
slouken@6646
   140
slouken@6646
   141
See SDL_system.h for more details on these functions.
slouken@6646
   142
gabomdq@6816
   143
The asset packaging system will, by default, compress certain file extensions.
gabomdq@6816
   144
SDL includes two asset file access mechanisms, the preferred one is the so
gabomdq@6816
   145
called "File Descriptor" method, which is faster and doesn't involve the Dalvik
gabomdq@6816
   146
GC, but given this method does not work on compressed assets, there is also the
gabomdq@6816
   147
"Input Stream" method, which is automatically used as a fall back by SDL. You
gabomdq@6816
   148
may want to keep this fact in mind when building your APK, specially when large
gabomdq@6816
   149
files are involved.
gabomdq@6816
   150
For more information on which extensions get compressed by default and how to
gabomdq@6816
   151
disable this behaviour, see for example:
gabomdq@6816
   152
    
gabomdq@6816
   153
http://ponystyle.com/blog/2010/03/26/dealing-with-asset-compression-in-android-apps/
gabomdq@6816
   154
slouken@6646
   155
================================================================================
gabomdq@6330
   156
 Pause / Resume behaviour
gabomdq@6330
   157
================================================================================
gabomdq@6330
   158
slouken@6664
   159
If SDL is compiled with SDL_ANDROID_BLOCK_ON_PAUSE defined (the default),
slouken@6664
   160
the event loop will block itself when the app is paused (ie, when the user
slouken@6664
   161
returns to the main Android dashboard). Blocking is better in terms of battery
slouken@6664
   162
use, and it allows your app to spring back to life instantaneously after resume
slouken@6664
   163
(versus polling for a resume message).
slouken@6664
   164
gabomdq@6330
   165
Upon resume, SDL will attempt to restore the GL context automatically.
gabomdq@6330
   166
In modern devices (Android 3.0 and up) this will most likely succeed and your
gabomdq@6330
   167
app can continue to operate as it was.
slouken@6664
   168
gabomdq@6330
   169
However, there's a chance (on older hardware, or on systems under heavy load),
gabomdq@6330
   170
where the GL context can not be restored. In that case you have to listen for
gabomdq@6330
   171
a specific message, (which is not yet implemented!) and restore your textures
gabomdq@6330
   172
manually or quit the app (which is actually the kind of behaviour you'll see
gabomdq@6330
   173
under iOS, if the OS can not restore your GL context it will just kill your app)
gabomdq@6330
   174
gabomdq@6330
   175
================================================================================
slouken@6987
   176
 Threads and the Java VM
gabomdq@6354
   177
================================================================================
gabomdq@6354
   178
slouken@6987
   179
For a quick tour on how Linux native threads interoperate with the Java VM, take
gabomdq@6354
   180
a look here: http://developer.android.com/guide/practices/jni.html
gabomdq@6354
   181
If you want to use threads in your SDL app, it's strongly recommended that you
gabomdq@6354
   182
do so by creating them using SDL functions. This way, the required attach/detach
gabomdq@6354
   183
handling is managed by SDL automagically. If you have threads created by other
gabomdq@6354
   184
means and they make calls to SDL functions, make sure that you call
gabomdq@6354
   185
Android_JNI_SetupThread before doing anything else otherwise SDL will attach
gabomdq@6354
   186
your thread automatically anyway (when you make an SDL call), but it'll never
gabomdq@6354
   187
detach it.
gabomdq@6354
   188
gabomdq@6354
   189
================================================================================
slouken@6631
   190
 Using STL
slouken@6631
   191
================================================================================
slouken@6631
   192
slouken@6631
   193
You can use STL in your project by creating an Application.mk file in the jni
slouken@6631
   194
folder and adding the following line:
slouken@6631
   195
APP_STL := stlport_static
slouken@6631
   196
slouken@6631
   197
For more information check out CPLUSPLUS-SUPPORT.html in the NDK documentation.
slouken@6631
   198
slouken@6631
   199
================================================================================
slouken@4965
   200
 Additional documentation
slouken@4965
   201
================================================================================
slouken@4965
   202
gabomdq@6678
   203
The documentation in the NDK docs directory is very helpful in understanding the
gabomdq@6678
   204
build process and how to work with native code on the Android platform.
slouken@4965
   205
slouken@4965
   206
The best place to start is with docs/OVERVIEW.TXT
slouken@4965
   207
paul@4725
   208
slouken@4964
   209
================================================================================
slouken@4964
   210
 Using Eclipse
slouken@4964
   211
================================================================================
slouken@4964
   212
slouken@4965
   213
First make sure that you've installed Eclipse and the Android extensions as described here:
slouken@4965
   214
	http://developer.android.com/sdk/eclipse-adt.html
slouken@4965
   215
slouken@4965
   216
Once you've copied the SDL android project and customized it, you can create an Eclipse project from it:
slouken@4965
   217
 * File -> New -> Other
slouken@4965
   218
 * Select the Android -> Android Project wizard and click Next
slouken@4965
   219
 * Enter the name you'd like your project to have
slouken@4965
   220
 * Select "Create project from existing source" and browse for your project directory
icculus@6387
   221
 * Make sure the Build Target is set to Android 2.0
slouken@4965
   222
 * Click Finish
slouken@4964
   223
slouken@4964
   224
slouken@4964
   225
================================================================================
slouken@6652
   226
 Using the emulator
slouken@4964
   227
================================================================================
slouken@4964
   228
slouken@6652
   229
There are some good tips and tricks for getting the most out of the
slouken@6652
   230
emulator here: http://developer.android.com/tools/devices/emulator.html
slouken@6652
   231
slouken@6652
   232
Especially useful is the info on setting up OpenGL ES 2.0 emulation.
slouken@4964
   233
gabomdq@6678
   234
Notice that this software emulator is incredibly slow and needs a lot of disk space.
gabomdq@6678
   235
Using a real device works better.
slouken@4964
   236
slouken@4964
   237
================================================================================
slouken@4964
   238
 Troubleshooting
slouken@4964
   239
================================================================================
slouken@4964
   240
slouken@4965
   241
You can create and run an emulator from the Eclipse IDE:
slouken@4965
   242
 * Window -> Android SDK and AVD Manager
slouken@4965
   243
slouken@4965
   244
You can see if adb can see any devices with the following command:
slouken@4965
   245
	adb devices
slouken@4965
   246
slouken@4965
   247
You can see the output of log messages on the default device with:
slouken@4965
   248
	adb logcat
slouken@4965
   249
slouken@4965
   250
You can push files to the device with:
slouken@4965
   251
	adb push local_file remote_path_and_file
slouken@4965
   252
slouken@4965
   253
You can push files to the SD Card at /sdcard, for example:
slouken@4965
   254
	adb push moose.dat /sdcard/moose.dat
paul@4727
   255
slouken@4976
   256
You can see the files on the SD card with a shell command:
slouken@4976
   257
	adb shell ls /sdcard/
slouken@4976
   258
slouken@4976
   259
You can start a command shell on the default device with:
slouken@4976
   260
	adb shell
slouken@4976
   261
gabomdq@6678
   262
You can remove the library files of your project (and not the SDL lib files) with:
slouken@4975
   263
	ndk-build clean
gabomdq@6678
   264
gabomdq@6678
   265
You can do a build with the following command:
slouken@4975
   266
	ndk-build
slouken@4975
   267
slouken@4973
   268
You can see the complete command line that ndk-build is using by passing V=1 on the command line:
slouken@4973
   269
	ndk-build V=1
slouken@4973
   270
gabomdq@6678
   271
If your application crashes in native code, you can use addr2line to convert the
gabomdq@6678
   272
addresses in the stack trace to lines in your code.
slouken@4973
   273
slouken@4973
   274
For example, if your crash looks like this:
slouken@4973
   275
I/DEBUG   (   31): signal 11 (SIGSEGV), code 2 (SEGV_ACCERR), fault addr 400085d0
slouken@4973
   276
I/DEBUG   (   31):  r0 00000000  r1 00001000  r2 00000003  r3 400085d4
slouken@4973
   277
I/DEBUG   (   31):  r4 400085d0  r5 40008000  r6 afd41504  r7 436c6a7c
slouken@4973
   278
I/DEBUG   (   31):  r8 436c6b30  r9 435c6fb0  10 435c6f9c  fp 4168d82c
slouken@4973
   279
I/DEBUG   (   31):  ip 8346aff0  sp 436c6a60  lr afd1c8ff  pc afd1c902  cpsr 60000030
slouken@4973
   280
I/DEBUG   (   31):          #00  pc 0001c902  /system/lib/libc.so
slouken@4973
   281
I/DEBUG   (   31):          #01  pc 0001ccf6  /system/lib/libc.so
slouken@4973
   282
I/DEBUG   (   31):          #02  pc 000014bc  /data/data/org.libsdl.app/lib/libmain.so
slouken@4973
   283
I/DEBUG   (   31):          #03  pc 00001506  /data/data/org.libsdl.app/lib/libmain.so
slouken@4973
   284
gabomdq@6678
   285
You can see that there's a crash in the C library being called from the main code.
gabomdq@6678
   286
I run addr2line with the debug version of my code:
slouken@4973
   287
	arm-eabi-addr2line -C -f -e obj/local/armeabi/libmain.so
slouken@4973
   288
and then paste in the number after "pc" in the call stack, from the line that I care about:
slouken@4973
   289
000014bc
slouken@4973
   290
slouken@4973
   291
I get output from addr2line showing that it's in the quit function, in testspriteminimal.c, on line 23.
slouken@4973
   292
slouken@4973
   293
You can add logging to your code to help show what's happening:
slouken@4973
   294
slouken@4973
   295
#include <android/log.h>
slouken@4973
   296
slouken@4973
   297
	__android_log_print(ANDROID_LOG_INFO, "foo", "Something happened! x = %d", x);
slouken@4973
   298
gabomdq@6678
   299
If you need to build without optimization turned on, you can create a file called
gabomdq@6678
   300
"Application.mk" in the jni directory, with the following line in it:
slouken@4974
   301
APP_OPTIM := debug
slouken@4974
   302
paul@4727
   303
paul@4727
   304
================================================================================
slouken@6650
   305
 Memory debugging
slouken@6650
   306
================================================================================
slouken@6650
   307
slouken@6650
   308
The best (and slowest) way to debug memory issues on Android is valgrind.
slouken@6650
   309
Valgrind has support for Android out of the box, just grab code using:
slouken@6650
   310
	svn co svn://svn.valgrind.org/valgrind/trunk valgrind
slouken@6650
   311
... and follow the instructions in the file README.android to build it.
slouken@6650
   312
slouken@6650
   313
One thing I needed to do on Mac OS X was change the path to the toolchain,
slouken@6650
   314
and add ranlib to the environment variables:
slouken@6650
   315
export RANLIB=$NDKROOT/toolchains/arm-linux-androideabi-4.4.3/prebuilt/darwin-x86/bin/arm-linux-androideabi-ranlib
slouken@6650
   316
slouken@6650
   317
Once valgrind is built, you can create a wrapper script to launch your
slouken@6650
   318
application with it, changing org.libsdl.app to your package identifier:
slouken@6650
   319
--- start_valgrind_app -------------------
slouken@6650
   320
#!/system/bin/sh
slouken@6650
   321
export TMPDIR=/data/data/org.libsdl.app
slouken@6650
   322
exec /data/local/Inst/bin/valgrind --log-file=/sdcard/valgrind.log --error-limit=no $*
slouken@6650
   323
------------------------------------------
slouken@6650
   324
slouken@6650
   325
Then push it to the device:
slouken@6650
   326
	adb push start_valgrind_app /data/local
slouken@6650
   327
slouken@6650
   328
and make it executable:
slouken@6650
   329
	adb shell chmod 755 /data/local/start_valgrind_app
slouken@6650
   330
slouken@6650
   331
and tell Android to use the script to launch your application:
slouken@6650
   332
	adb shell setprop wrap.org.libsdl.app "logwrapper /data/local/start_valgrind_app"
slouken@6650
   333
slouken@6650
   334
If the setprop command says "could not set property", it's likely that
slouken@6650
   335
your package name is too long and you should make it shorter by changing
slouken@6650
   336
AndroidManifest.xml and the path to your class file in android-project/src
slouken@6650
   337
slouken@6650
   338
You can then launch your application normally and waaaaaaaiiittt for it.
slouken@6650
   339
You can monitor the startup process with the logcat command above, and
slouken@6650
   340
when it's done (or even while it's running) you can grab the valgrind
slouken@6650
   341
output file:
slouken@6650
   342
	adb pull /sdcard/valgrind.log
slouken@6650
   343
slouken@6650
   344
When you're done instrumenting with valgrind, you can disable the wrapper:
slouken@6650
   345
	adb shell setprop wrap.org.libsdl.app ""
slouken@6650
   346
gabomdq@6962
   347
================================================================================
gabomdq@6962
   348
 Why is API level 10 the minimum required?
gabomdq@6962
   349
================================================================================
gabomdq@6962
   350
slouken@6987
   351
API level 10 is required because SDL requires some functionality for running not
slouken@6987
   352
available on older devices and some for building which is not in older NDK/SDKs.
slouken@6987
   353
slouken@6987
   354
Support for native OpenGL ES and ES2 applications was introduced in the NDK for
slouken@6987
   355
API level 4 and 8. EGL was made a stable API in the NDK for API level 9, which
slouken@6987
   356
has since then been obsoleted, with the recommendation to developers to bump the
slouken@6987
   357
required API level to 10.
gabomdq@6962
   358
As of this writing, according to http://developer.android.com/about/dashboards/index.html
slouken@6987
   359
about 90% of the Android devices accessing Google Play support API level 10 or
slouken@6987
   360
higher (March 2013).
slouken@6650
   361
slouken@6650
   362
================================================================================
gabomdq@6979
   363
 A note regarding the use of the "dirty rectangles" rendering technique
gabomdq@6979
   364
================================================================================
gabomdq@6979
   365
gabomdq@6979
   366
If your app uses a variation of the "dirty rectangles" rendering technique,
gabomdq@6979
   367
where you only update a portion of the screen on each frame, you may notice a
gabomdq@6979
   368
variety of visual glitches on Android, that are not present on other platforms.
gabomdq@6979
   369
This is caused by SDL's use of EGL as the support system to handle OpenGL ES/ES2
gabomdq@6979
   370
contexts, in particular the use of the eglSwapBuffers function. As stated in the
gabomdq@6979
   371
documentation for the function "The contents of ancillary buffers are always 
gabomdq@6979
   372
undefined after calling eglSwapBuffers".
gabomdq@6979
   373
Setting the EGL_SWAP_BEHAVIOR attribute of the surface to EGL_BUFFER_PRESERVED
gabomdq@6979
   374
is not possible for SDL as it requires EGL 1.4, available only on the API level
gabomdq@6979
   375
17+, so the only workaround available on this platform is to redraw the entire
gabomdq@6979
   376
screen each frame.
gabomdq@6979
   377
gabomdq@6979
   378
Reference: http://www.khronos.org/registry/egl/specs/EGLTechNote0001.html
gabomdq@6979
   379
gabomdq@6979
   380
================================================================================
paul@4727
   381
 Known issues
paul@4727
   382
================================================================================
paul@4727
   383
paul@4727
   384
- TODO. I'm sure there's a bunch more stuff I haven't thought of