JNA semble un peu plus facile à utiliser pour appeler du code natif par rapport à JNI. Dans quels cas utiliseriez-vous JNI sur JNA?
JNA semble un peu plus facile à utiliser pour appeler du code natif par rapport à JNI. Dans quels cas utiliseriez-vous JNI sur JNA?
Réponses:
Ce sont les problèmes que j'ai rencontrés. Il y a peut-être plus. Mais en général, les performances ne sont pas si différentes entre jna et jni, donc partout où vous pouvez utiliser JNA, utilisez-le.
ÉDITER
Cette réponse semble être très populaire. Voici donc quelques ajouts:
Donc, je crois toujours que dans la mesure du possible, il est préférable d'utiliser JNA ou BridJ, et de revenir à jni si les performances sont critiques, car si vous devez appeler fréquemment des fonctions natives, une baisse des performances est perceptible.
JNIEXPORT
fonctions globales . J'explore actuellement JavaCpp en tant qu'option, qui utilise JNI, mais je ne pense pas que vanilla JNI le supporte. Existe-t-il un moyen d'appeler des fonctions membres C ++ en utilisant le JNI vanilla que je néglige?
Il est difficile de répondre à une question aussi générique. Je suppose que la différence la plus évidente est qu'avec JNI, la conversion de type est implémentée du côté natif de la frontière Java / native, tandis qu'avec JNA, la conversion de type est implémentée en Java. Si vous vous sentez déjà assez à l'aise avec la programmation en C et que vous devez implémenter vous-même du code natif, je suppose que JNI ne semble pas trop complexe. Si vous êtes un programmeur Java et avez seulement besoin d'appeler une bibliothèque native tierce, l'utilisation de JNA est probablement le chemin le plus simple pour éviter les problèmes peut-être pas si évidents avec JNI.
Bien que je n'ai jamais évalué de différences, je le ferais à cause de la conception, au moins supposons que la conversion de type avec JNA dans certaines situations fonctionnera moins bien qu'avec JNI. Par exemple, lors du passage de tableaux, JNA les convertira de Java en natif au début de chaque appel de fonction et de nouveau à la fin de l'appel de fonction. Avec JNI, vous pouvez vous contrôler lorsqu'une "vue" native du tableau est générée, ne créant potentiellement qu'une vue d'une partie du tableau, gardez la vue sur plusieurs appels de fonction et à la fin, libérez la vue et décidez si vous voulez pour conserver les modifications (nécessitant éventuellement de recopier les données) ou annuler les modifications (aucune copie requise). Je sais que vous pouvez utiliser un tableau natif pour les appels de fonction avec JNA en utilisant la classe Memory, mais cela nécessitera également une copie de la mémoire, ce qui peut être inutile avec JNI. La différence n'est peut-être pas pertinente, mais si votre objectif initial est d'augmenter les performances de l'application en en implémentant certaines parties dans du code natif, l'utilisation d'une technologie de pont moins performante ne semble pas être le choix le plus évident.
Ce n'est que ce que je peux trouver du haut de ma tête, même si je ne suis pas un grand utilisateur de l'un ou de l'autre. Il semble également que vous pourriez éviter JNA si vous vouliez une meilleure interface que celle qu'ils fournissent, mais vous pouvez coder autour de cela en java.
D'ailleurs, dans l'un de nos projets, nous avons gardé une très petite empreinte JNI. Nous avons utilisé des tampons de protocole pour représenter nos objets de domaine et n'avions donc qu'une seule fonction native pour relier Java et C (alors bien sûr, cette fonction C appellerait un tas d'autres fonctions).
Ce n'est pas une réponse directe et je n'ai aucune expérience avec JNA mais, quand je regarde les projets utilisant JNA et que je vois des noms comme SVNKit, IntelliJ IDEA, NetBeans IDE, etc., j'ai tendance à croire que c'est une bibliothèque assez décente.
En fait, je pense vraiment que j'aurais utilisé JNA au lieu de JNI quand je devais le faire car cela semble en effet plus simple que JNI (qui a un processus de développement ennuyeux). Dommage, la JNA n'a pas été libérée à ce moment-là.
Si vous voulez des performances JNI mais que vous êtes découragé par sa complexité, vous pouvez envisager d'utiliser des outils qui génèrent automatiquement des liaisons JNI. Par exemple, JANET (avertissement: je l'ai écrit) vous permet de mélanger du code Java et C ++ dans un seul fichier source, et par exemple de faire des appels de C ++ vers Java en utilisant la syntaxe Java standard. Par exemple, voici comment imprimer une chaîne C sur la sortie standard Java:
native "C++" void printHello() {
const char* helloWorld = "Hello, World!";
`System.out.println(#$(helloWorld));`
}
JANET traduit ensuite le Java intégré à backtick en appels JNI appropriés.
J'ai fait quelques benchmarks simples avec JNI et JNA.
Comme d'autres l'ont déjà souligné, la JNA est pour des raisons de commodité. Vous n'avez pas besoin de compiler ou d'écrire du code natif lorsque vous utilisez JNA. Le chargeur de bibliothèque natif de JNA est également l'un des meilleurs / des plus faciles à utiliser que j'ai jamais vu. Malheureusement, vous ne pouvez pas l'utiliser pour JNI, semble-t-il. (C'est pourquoi j'ai écrit une alternative pour System.loadLibrary () qui utilise la convention de chemin de JNA et prend en charge le chargement transparent à partir du chemin de classe (c.-à-d. Jars ).)
Les performances de JNA peuvent cependant être bien pires que celles de JNI. J'ai fait un test très simple qui appelait une fonction d'incrémentation d'entier natif simple "return arg + 1;". Les tests de performance effectués avec jmh ont montré que les appels JNI à cette fonction sont 15 fois plus rapides que JNA.
Un exemple plus "complexe" où la fonction native résume un tableau d'entiers de 4 valeurs montre toujours que les performances JNI sont 3 fois plus rapides que JNA. L'avantage réduit était probablement dû à la façon dont vous accédez aux tableaux dans JNI: mon exemple a créé des éléments et les a relâchés à chaque opération de sommation.
Le code et les résultats des tests peuvent être trouvés sur github .
J'ai étudié JNI et JNA pour comparer les performances car nous devions décider que l'un d'entre eux appelle une dll dans le projet et nous avions une contrainte de temps réel. Les résultats ont montré que JNI a de meilleures performances que JNA (environ 40 fois). Il existe peut-être une astuce pour de meilleures performances dans JNA, mais elle est très lente pour un exemple simple.
À moins que je ne manque quelque chose, la principale différence entre JNA et JNI n'est-elle pas qu'avec JNA, vous ne pouvez pas appeler du code Java à partir du code natif (C)?
Dans mon application spécifique, JNI s'est avéré beaucoup plus facile à utiliser. J'avais besoin de lire et d'écrire des flux continus vers et depuis un port série - et rien d'autre. Plutôt que d'essayer d'apprendre l'infrastructure très impliquée dans JNA, j'ai trouvé beaucoup plus facile de prototyper l'interface native de Windows avec une DLL spéciale qui n'exportait que six fonctions: