Wednesday, August 04, 2010

HACK: Adding JAR & JNI Libraries to a NBAndroid Project

In general, the Android Plugin for Netbeans (NBAndroid) is a great alternative to the the official ADT Plugin for Eclipse provided by Google. However, as of the timing writing it has two annoying bugs: it does neither support Java nor native libraries to be installed with the application package (.apk) on the phone. Fortunately, there is an easy workaround for both:

1. Java Libraries

Actually, this already works partly as adding .jar files using the GUI allows compilation of dependent code. The problem is that the contained .class files are not converted and bundled. The result is a ClassNotFoundException when running the application. Overriding the "-dex" target in the build.xml does the job:

<target depends="init,compile,-pre-pre-jar,-pre-jar" name="-dex">
    <pathconvert pathsep=" " property="javac.classpath.with.spaces">
        <path path="${javac.classpath}"/>
    </pathconvert>
    <exec executable="${dx}" failonerror="true">
        <arg value="--dex"/>
        <arg value="--output=${basedir}/${intermediate.dex}"/>
        <arg value="--positions=lines"/>
        <arg file="${build.classes.dir}"/>
        <arg line="${javac.classpath.with.spaces}"/>
    </exec>
</target>
Source: This workaround is actually a re-post of Andrey Moiseev's comment on the bug report.

2. Native Libraries

Native libraries are usually found below an optional libs folder in the project directory, e.g. "libs/armeabi/libFoo.so". While some build scripts automatically add the contents to the .apk file, the one generated by the NBAndroid plug-in unfortunately does not. Again the solution is overriding a target by adding the following to the build.xml (requries an exisiting libs directory).

<target depends="init,-package-res-and-assets,-package-res-no-assets,-package-dex" name="-sign">
    <exec executable="${apkbuilder}" failonerror="true">
        <arg value="${basedir}/${dist.apk}"/>
        <arg value="-z"/>
        <arg value="${basedir}/${dist.apk}_"/>
        <arg value="-nf"/>
        <arg value="${basedir}/libs"/>
    </exec>
    <delete file="${dist.apk}_"/>
</target>

Workaround: Using Vertex-Buffer-Objects (VBOs) with OpenGL ES 2.0 on Android 2.2 (Froyo)

Starting with the Android 2.2 SDK the OpenGL ES 2.0 functionality is finally available. Unfortunately however, you can't really use vertex buffer objets (VBO) with it at the moment. The problem is that the class GLES20 is missing (overloaded) variants of glVertexArrtribPointer and glDrawElements with the last parameter of type int (instead of Buffer). These are required if a buffer object is bound to the VERTEX_ARRAY target or VERTEX_ELEMENT_ARRAY respectively.

The good news is that the Android graphics team already checked in a fix. However, because it requires API changes, they won't be able to push it until there is another system update. (Thanks to Chris Pruett for the info!)

For the the mean time, I wrote a few lines of code enabling the missing functionality through two Java-Native-Interface (JNI) calls (fix-GLES20.zip). In order to use it, you need to:

  1. Copy the zip file's content to your Android project folder.
  2. Download and install(unzip) the Native Development Kit (NDK).
  3. Run "$ANDROID_NDK/ndk-build -C %PATH_TO_PROJECT%"
This will create the file "libs/armeabi/libfix-GLES20.so" in your project's folder. Actually, this is all you need in addition to the file "src/fix/android/opengl/GLES20.java". Now you can add "import static fix.android.opengl.GLES20.glVertexAttribPointer|glDrawElements;" to your java sources and use the missing functionality.

Note: The file "libs/armeabi/libfix-GLES20.so" should get included in the application package (.apk) automatically with most build scripts. You can print and verify the content it with: "$jar -rf %PATH_TO_APK_FILE%". If for some reason the file is not listed, simply make a copy of the .apk file and run: "$ANDROID_SDK/tools/apkbuilder %PATH_TO_APK_FILE% -z %PATH_TO_APK_COPY% -nf %PATH_TO_PROJECT%/libs".