JNI:C++コールバックJAVAの関数


サマリ
JNI、オブジェクトグローバル参照NewGlobalRef()、スレッドバインドJAVA仮想マシンAttachCurrentThread()
テストコード
JAVA
MainApp
//MainApp.java
import sun.misc.*;


public class MainApp {
	static {
        System.loadLibrary("animal");
    }
 
    public static void main(String[] args) {
		TestSignal handler = new TestSignal();
        Signal.handle(new Signal("TERM"), handler);
        Signal.handle(new Signal("INT"), handler);
        //Signal.handle(new Signal("USR1"), handler);
        //Signal.handle(new Signal("USR2"), handler);

        Animal animal = new Animal("Puppy");  
        animal.printName();
		
		animal.setCallback();
		
		while(handler.is_running()) {
			System.out.println("I am Main Thread.");
			try {
				Thread.sleep(5000);
			}
			catch(InterruptedException e) {
				System.out.println("InterruptedException.");
				e.printStackTrace();
			}
		}
		
		animal.closeCallback();
    }  
}

Animal
Animal.javaはコールバック関数void logMessage(String)を提供する.
//Animal.java  
public class Animal {  
    public String name;  
    public Animal(String name) {  
        this.name = name;  
    }

    public void printName() {  
        System.out.println("Animal [" + name + "]");  
    }

    private void logMessage(String msg) {
        System.out.println("msg:" + msg);
    }
 
    public native void setCallback();
	public native void closeCallback();
	
	protected long mObj;
}

コンパイル「javac Animal.java」実行後、「javah-jni Animal」を実行してC++ヘッダファイルAnimal.hを生成します.
しんごうしょり
//TestSignal.java

import sun.misc.*;

@SuppressWarnings("restriction")
public class TestSignal implements SignalHandler {
    public void handle(Signal arg0) {
        System.out.println(arg0.getName() + " is recevied.");
		if(arg0.getName() == "TERM") {
			brunning = false;
		}
    }
	
	public boolean is_running() {
		return brunning;
	}
	
	private boolean brunning = true;
}

JNIコード
ヘッダファイルAnimal.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#include 
/* Header for class Animal */

#ifndef _Included_Animal
#define _Included_Animal
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     Animal
 * Method:    setCallback
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_Animal_setCallback
  (JNIEnv *, jobject);

/*
 * Class:     Animal
 * Method:    closeCallback
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_Animal_closeCallback
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

ソースファイルAnimal.cpp
#include "Animal.h"

#include 
#include 
#include 
#include 
#include 


static JavaVM *g_VM = NULL;

class CLog {
public:
	CLog();
	~CLog();
	/*static CLog& instance() {
		return CLog::static_clog_;
	}*/
	
	bool attach(JavaVM* vm, JNIEnv *env, jobject thiz);
	bool detach(JNIEnv *env);
	
	void set_JavaVM(JavaVM* jVM) {
		javaVM_ = jVM;
	};
private:
	CLog(const CLog& obj);
	CLog& operator=(const CLog& obj);

	void attach_thread();
	void dettach_thread();
	
	static void *TestThread(void *arg);
private:
    bool brunning_ = true;
    JavaVM* javaVM_ = NULL;
    JNIEnv *env_ = NULL;
    jobject thiz_ = NULL;
	
	pthread_t tid_;
	
	//static CLog static_clog_;
};


//CLog CLog::static_clog_;

#define  CONSTRUCT(T) { T *t = new T(); \
    jclass clazz = (jclass)(*env).GetObjectClass(thiz); \
    jfieldID fid = (jfieldID)(*env).GetFieldID(clazz, "mObj", "J"); \
    jlong jstr = (jlong) (*env).GetLongField(thiz, fid);  \
    (*env).SetLongField(thiz, fid, (jlong)t);}
 
#define OBJECT(T,name) jclass clazz = (jclass)env->GetObjectClass(thiz); \
     jfieldID fid = env->GetFieldID(clazz, "mObj","J");  \
     T *name = (T *)env->GetLongField(thiz, fid);
 
#define DESTRUCT(T)  {jclass clazz = (jclass)env->GetObjectClass(thiz); \
     jfieldID fid = env->GetFieldID(clazz, "mObj","J");  \
     T *object = (T *)env->GetLongField(thiz, fid); \
     if(object != NULL) delete object; \
     (*env).SetLongField(thiz, fid, (jlong)0);}


CLog::CLog() {
}


CLog::~CLog() {
}


bool CLog::attach(JavaVM* vm, JNIEnv *env, jobject object) {
	javaVM_ = vm;
    thiz_ = env->NewGlobalRef(object);
	
	int ret = pthread_create(&tid_, NULL, CLog::TestThread, this);
	printf("create thread, ret = %d.
", ret); return true; } bool CLog::detach(JNIEnv *env) { env->DeleteGlobalRef(thiz_); thiz_ = NULL; brunning_ = false; pthread_join(tid_, NULL); return false; } void CLog::attach_thread() { if(javaVM_ == NULL) return; if(thiz_ == NULL) return ; int ret = javaVM_->AttachCurrentThread((void **)&env_, NULL); if(ret != 0) { } } void CLog::dettach_thread() { if(javaVM_ == NULL) return; javaVM_->DetachCurrentThread(); env_ = NULL; } void *CLog::TestThread(void *arg) { CLog *pThis = (CLog *)arg; pThis->attach_thread(); for(int i = 0; i < 100 && pThis->brunning_; ++i) { char buffer[64]; sprintf(buffer, "%5d", i); jstring msg_ = pThis->env_->NewStringUTF(buffer); jclass clazz = pThis->env_->GetObjectClass(pThis->thiz_); jmethodID jid = pThis->env_->GetMethodID(clazz, "logMessage", "(Ljava/lang/String;)V"); pThis->env_->CallVoidMethod(pThis->thiz_, jid, msg_); pThis->env_->DeleteLocalRef(msg_); sleep(5); } pThis->dettach_thread(); return NULL; } /* * Class: Animal * Method: setCallback * Signature: ()V */ JNIEXPORT void JNICALL Java_Animal_setCallback (JNIEnv *env, jobject thiz) { CONSTRUCT(CLog); OBJECT(CLog,log); if(log == NULL) { return; } log->attach(g_VM, env, thiz); } /* * Class: Animal * Method: closeCallback * Signature: ()V */ JNIEXPORT void JNICALL Java_Animal_closeCallback (JNIEnv *env, jobject thiz) { { OBJECT(CLog, log); if (log == NULL) { return; } log->detach(env); } DESTRUCT(CLog); } JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) { JNIEnv* env = NULL; //jint result = -1; if(JNI_OK != vm->GetEnv((void**) &env, JNI_VERSION_1_4)) { return -1; } assert(env != NULL); g_VM = vm; return JNI_VERSION_1_4; }

コンパイル
g++ -std=c++11 -shared -I/home/syq/java/jdk-14.0.2/include -I/home/syq/java/jdk-14.0.2/include/linux -fPIC Animal.cpp -o libanimal.so

うんてん
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:./
java MainApp

参照
  • Javaプログラムのコンパイルと実行のプロセス
  • JNI c++オブジェクトとjavaオブジェクトの相互関連