JNA parece um pouco mais fácil de usar para chamar código nativo em comparação com JNI. Em que casos você usaria JNI em vez de JNA?
JNA parece um pouco mais fácil de usar para chamar código nativo em comparação com JNI. Em que casos você usaria JNI em vez de JNA?
Respostas:
Esses são os problemas que encontrei. Talvez haja mais. Mas, em geral, o desempenho não é tão diferente entre jna e jni, portanto, onde quer que você possa usar JNA, use-o.
EDITAR
Esta resposta parece ser bastante popular. Então, aqui estão algumas adições:
Portanto, ainda acredito que, sempre que possível, é melhor usar JNA ou BridJ e reverter para jni se o desempenho for crítico, porque se você precisar chamar funções nativas com frequência, o impacto no desempenho será perceptível.
JNIEXPORT
funções globais . Atualmente estou explorando JavaCpp como uma opção, que usa JNI, mas não acho que o vanilla JNI suporte isso. Existe uma maneira de chamar funções de membro C ++ usando vanilla JNI que estou ignorando?
É difícil responder a uma pergunta tão genérica. Suponho que a diferença mais óbvia é que com JNI, a conversão de tipo é implementada no lado nativo da fronteira Java / nativa, enquanto com JNA, a conversão de tipo é implementada em Java. Se você já se sente bastante confortável com a programação em C e precisa implementar algum código nativo sozinho, eu presumiria que o JNI não parecerá muito complexo. Se você é um programador Java e só precisa invocar uma biblioteca nativa de terceiros, usar JNA é provavelmente o caminho mais fácil para evitar os problemas talvez não tão óbvios com JNI.
Embora eu nunca tenha feito o benchmark de nenhuma diferença, por causa do design, pelo menos suponha que a conversão de tipo com JNA em algumas situações terá um desempenho pior do que com JNI. Por exemplo, ao passar arrays, o JNA os converterá de Java para nativo no início de cada chamada de função e de volta no final da chamada de função. Com JNI, você pode controlar a si mesmo quando uma "visão" nativa da matriz é gerada, potencialmente criando apenas uma visão de uma parte da matriz, manter a visão em várias chamadas de função e no final liberar a visão e decidir se deseja para manter as alterações (potencialmente exigindo a cópia de volta dos dados) ou descartar as alterações (nenhuma cópia necessária). Eu sei que você pode usar uma matriz nativa em chamadas de função com JNA usando a classe Memory, mas isso também exigirá a cópia de memória, o que pode ser desnecessário com JNI. A diferença pode não ser relevante, mas se seu objetivo original é aumentar o desempenho do aplicativo implementando partes dele em código nativo, usar uma tecnologia de ponte de pior desempenho não parece ser a escolha mais óbvia.
Isso é apenas o que posso fazer de cabeça, embora eu não seja um usuário pesado de qualquer um. Também parece que você pode evitar o JNA se quiser uma interface melhor do que a que eles fornecem, mas você pode codificar em java.
A propósito, em um de nossos projetos, mantivemos uma pegada JNI muito pequena. Usamos buffers de protocolo para representar nossos objetos de domínio e, portanto, tínhamos apenas uma função nativa para fazer a ponte entre Java e C (então, é claro, essa função C chamaria várias outras funções).
Não é uma resposta direta e não tenho experiência com JNA, mas, quando olho para Projetos usando JNA e vejo nomes como SVNKit, IntelliJ IDEA, NetBeans IDE, etc, tenho tendência a acreditar que é uma biblioteca bastante decente.
Na verdade, eu definitivamente acho que teria usado JNA em vez de JNI quando era necessário, pois parece mais simples do que JNI (que tem um processo de desenvolvimento chato). Que pena, JNA não foi lançado neste momento.
Se você deseja desempenho JNI, mas está intimidado por sua complexidade, pode considerar o uso de ferramentas que geram ligações JNI automaticamente. Por exemplo, JANET (isenção de responsabilidade: eu escrevi) permite que você misture código Java e C ++ em um único arquivo de origem e, por exemplo, faça chamadas de C ++ para Java usando a sintaxe Java padrão. Por exemplo, veja como você imprimiria uma string C na saída padrão Java:
native "C++" void printHello() {
const char* helloWorld = "Hello, World!";
`System.out.println(#$(helloWorld));`
}
O JANET então converte o Java embutido no crase nas chamadas JNI apropriadas.
Na verdade, fiz alguns benchmarks simples com JNI e JNA.
Como outros já apontaram, JNA é por conveniência. Você não precisa compilar ou escrever código nativo ao usar JNA. O carregador de biblioteca nativa do JNA também é um dos melhores / mais fáceis de usar que já vi. Infelizmente, você não pode usá-lo para JNI ao que parece. (É por isso que escrevi uma alternativa para System.loadLibrary () que usa a convenção de caminho do JNA e oferece suporte ao carregamento direto do caminho de classe (ou seja, jars).)
O desempenho do JNA, entretanto, pode ser muito pior do que o do JNI. Fiz um teste muito simples que chamou uma função de incremento de inteiro nativo simples "return arg + 1;". Benchmarks feitos com jmh mostraram que as chamadas JNI para essa função são 15 vezes mais rápidas do que JNA.
Um exemplo mais "complexo" em que a função nativa soma um array inteiro de 4 valores ainda mostrou que o desempenho do JNI é 3 vezes mais rápido do que o do JNA. A vantagem reduzida provavelmente se deu por causa de como você acessa arrays no JNI: meu exemplo criou algumas coisas e as liberou novamente durante cada operação de soma.
O código e os resultados dos testes podem ser encontrados no github .
Eu investiguei JNI e JNA para comparação de desempenho porque precisávamos decidir um deles para chamar uma dll no projeto e tínhamos uma restrição de tempo real. Os resultados mostraram que o JNI tem um desempenho superior ao do JNA (aproximadamente 40 vezes). Talvez haja um truque para melhor desempenho em JNA, mas é muito lento para um exemplo simples.
A menos que esteja faltando alguma coisa, a principal diferença entre JNA e JNI não é que com JNA você não pode chamar o código Java do código nativo (C)?
Em meu aplicativo específico, o JNI provou ser muito mais fácil de usar. Eu precisava ler e gravar fluxos contínuos de e para uma porta serial - e nada mais. Em vez de tentar aprender a infraestrutura muito envolvida em JNA, achei muito mais fácil criar um protótipo da interface nativa do Windows com uma DLL de propósito especial que exportava apenas seis funções: