IntroductionThe android-cpp-sdk is a c++ wrapper for android's java sdk. The purpose of android-cpp-sdk is to ease native/NDK development on android platform, by providing a corresponding c++ class for almost every java class from android-sdk. Designandroid-cpp-sdk is designed as header-only library and consists of folowing 2 parts: - j2cpp: support library that eases the using of JNI;
- android platform header files: automatically generated files from corresponding android sdk .jar file;
Each android sdk class and its nested classes are implemented in one header file and is automatically generated by java2cpp tool. The header file contains declaration and definition of classes. Whether declaration or definition is needed can be selected by defining a folowing preprocessor macro: #define J2CPP_INCLUDE_IMPLEMENTATION before including the file. UsageAs it is said, the library is header only so the usage is simple as including the header files for used classes. The project must have defined include paths to following directories: $(PATH_TO_ANDROID_CPP_SDK)/j2cpp and depending on android platform the application is written for : $(PATH_TO_ANDROID_CPP_SDK)/platforms/android-(platform id) Before using the android-cpp-sdk, first it must be initialized. InitializationThe library is initialized with an instance of j2cpp::environment class implementation. j2cpp::environment is responsible for retrieving classes, methods and fields from java virtual machine. j2cpp::raw_environment is raw implementation of this class that entirely relies on raw jni calls. Initialization with default/raw implementation of j2cpp::environment is like this: j2cpp::environment::init(
j2cpp::shared_ptr<j2cpp::raw_environment>(
new j2cpp::raw_environment(vm)
)
);. The best practice to do this is while the native library (.so) is loading (in JNI_OnLoad), for example: #include <j2cpp/j2cpp.hpp>
#include <j2cpp/raw_environment.hpp>
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
j2cpp::shared_ptr<j2cpp::raw_environment> raw_env(new j2cpp::raw_environment(vm));
if(!j2cpp::environment::init(raw_env))
return JNI_ERR;
return JNI_VERSION_1_4;
}
Importing classesImporting/using of android-sdk classes can be done as follows: #include <j2cpp/j2cpp.hpp>
#include <android/hardware/Camera.hpp>
#define J2CPP_INCLUDE_IMPLEMENTATION
#include <android/hardware/Camera.hpp> Be aware of including a definition of same class in multiple .cpp files, linker will error with multiple symbol definition. The best way is to have one .cpp file which will include definition of all used classes: my_project/jni/android_cpp_sdk_lib.cpp : #include <j2cpp/j2cpp.hpp>
#include <android/app/Activity.hpp>
#include <android/hardware/Camera.hpp>
.
.
.
#define J2CPP_INCLUDE_IMPLEMENTATION
#include <android/app/Activity.hpp>
#include <android/hardware/Camera.hpp>
.
.
. ReferencesFor referencing java objects j2cpp library provides two reference classes: - local_ref< ClassType > : used for holding a local/stack reference to java object;
- global_ref< ClassType > : used for holding a global reference to java object;
Wrapping and using an existing instance of java class (jobject)Each automatically generated class has an explicit constructor that accepts jobject. For simply using an existing instance of known java class, it is simple as : using namespace j2cpp;
void processMotionEvent(jobject me)
{
android::view::MotionEvent motionEvent(me);
jfloat meX=motionEvent.getX();
jfloat meY=motionEvent.getY();
.
.
.
}
Creating an objectsCreating new objects is done by invoking generated corresponding c++ constructor: using namespace j2cpp;
local_ref<java::lang::String> newString1=java::lang::String();
local_ref<java::lang::String> newString2=java::lang::String(array<jbyte,1>("newString2"));
Working with static and member fieldsThe library is designed to have a simple and logical syntax. Working with static field is as simple as: using namespace j2cpp;
void doRender(jobject gl10)
{
using namespace javax::microedition::khronos::opengles;
GL10 gl(gl10);
gl.glEnable(GL10::GL_TEXTURE_2D);
gl.glBindTexture(texId);
.
.
.
}
and with member fields: using namespace j2cpp;
void Polygon::append(jobject jp)
{
android::graphics::Point point(jp);
m_points.push_back(Vertex(point.x,point.y));
}
Attaching/Detaching native threads to Java VMAttaching of current native threads to the java virtual machine is performed with calling: j2cpp::environment::get()->attach_current_thread(); and detaching with: j2cpp::environment::get()->detach_current_thread(); Sometimes, java virtual machine assigns default class loader to newly attached native threads, that is not able to load yours and/or non system classes, so the thread can not make jni calls. This can be solved with implementing your own j2cpp::environment, pre-caching of class loader of the main thread that initializes your native library (.so) and using this class loader to find classes. Here is an example of how it can be done: #include <j2cpp/raw_environment.hpp>
#include <j2cpp/j2cpp.hpp>
#include <java/lang/Class.hpp>
#include <java/lang/Object.hpp>
#include <java/lang/Comparable.hpp>
#include <java/lang/String.hpp>
#include <java/lang/ClassLoader.hpp>
#include <java/lang/Thread.hpp>
#define J2CPP_INCLUDE_IMPLEMENTATION
#include <java/lang/Class.hpp>
#include <java/lang/Object.hpp>
#include <java/lang/Comparable.hpp>
#include <java/lang/String.hpp>
#include <java/lang/ClassLoader.hpp>
#include <java/lang/Thread.hpp>
using namespace j2cpp;
using namespace j2cpp::java::lang;
class custom_environment
: public j2cpp::raw_environment
{
public:
custom_environment(JavaVM *jvm)
: j2cpp::raw_environment(jvm)
{
}
~custom_environment()
{
}
bool init()
{
if(local_ref<Thread> currThread=Thread::currentThread())
{
if(local_ref<ClassLoader> cler=currThread->getContextClassLoader())
{
//call java.lang.ClassLoader.loadClass method for j2cpp to load (precache) java.lang.ClassLoader class:
if(local_ref<Class> dummyClass=cler->loadClass(local_ref<String>(jenv()->NewStringUTF("java/lang/String"))))
m_class_loader=cler;
}
}
return static_cast<bool>(m_class_loader);
}
bool attach_current_thread()
{
if(raw_environment::attach_current_thread())
{
if(m_class_loader)
{
//make sure that Java VM has attached java.lang.Thread to the native thread
if(local_ref<Thread> currThread=Thread::currentThread())
return true;
}
}
return false;
}
bool detach_current_thread()
{
return raw_environment::detach_current_thread();
}
jclass find_class(char const *cn)
{
if(m_class_loader)
{
//find the class with pre-cached java.lang.ClassLoader
if(local_ref<Class> found_class=m_class_loader->loadClass(local_ref<String>(jenv()->NewStringUTF(cn))))
return reinterpret_cast<jclass>(found_class->get_jobject());
}
return raw_environment::find_class(cn);
}
global_ref<ClassLoader> m_class_loader;
};
JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
j2cpp::shared_ptr<custom_environment> custom_env(new custom_environment(vm));
if(j2cpp::environment::init(custom_env) && custom_env->init())
return JNI_VERSION_1_4;
return JNI_ERR;
}SamplesDirectory $(PATH_TO_ANDROID_CPP_SDK)/samples contains two working samples of how to use the library: - j2cppTest : very first sample;
- j2cppCameraTest : sample that opens the camera and draws the preview frames using OpenGL ES also shows how to manualy add new java classes ( com.j2cpp.CameraTest );
|