See discussions, stats, and author profiles for this publication at: https://www.researchgate.net/publication/260460628 Conhecendo o Android NDK: integrando código nativo às suas aplicações Android ARTICLE · JANUARY 2014 READS 115 1 AUTHOR: Leandro Luque Centro Paula Souza 23 PUBLICATIONS 1 CITATION SEE PROFILE Available from: Leandro Luque Retrieved on: 11 March 2016
20
Embed
Conhecendo o Android NDK: integrando código nativo às suas aplicações Android
Artigo da Revista MundoJ sobre a customização de um framework MVVM para Android (AndroidBinding).
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Conhecendo o Android NDK Integrando código nativo às suas aplicações Android
Porque este artigo é útil
Este artigo apresenta um introdução ao Android Native Development Kit - NDK, permitindo
a você tirar o máximo de proveito do dispositivo em aplicações que consomem muitos recursos de
CPU ou que precisam aproveitar um hardware específico. Para muitas aplicações, o desempenho
obtido com o desenvolvimento baseado apenas no Android Software Development Kit - SDK pode
não ser satisfatório. Nestes casos, o uso do NDK contribui para que a aplicação atenda aos
requisitos de desempenho desejados.
O mercado de dispositivos móveis cresce em ritmo acelerado no Brasil e no mundo.
Segundo a International Data Corporation - IDC, o crescimento mundial anual deve ficar em torno
de 7,3% no fim de 2013. No Brasil, o volume de vendas de smartphones no segundo trimestre de
2013 foi 110% superior ao do mesmo período de 2012, ultrapassando, pela primeira vez na história,
a venda de celulares comuns. Quando considerados os tablets, o aumento foi de 151% no mesmo
período.
De cada 10 destes smartphones e tablets vendidos no país, 9 possuem o sistema operacional
Android. No mundo, este número fica em torno de 7 a cada 10 (Tabela 1).
Sistema Operacional Fatia de Mercado (Smartphone) Fatia de Mercado (Tablet)
Android 75,3% 62,5%
iOS 16,9% 32,5%
Windows Phone 3,9% 4,0%
BlackBerry OS 2,7% 0,3%
Outros 1,2% 0,7%
Tabela 1. Fatia do mercado mundial dos sistemas operacionais de dispositivos móveis. Fonte: IDC (2013).
Esse crescimento do mercado, aliado à maior capacidade dos dispositivos móveis, está
impulsionando a demanda por aplicações e jogos cada vez mais complexos, rápidos e interativos.
Isto pode ser facilmente percebido pelo volume de jogos com alta complexidade gráfica que vêm
sendo comercializados e também por aplicações baseadas em processamento de imagens e realidade
aumentada que possibilitam a tradução simultânea de imagens com textos em outros idiomas (p.ex.:
Word Lens e CamTranslator), a pesquisa por informações baseada em imagens (p.ex.: Google
Goggles), a simulação de mobílias em ambientes a partir de imagens da câmera (p.ex.: Home
Design 3D), entre muitas outras.
Para muitas das aplicações citadas, o desempenho obtido com o desenvolvimento baseado
apenas no Android Software Development Kit - SDK pode não ser satisfatório. Nestes casos, o uso
2/19
do Android Native Development Kit – NDK contribui para que a aplicação atenda aos requisitos de
desempenho desejados.
Neste contexto, este artigo apresenta o NDK e como ele pode ser integrado ao SDK, para
que você possa tirar o máximo de proveito do dispositivo em aplicações que consomem muitos
recursos de CPU ou que precisam aproveitar um hardware específico. Será utilizada uma aplicação
exemplo, desenvolvida e comentada passo-a-passo, para esclarecer as etapas necessárias para
atingir o objetivo proposto.
Integração de Aplicações Java com Código Nativo Existem duas alternativas principais para a integração de aplicações Java, não apenas
móveis, com código nativo. A primeira delas envolve a criação de processos do sistema operacional
para a execução do código nativo. Uma forma de fazer isso é por meio da classe ProcessBuilder,
disponível a partir do Java 5 (Listagem 1). O código apresentado nesta listagem é autoexplicativo e
permite a recuperação da saída padrão produzida pelo comando cmd /c dir /ad, executado na pasta
raiz – para sistemas Windows.
Esse mesmo recurso poderia ser utilizado para executar um código nativo que realiza um
cálculo matemático, por exemplo. Entre outras formas, a recuperação do resultado poderia ser feita
a partir da saída padrão, como no exemplo apresentado, ou mesmo por meio de arquivos – o código
nativo gravaria um arquivo como saída e o código Java realizaria a leitura deste arquivo e
recuperaria o resultado.
Listagem 1. Exemplo de código que executa o comando cmd /c dir /ad e exibe as subpastas da pasta raiz (para sistemas Windows). // ... package e imports
public class TesteProcessBuilder {
public static void main(String[] args) {
// Comando que será executado: cmd /c dir /ad
// A array contém em ordem: comando parâmetro1 parâmetro2 ...
String[] comando = {"cmd", "/c", "dir", "/ad"};
// Cria um construtor de processo.
ProcessBuilder construtorProcesso = new ProcessBuilder(comando);
// Pasta de trabalho relacionada ao comando (raiz).
construtorProcesso.directory(new File("c:\\"));
try {
// Tenta iniciar o processo.
Process processo = construtorProcesso.start();
// Aguarda a finalizaçao do processo.
processo.waitFor();
// Lê a saída do comando.
BufferedReader in = new BufferedReader(new
InputStreamReader(processo.getInputStream()));
System.out.println("Saída do comando: cmd /c dir /ad");
String saida;
while ((saida = in.readLine()) != null) {
System.out.println(saida);
}
} catch (IOException erro) {
System.out.println("Sentimos muito. Ocorreu um erro durante a execução do programa.");
// ...
} catch (InterruptedException erro) {
System.out.println("Sentimos muito. Ocorreu um erro durante a execução do programa.");
// ...
}
}
}
Existem algumas implicações relacionadas a esta abordagem. Uma delas é a necessidade do
código nativo ser executável na plataforma em questão. Outra está relacionada à comunicação, que
geralmente envolverá o processamento de textos e conversões. Ainda, não sendo possível acessar
diretamente variáveis e métodos da máquina virtual do Java, as informações que o método nativo
necessita devem ser fornecidas previamente via parâmetros ou arquivos.
3/19
A outra abordagem envolve o uso da JNI – Java Native Interface, um padrão que permite
que bibliotecas, não código executável, sejam integradas ao código Java. É por meio dela que
aplicações Android NDK conseguem executar códigos implementados em C/C++. Diferentemente
da outra abordagem, a JNI possibilita a interação direta com a máquina virtual do Java, não
apresentando as implicações citadas.
A seguir, ela será descrita mais detalhadamente.
Java Native Interface - JNI Para utilizar a Java Native Interface – JNI em um projeto, além do código nativo ter que ser
escrito seguindo algumas convenções, a biblioteca onde o código nativo se encontra deve ser
carregada no código Java, onde também devem ser escritas as assinaturas dos métodos nativos. Por
fim, basta invocar os métodos cujas assinaturas foram definidas e o código nativo será
automaticamente executado.
A seguir, cada uma destas etapas e alguns detalhes importantes sobre elas serão descritos.
Carregamento da biblioteca nativa No código Java, antes de acessar métodos nativos, a biblioteca que os contém deve ser
carregada por meio do comando System.loadLibrary ou System.load. Eles são geralmente
colocados em um bloco estático, pois o carregamento precisa ser feito apenas uma única vez.
(Listagem 2). Caso seja realizado mais de uma vez, os carregamentos subsequentes são
desconsiderados.
Listagem 2. Exemplo de código que carrega uma biblioteca chamada “minhabiblioteca”. //...
public class LadoJava {
// ...
static {
System.loadLibrary("minhabiblioteca");
// ou System.load("caminhoCompleto/minhabiblioteca.dll");
}
// ...
}
No caso do método loadLibrary, o Java segue um padrão específico por plataforma para
encontrar a biblioteca informada. Como exemplo, caso seja um sistema Windows, ele procurará por
uma biblioteca chamada minhabiblioteca.dll. Caso seja um sistema Linux, como no caso do
Android, ele procurará por libminhabiblioteca.so. O caminho onde a biblioteca é procurada é
especificado na variável java.library.path.
Para o caso do método load, uma caminho completo para a biblioteca deve ser informado, o
que pode ser interessante em alguns casos.
Assinaturas nativas no código Java Além do carregamento da biblioteca, o código Java deve criar assinaturas de métodos que
serão associadas ao código nativo. Estas assinaturas exigem o modificador native na sua declaração.
Na Listagem 3, são definidas duas assinaturas, que serão associadas a dois métodos nativos.
Listagem 3. Exemplo assinatura de método nativo no código Java. package br.com.fatec.projetomm;
//...
public class LadoJava {
// ... Carregamento da biblioteca ...
public native void fazAlgumaCoisa();
public native static void fazAlgumaOutraCoisa();
}
A seguir, veremos como o código nativo deve ser criado para que o código Java consiga
executá-lo.
Declarações do código nativo
4/19
Existem duas formas de declarar no código nativo os métodos especificados com native no
código Java. A primeira delas envolve seguir algumas convenções da JNI e a forma mais rápida e
fácil de segui-las é utilizar um utilitário que acompanha o Java SDK e permite a geração do
cabeçalho do código-fonte nativo: o javah.
A Listagem 4 apresenta alguns exemplos de uso deste utilitário – os comentários estão no
formato Windows. Para executar os comandos desta listagem, é necessário que a pasta “bin” do
SDK esteja na variável de ambiente PATH e as classes especificadas como parâmetro estejam na
variável CLASSPATH. Os arquivos gerados pelo utilitário são gravados na pasta de trabalho atual.
Listagem 4. Exemplo de chamada do programa javah. :: Assinatura do comando.
javah [opções] <caminho completo da classe>[...]
:: Criando o cabeçalho C para a classe br.com.fatec.projetomm.LadoJava.
javah –jni br.com.fatec.projetomm.LadoJava
:: A opção –jni é padrão, portanto, pode ser omitida.
javah br.com.fatec.projetomm.LadoJava
A Listagem 5 apresenta o arquivo gerado para a classe br.com.fatec.projetomm.LadoJava.
Listagem 5. Arquivo br_com_fatec_projetomm_LadoJava.h gerado por meio do programa javah. /* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class br_com_fatec_projetomm_LadoJava */
Em testes realizados em um Samsung Galaxy Tab, o código nativo foi duas vezes mais
rápido que o Java para máscaras com tamanho 3x3. Esta diferença tende a crescer com o aumento
do tamanho da máscara.
O código completo do projeto está disponível no site da revista.
Conclusões Este artigo apresentou uma introdução ao NDK e como ele pode contribuir para a melhoria
do desempenho de suas aplicações Android. A JNI, utilizada no NDK, é muito extensa e apenas
uma breve introdução foi apresentada. Recomendamos a leitura dos artigos e livros na seção de
links para um aprofundamento sobre o tema.
Leandro Luque [email protected] É professor da FATEC Mogi das Cruzes, onde desenvolve pesquisas na área de Interação Humano-Computador, Engenharia de Software e Processamento de Imagens. Bacharel em Ciência da Computação pela Universidade de Mogi das Cruzes e mestre em Computação Aplicada pelo Instituto Nacional de Pesquisas Espaciais (INPE), trabalha com Java há 13 anos, atuando no desenvolvimento de aplicações de grande porte, tanto no segmento empresarial quanto governamental.
19/19
Eron Silva [email protected] É graduando em Análise e Desenvolvimento de Sistemas pela FATEC Mogi das Cruzes. Tem dois anos de experiência em desenvolvimento Android, principalmente de aplicações para o processamento de imagens e reconhecimento de códigos de barras.