2009-04-28 6 views
2

Je viens de commencer un nouveau travail. Ici, nous sommes nouveaux à l'aide de JNI (pour le pontage C++/Java). Je suis nouveau à JNI alors s'il vous plaît pardonnez mon noobness :)Instances d'objets JNI et C++

Dans notre application (win32) Java, nous sommes en train de charger une DLL C++. Du côté Java nous avons plusieurs instances de "SomeJClass" chacune de ces instances a besoin d'accéder à l'instance correspondante de "SomeCClass" du côté DLL. La DLL expose des points d'entrée tels que GlobalDoSomethingInC(). Ici, je dois appeler la méthode d'instance de Doer :: DoSomethingInC(). J'ai donc besoin d'un moyen facile de mapper les pointeurs respectifs. J'ai aussi besoin de faire le même mapping lorsqu'un thread DLL découvre quelque chose d'intéressant dont il a besoin pour notifier l'instance Java correspondante de.

Je peux penser à plusieurs solutions, mais je ne les aime pas trop. Ma question est la suivante: y a-t-il un meilleur moyen que celui-ci?

1 Java appelle C: GetNewInstance(). Cela retourne un int qui est en fait un pointeur sur la nouvelle instance C. Java le stocke dans m_myCInstance. Ensuite, appelle Java GlobalDoSomethingInC() et 1a

// DLL global 
void GlobalDoSomethingInC() 
{ 
    // retrive this pointer 
    //calling back to Java: 
    jobj tmpJ = NewGlobalRef(env, obj); 
    Doer* myDoer = <reinterpret_cast>(Doer)tmpJ->GetMyCInstance(); 
    myDoer->DoSomething(); 
    DeleteGlobalRef(env, tmpJ); 
    // Arrrrgh 
} 

1b ou:

// for **every call** that Java adds a parameter, 
    //which is the stored int:m_myCInstance, and 
    Doer* myDoer = <reinterpret_cast>(Doer)instanceParam->DoSomethingInC(); 
    // Can we do better that this? 

2 Pour appeler de C à Java, les choses, peut-être, mieux

In the constructor C calls back into Java and stores 
the Java instance reference 
    in a member variable. m_myJInstance. 
    In all subsequent calls m_myJInstance can be used to call back Java. 
    In the destructor we need to call DeleteGlobalRef(env, m_myJInstance); 

Pas trop mauvais je suppose. Mais il est vraiment sûr de stocker la référence jobject. Je veux dire: Que se passe-t-il lorsque le GC déplace l'objet?

3 Notre solution actuelle "fonctionne". Mais il appartient plutôt sur le http://www.codinghorror.com/blog/ :)

Thanx

Répondre

2

Typiquement, cela dépendra de votre environnement un peu. J'ai seulement utilisé KNI, qui est encore plus primitif que JNI. Je pense qu'un peu de laideur est inévitable, car vous mélangez le suivi de la mémoire entre deux systèmes, dont un seul a GC.

En général, j'ai trouvé préférable d'emballer tous les appels sur le code C dans les fonctions qui ont pris soin de la coulée méchant, ce qui je pense est inévitable. (BTW, je vais utiliser C pour signifier le code non-Java ici)

Sur le côté C, le mouvement des objets Java est certainement un problème potentiel. Cela dépendra de votre plate-forme, mais je m'attendrais à ce que tant que vous êtes dans la bibliothèque lib, vous ne pouvez pas vous attendre à ce que Java GC se produise, donc vos objets sont stables. Vous devez être sûr de cela. D'un autre côté, si ce n'est pas le cas, vous êtes plutôt foutu. En supposant que ce soit le cas, vous voulez faire la même chose d'isoler dereferencing/casting à la fonction qui est exposée à JNI, de sorte que vous pouvez heureusement travailler avec des objets C normaux dans toutes vos fonctions appelées. Là où ça peut vraiment être laide, c'est si vous pouvez avoir des objets hors de portée de chaque côté, car alors potentiellement l'un ou l'autre côté peut contenir une référence à votre objet. Ici, nous avons utilisé des finaliseurs du côté Java, ainsi que des destructeurs du côté C. Ce n'était pas joli, mais je pense que c'est quelque peu inévitable. Donc, réponse courte, ce sera un peu moche, isolez la laideur autour de l'interface entre les deux langues, de sorte que pour la majeure partie du travail, dans l'une ou l'autre langue, vous n'avez pas à vous inquiéter de telles choses.

Il est également intéressant d'avoir une classe de base pour les objets qui existent sur cette interface, car ici vous pouvez également isoler de la laideur.

0

jobject est un descripteur opaque d'un objet. Peut varier dans la mise en œuvre d'exécution (voir Android 2.x vs 4.x), mais juste faire confiance qu'il s'agit d'un objet opaque.

La solution actuelle est probablement correcte. Si vous devez stocker un jobject en code natif, vous devez le convertir en référence globale - Si vous appelez NewGlobalRef, le refcount de l'objet a augmenté, et ne sera pas détruit jusqu'à ce que vous appeliez DeleteGlobalRef (et le GC a remarqué qu'il est inaccessible autrement)