AndroidはJNIを利用してC++カスタムクラスを呼び出す


長い間android呼び出しC/C++ライブラリに関する文章を探していたが、androidがjniを利用してC++カスタムクラスを呼び出す文章は一つもなかった.仕方なくandroidのソースコードだけを見て、androidのグラフィックライブラリの実現を勉強した.その実現の底層もC++のskiaライブラリを利用しているからだ.以下、3つのファイルについて説明します.
まずjavaのクラスで、アプリケーションでPersonクラスという名前を呼び出すために使用されます.
package whf.jnitest;

public class Person {
	
	static
	{
		System.loadLibrary("Person");
	}
	
	private int mNativePerson;
	
	public Person()
	{
		mNativePerson = init();
	}
	protected void finalize()
	{
		try {
            finalizer(mNativePerson);
        } finally {
            try {
				super.finalize();
			} catch (Throwable e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
        }
	}	
	public int getAge()
	{
		return native_getAge(mNativePerson);
	}
	
	public void setAge(int age)
	{
		native_setAge(mNativePerson, age);
	}

	private native void finalizer(int nPerson);
	public 	native int	add(int a, int b);
	public	native int  sub(int a, int b);
	private native int  init();
	
	private native int	native_getAge(int nPerson);
	private native void native_setAge(int nPerson, int age);
}

そしてあなたのC++ファイルは以下の通りです.CPersonクラスと定義します.
#define LOG_TAG "this is a first JNI test"

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include "jni.h"
#include "CPerson.h"



	jint add(JNIEnv *env, jobject obj, jint a, jint b)
	{
		return a + b;
	}

    jint Java_whf_jnitest_Person_sub(JNIEnv *env, jobject obj, jint a, jint b)
	{
		return a - b;
	}

	void Java_whf_jnitest_Person_finalizer(JNIEnv *env, jobject clazz, CPerson *obj)
	{
		delete obj;
	}

	CPerson * Java_whf_jnitest_Person_init(JNIEnv *env, jobject clazz)
	{
	    CPerson *p = new CPerson();
		return p;
	}

	void Java_whf_jnitest_Person_setAge(JNIEnv *env, jobject clazz, CPerson *obj, jint age)
	{
		return obj->setAge(age);
	}

	jint Java_whf_jnitest_Person_getAge(JNIEnv *env, jobject clazz, CPerson *obj)
	{
		return obj->getAge();
	}

	static JNINativeMethod methods[]{
		{"add", "(II)I", (void *)add},
		{"sub", "(II)I", (void *)Java_whf_jnitest_Person_sub},
		{"finalizer", "(I)V", (void *)Java_whf_jnitest_Person_finalizer},
		{"init", "()I", (void *)Java_whf_jnitest_Person_init},
		{"native_setAge", "(II)V", (void *)Java_whf_jnitest_Person_setAge},
		{"native_getAge", "(I)I", (void *)Java_whf_jnitest_Person_getAge},
	};

	static const char * classPathName = "whf/jnitest/Person";

	static int registerNativeMethods(JNIEnv* env, const char* className,
		JNINativeMethod* gMethods, int numMethods)
	{
		jclass clazz;

		clazz = env->FindClass(className);
		if (clazz == NULL) {
			//LOGE("Native registration unable to find class '%s'", className);
			return JNI_FALSE;
		}
		if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {
			//LOGE("RegisterNatives failed for '%s'", className);
			return JNI_FALSE;
		}

		return JNI_TRUE;
	}

	static int registerNatives(JNIEnv* env)
	{
	  if (!registerNativeMethods(env, classPathName,
					 methods, sizeof(methods) / sizeof(methods[0]))) {
		return JNI_FALSE;
	  }

	  return JNI_TRUE;
	}

	typedef union {
		JNIEnv* env;
		void* venv;
	} UnionJNIEnvToVoid;

	/* This function will be call when the library first be loaded */
	jint JNI_OnLoad(JavaVM* vm, void* reserved)
	{
		UnionJNIEnvToVoid uenv;
		JNIEnv* env = NULL;
		//LOGI("JNI_OnLoad!");

		if (vm->GetEnv((void**)&uenv.venv, JNI_VERSION_1_4) != JNI_OK) {
			//LOGE("ERROR: GetEnv failed");
			return -1;
		}

		env = uenv.env;;

        //jniRegisterNativeMethods(env, "whf/jnitest/Person", methods, sizeof(methods) / sizeof(methods[0]));

		if (registerNatives(env) != JNI_TRUE) {
			//LOGE("ERROR: registerNatives failed");
			return -1;
		}

		 return JNI_VERSION_1_4;
	}

CPersonの声明は以下の通りです.
#ifndef _CPERSON_H_
#define _CPERSON_H_

class CPerson
{
private:
    int m_age;
public:
    CPerson();
    int getAge();
    void setAge(int age);
};

#endif

このような簡単なJNI呼び出しC++カスタムクラスが実現しました