- Write once, run everywhere. That is Java's motto, but actually native code is going to run on more devices than your Java. If you write your game proper in C/C++, you automatically have to create some sort of device interface to make it work under android, because in NDK you can not access the Android input and UI APIs (actually 2.3 supports that, but we are going to target 1.6). It is on you, where you draw the line.
- Using native libraries. If they have a Java binding, they probably do not have one for Android.
- Speed is the least important factor. Not many games are CPU bound and unless you are doing heavy physics simulation, it is not going to be an important factor.
Simple JNI, the hard way
I assume you can make a sample OpenGL application. Make a simple project in Eclipse with just one activity and GLSurfaceView. The renderer might look similar to this:
public void onSurfaceCreated(GL10 gl, EGLConfig config)
{
activity.surfaceCreatedNativeCallback();
}
public void onSurfaceChanged(GL10 gl, int width, int height)
{
if(height == 0) {
height = 1;
}
gl.glViewport(0, 0, width, height);
}
public void onDrawFrame(GL10 gl) {
gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
}
Let's call our activity cz.badroid.FooActivity. We will define the surfaceNativeCallback like this:
public native void surfaceCreatedNativeCallback();
There is no method body, because, we are going to implement it in native code. Download android NDK and extract it to folder we will call %NDKHOME% from now on. Create a directory called jni in your Eclipse project(on the same level as those src, res directories) and add two files there:
Android.mk:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := yourlibname
LOCAL_SRC_FILES := main.cpp
LOCAL_LDLIBS := -llog -lGLESv1_CM
include $(BUILD_SHARED_LIBRARY)
main.cpp:
#include <GLES/gl.h> extern "C"{ JNIEXPORT void JNICALL Java_cz_badroid_FooActivity_surfaceCreatedNativeCallback (JNIEnv * env, jobject activity) { glClearColor(0,0,1,1); } }Now just build it by running %NDKHOME%/ndk-build in the jni directory. You have just set GL Clear Color in native code! There are common problems and misconeceptions:
- You need a JNI_OnLoad method. No you do not, but it is recommended, so that you can check if your library is being loaded.
- You need to register native methods -- no that's not needed at all.
- The Android Eclipse project does not depend on the jni libs. That means that whenever you rebuild your NDK project, you also have to make sure your parent Eclipse project is rebuilt. I do it by "touch ../AndroidManifest.xml"
Just imagine writing a JNI methods for all your engine's methods(and converting all the parameters).... Ufff.... The javah tool will help you a little, but it works the other way around, it generates a native code for Java one.
The answer is SWIG -- Simple Wrapper and Interface Generator. There is also other tool, called gluegen, but it works only for plain C. There is our new set of files(I have not tried to compile them, I hope they work):
main.cpp:
#include "main.h"; #include <GLES/gl.h> void NativeComponent::testNativeMethod(){ glClearColor(0,0,1,1); }main.h:
#ifndef MAIN_H #define MAIN_H class NativeComponent{ public: void testNativeMethod(); }; #endif
Android.mk:
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := yourlibname LOCAL_SRC_FILES := main.cpp bindings_wrap.cxx LOCAL_LDLIBS := -llog -lGLESv1_CM include $(BUILD_SHARED_LIBRARY)bindings.i:
%module nativecomponent %{ /* Includes the header in the wrapper code */ #include "main.h" %} /* Parse the header file to generate wrappers */ %include "main.h"That's it. Now we can generate all the bindings by calling:
swig -c++ -java -package cz.badroid.bindings -outdir ../src/cz/badroid/bindings/ bindings.i
Now you can do in your Java code: