Building on Android with a Single Batch File

Hey guys, I've been working on trying to setup a similar build system to handmade hero with android, where you have a platform layer that loads in your game code and build all of it using a single batch file. As far as I understand how Android works, if you want to build native code, you can either write your own java layer that loads the library and calls main, or you can create a native activity in your AndroidManifest.xml and just have it point to the platform code you compiled as a shared library (.so). I started off by trying to compile some of googles example code (http://brian.io/android-ndk-r10c-...mples_sample--nativeactivity.html) and then debug it in visual studio (you can create a vs solution for a .apk the same way you can for a .exe).

The way my build works is first, it compiles my code into a .so. We then create a apk and add the .so to the apk. We then sign the .apk with a debug key, zip it and at that point, its ready to be tested. Note that for all of this, I'm trying not to use ant or the other build systems in my build.bat, just straight ndk and the commands used to generate apk and keystores. I got my project to be successfully installed on two vs android emulators and it ran properly on one of them. My problem is that in both cases, visual studio couldn't debug the actual code and instead, gave me this error message: "Unable to start debugging. Android command run-as failed. Package com.example.native_activity is not debuggable". I looked all over online and I made sure that my ndk builds in debug mode, and that my key is a debug key but I couldn't find an answer. My manifest file also has debuggable set to true. I use the android_native_glue static library when building my code if that matters. Here are my files:

AndroidManifest.xml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<?xml version="1.0" encoding="utf-8"?>
<!-- BEGIN_INCLUDE(manifest) -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.example.native_activity"
        android:versionCode="1"
        android:versionName="1.0"
        android:debuggable="true">

    <!-- This is the platform API where NativeActivity was introduced. -->
    <uses-sdk android:minSdkVersion="9" />

    <!-- This .apk has no Java code itself, so set hasCode to false. -->
    <application android:label="@string/app_name" android:hasCode="false">

        <!-- Our activity is the built-in NativeActivity framework class.
             This will take care of integrating with our NDK code. -->
        <activity android:name="android.app.NativeActivity"
                android:label="@string/app_name"
                android:configChanges="orientation|keyboardHidden">
            <!-- Tell NativeActivity the name of or .so -->
            <meta-data android:name="android.app.lib_name"
                    android:value="native-activity" />
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest> 
<!-- END_INCLUDE(manifest) -->


Android.mk
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := native-activity
LOCAL_SRC_FILES := main.c
LOCAL_LDLIBS    := -llog -landroid -lEGL -lGLESv1_CM
LOCAL_STATIC_LIBRARIES := -L$(LOCAL_PATH)/glue android_native_app_glue

include $(BUILD_SHARED_LIBRARY)

$(call import-module,android/native_app_glue)


Application.mk
1
2
APP_ABI := x86
APP_PLATFORM := android-9


build.bat
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@echo off

set CodeDir=W:\untitled\code
set OutputDir=W:\untitled\build_android
set AndroidDir=%ProgramFiles(x86)%\Android\android-sdk
set AndroidCmdDir=%AndroidDir%\build-tools\21.1.2

call ndk-build -B NDK_DEBUG=1 APP_BUILD_SCRIPT=%CodeDir%\android\Android.mk NDK_APPLICATION_MK=%CodeDir%\android\Application.mk -C %CodeDir%\android NDK_PROJECT_PATH=%CodeDir%\android NDK_LIBS_OUT=%OutputDir%\lib NDK_OUT=%OutputDir%\obj

REM Create Keystore for signing our apk
REM call keytool -genkey -v -keystore %OutputDir%\debug.keystore -storepass android -alias androiddebugkey -dname "filled in with the relevant arguments" -keyalg RSA -keysize 2048 -validity 20000

pushd %OutputDir%
del *.apk >NUL 2> NUL
popd

REM Create APK file
call "%AndroidCmdDir%\aapt" package -v -f -M %CodeDir%\android\AndroidManifest.xml -S %CodeDir%\android\res -I "%AndroidDir%/platforms/android-19/android.jar" -F %OutputDir%\AndroidTest.unsigned.apk %OutputDir%
call "%AndroidCmdDir%\aapt" add W:\untitled\build_android\AndroidTest.unsigned.apk W:\untitled\build_android\lib\x86\libnative-activity.so

REM Sign the apk with our keystore
call jarsigner -sigalg SHA1withRSA -digestalg SHA1 -storepass android -keypass android -keystore %OutputDir%\debug.keystore -signedjar %OutputDir%\AndroidTest.signed.apk %OutputDir%\AndroidTest.unsigned.apk androiddebugkey

"%AndroidCmdDir%\zipalign" -v 4 %OutputDir%\AndroidTest.signed.apk %OutputDir%\AndroidTest.aligned.apk


The key generating command is commented out because I had it generate a key once and then I use that same key for all the future apk builds instead of regenerating a key each time. If anyone knows how to get fix my apk and make it debuggable for visual studio, please let me know.

Edited by HawYeah on
Cool, you are doing almost exactly same thing I do with building Android stuff! :)

The problem is that you have android:debuggable set for wrong XML tag. It should be specified for "application" tag, not "manifest". See here: https://developer.android.com/gui...st/application-element.html#debug

Alternative is to use --debug-mode for aapt when you call it for "package" - it will automatically insert this attribute for application.

I think you should move build files (.o) out from output directory. Otherwise "aapt package will add "obj" folder in apk file and you don't need it. It also takes debug.keystore file and puts in apk file.


Edited by Mārtiņš Možeiko on
Interesting - you guys are writing your Android apps in pure C?
If so, can you estimate how much extra work compared to using Java? (I guess most C programmers can learn Java reasonably quickly).

Edited by hugo on
What do you mean by extra work? For me it would be actually extra work to write Java code. Writing C code is no extra work for me, because I like C and C++. And this works very well, especially if you do cross-platform development where you want to use same game code for multiple platforms (like Handmade Hero will).

Edited by Mārtiņš Možeiko on
mmozeiko
Cool, you are doing almost exactly same thing I do with building Android stuff! :)

The problem is that you have android:debuggable set for wrong XML tag. It should be specified for "application" tag, not "manifest". See here: https://developer.android.com/gui...st/application-element.html#debug

Alternative is to use --debug-mode for aapt when you call it for "package" - it will automatically insert this attribute for application.

I think you should move build files (.o) out from output directory. Otherwise "aapt package will add "obj" folder in apk file and you don't need it. It also takes debug.keystore file and puts in apk file.



Wow of course its something that easy. Thanks a lot. I ended up adding the debug-mode tag to the aapt call and the debugging error in visual studio went away! Thanks for the comment on build files being added to the apk. I did a bunch of testing and it seems like the apk wants the .so files to be in a specific path (eg: lib\x86\lib.so) so I just made aapt look for files in that folder instead of in the whole OutputDir. This is what my batch file currently looks like:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
@echo off

set CodeDir=W:\untitled\code
set OutputDir=W:\untitled\build_android
set AndroidDir=%ProgramFiles(x86)%\Android\android-sdk
set AndroidCmdDir=%AndroidDir%\build-tools\21.1.2

set NdkBuildCommands=-B NDK_DEBUG=1 %NdkBuildCommands%
set NdkBuildCommands=APP_BUILD_SCRIPT=%CodeDir%\android\Android.mk %NdkBuildCommands%
set NdkBuildCommands=NDK_APPLICATION_MK=%CodeDir%\android\Application.mk %NdkBuildCommands%
set NdkBuildCommands=-C %CodeDir%\android %NdkBuildCommands%
set NdkBuildCommands=NDK_PROJECT_PATH=%CodeDir%\android %NdkBuildCommands%
set NdkBuildCommands=NDK_LIBS_OUT=%OutputDir%\lib\lib %NdkBuildCommands%
set NdkBuildCommands=NDK_OUT=%OutputDir%\obj %NdkBuildCommands%

call ndk-build %NdkBuildCommands%

REM Create Keystore for signing our apk
REM call keytool -genkey -v -keystore %OutputDir%\debug.keystore -storepass android -alias androiddebugkey -dname "CN=company name, OU=0, O=Dream, L=Toronto, S=Ontario, C=CA" -keyalg RSA -keysize 2048 -validity 20000

pushd %OutputDir%
del *.apk >NUL 2> NUL
popd

REM Create APK file
call "%AndroidCmdDir%\aapt" package --debug-mode -f -M %CodeDir%\android\AndroidManifest.xml -S %CodeDir%\android\res -I "%AndroidDir%/platforms/android-19/android.jar" -F %OutputDir%\AndroidTest.unsigned.apk %OutputDir%\lib
call jarsigner -sigalg SHA1withRSA -digestalg SHA1 -storepass android -keypass android -keystore %OutputDir%\debug.keystore -signedjar %OutputDir%\AndroidTest.signed.apk %OutputDir%\AndroidTest.unsigned.apk androiddebugkey
"%AndroidCmdDir%\zipalign" 4 %OutputDir%\AndroidTest.signed.apk %OutputDir%\AndroidTest.aligned.apk


I tried setting a breakpoint in my app but it says that the module hasn't been loaded or the breakpoint address could not be found so the break point is never hit. If I break all, it seems to be executing libc.so code so visual studio cannot find the symbols. The app itself runs properly on the emulator I'm testing so is this just a visual studio problem? I added the extra symbols path to include the path where my main.c is so visual studio should know where my code for the android program is.

Hugo - whatever code you make in straight C on android for the platform layer will probably be similar to what you write in Java anyways so if anything, writing everything in one language is more consistent and easier to maintain.

EDIT: It seems that for visual studio to find your symbols, you have to set the additional symbols path to your obj folder instead of your code folder. Doing this allowed me to step through my code successfully but if I ever called break all, it would still say that the symbols for libc.so could not be loaded. I guess I'll research that error a bit more but for now, I can actually debug the app and start building my own. Thanks a lot mmozeiko for the help!!!

Edited by HawYeah on
For libc.so you won't see any internal symbols, because libc.so doesn't have any debug info on device. gdb (that VS uses) will show only export symbols (like malloc, select, epoll_wait, etc...).

Edited by Mārtiņš Možeiko on
Thanks guys. I didn't mean only in the games context, but other apps as well :)
For regular apps you are probably better with Java code. Because NDK has limited functionality - you won't be able to use a lot of Android API. Especially those which need you to subclass some interface (you cannot do that using just C). Calling existing Java methods works fine though, although code gets very very verbose. What is one line in Java could turn out tenths of lines in C, add there error handling and you'll have much much more lines of code. Also performance will probably suffer - transitioning from C to Java and other way costs a lot. Writing everything in Java makes JIT to optimize a lot of small things which cannot be done across JNI calls.

NDK works best when you can render everything yourself using OpenGL ES. For regular apps it is expected that they'll have same UI style as rest of Android.

Edited by Mārtiņš Možeiko on