Você está na página 1de 20

See

discussions, stats, and author profiles for this publication at: https://www.researchgate.net/publication/260460628

Conhecendo o Android NDK: integrando cdigo


nativo s suas aplicaes 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

1/19

Conhecendo o Android
NDK
Integrando cdigo nativo s suas
aplicaes Android
Porque este artigo til
Este artigo apresenta um introduo ao Android Native Development Kit - NDK, permitindo
a voc tirar o mximo de proveito do dispositivo em aplicaes que consomem muitos recursos de
CPU ou que precisam aproveitar um hardware especfico. Para muitas aplicaes, o desempenho
obtido com o desenvolvimento baseado apenas no Android Software Development Kit - SDK pode
no ser satisfatrio. Nestes casos, o uso do NDK contribui para que a aplicao atenda aos
requisitos de desempenho desejados.

O mercado de dispositivos mveis 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 perodo de 2012, ultrapassando, pela primeira vez na histria,
a venda de celulares comuns. Quando considerados os tablets, o aumento foi de 151% no mesmo
perodo.
De cada 10 destes smartphones e tablets vendidos no pas, 9 possuem o sistema operacional
Android. No mundo, este nmero 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 mveis.
Fonte: IDC (2013).
Esse crescimento do mercado, aliado maior capacidade dos dispositivos mveis, est
impulsionando a demanda por aplicaes e jogos cada vez mais complexos, rpidos e interativos.
Isto pode ser facilmente percebido pelo volume de jogos com alta complexidade grfica que vm
sendo comercializados e tambm por aplicaes baseadas em processamento de imagens e realidade
aumentada que possibilitam a traduo simultnea de imagens com textos em outros idiomas (p.ex.:
Word Lens e CamTranslator), a pesquisa por informaes baseada em imagens (p.ex.: Google
Goggles), a simulao de moblias em ambientes a partir de imagens da cmera (p.ex.: Home
Design 3D), entre muitas outras.
Para muitas das aplicaes citadas, o desempenho obtido com o desenvolvimento baseado
apenas no Android Software Development Kit - SDK pode no ser satisfatrio. Nestes casos, o uso

2/19
do Android Native Development Kit NDK contribui para que a aplicao 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 mximo de proveito do dispositivo em aplicaes que consomem muitos
recursos de CPU ou que precisam aproveitar um hardware especfico. Ser utilizada uma aplicao
exemplo, desenvolvida e comentada passo-a-passo, para esclarecer as etapas necessrias para
atingir o objetivo proposto.

Integrao de Aplicaes Java com Cdigo Nativo


Existem duas alternativas principais para a integrao de aplicaes Java, no apenas
mveis, com cdigo nativo. A primeira delas envolve a criao de processos do sistema operacional
para a execuo do cdigo nativo. Uma forma de fazer isso por meio da classe ProcessBuilder,
disponvel a partir do Java 5 (Listagem 1). O cdigo apresentado nesta listagem autoexplicativo e
permite a recuperao da sada padro produzida pelo comando cmd /c dir /ad, executado na pasta
raiz para sistemas Windows.
Esse mesmo recurso poderia ser utilizado para executar um cdigo nativo que realiza um
clculo matemtico, por exemplo. Entre outras formas, a recuperao do resultado poderia ser feita
a partir da sada padro, como no exemplo apresentado, ou mesmo por meio de arquivos o cdigo
nativo gravaria um arquivo como sada e o cdigo Java realizaria a leitura deste arquivo e
recuperaria o resultado.
Listagem 1. Exemplo de cdigo 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 contm em ordem: comando parmetro1 parmetro2 ...
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 finalizaao do processo.
processo.waitFor();
// L a sada do comando.
BufferedReader in = new BufferedReader(new
InputStreamReader(processo.getInputStream()));
System.out.println("Sada 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 execuo do programa.");
// ...
} catch (InterruptedException erro) {
System.out.println("Sentimos muito. Ocorreu um erro durante a execuo do programa.");
// ...
}
}
}

Existem algumas implicaes relacionadas a esta abordagem. Uma delas a necessidade do


cdigo nativo ser executvel na plataforma em questo. Outra est relacionada comunicao, que
geralmente envolver o processamento de textos e converses. Ainda, no sendo possvel acessar
diretamente variveis e mtodos da mquina virtual do Java, as informaes que o mtodo nativo
necessita devem ser fornecidas previamente via parmetros ou arquivos.

3/19
A outra abordagem envolve o uso da JNI Java Native Interface, um padro que permite
que bibliotecas, no cdigo executvel, sejam integradas ao cdigo Java. por meio dela que
aplicaes Android NDK conseguem executar cdigos implementados em C/C++. Diferentemente
da outra abordagem, a JNI possibilita a interao direta com a mquina virtual do Java, no
apresentando as implicaes citadas.
A seguir, ela ser descrita mais detalhadamente.

Java Native Interface - JNI


Para utilizar a Java Native Interface JNI em um projeto, alm do cdigo nativo ter que ser
escrito seguindo algumas convenes, a biblioteca onde o cdigo nativo se encontra deve ser
carregada no cdigo Java, onde tambm devem ser escritas as assinaturas dos mtodos nativos. Por
fim, basta invocar os mtodos cujas assinaturas foram definidas e o cdigo nativo ser
automaticamente executado.
A seguir, cada uma destas etapas e alguns detalhes importantes sobre elas sero descritos.
Carregamento da biblioteca nativa
No cdigo Java, antes de acessar mtodos nativos, a biblioteca que os contm deve ser
carregada por meio do comando System.loadLibrary ou System.load. Eles so geralmente
colocados em um bloco esttico, pois o carregamento precisa ser feito apenas uma nica vez.
(Listagem 2). Caso seja realizado mais de uma vez, os carregamentos subsequentes so
desconsiderados.
Listagem 2. Exemplo de cdigo que carrega uma biblioteca chamada minhabiblioteca.
//...
public class LadoJava {
// ...
static {
System.loadLibrary("minhabiblioteca");
// ou System.load("caminhoCompleto/minhabiblioteca.dll");
}
// ...
}

No caso do mtodo loadLibrary, o Java segue um padro especfico 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 varivel java.library.path.
Para o caso do mtodo load, uma caminho completo para a biblioteca deve ser informado, o
que pode ser interessante em alguns casos.
Assinaturas nativas no cdigo Java
Alm do carregamento da biblioteca, o cdigo Java deve criar assinaturas de mtodos que
sero associadas ao cdigo nativo. Estas assinaturas exigem o modificador native na sua declarao.
Na Listagem 3, so definidas duas assinaturas, que sero associadas a dois mtodos nativos.
Listagem 3. Exemplo assinatura de mtodo nativo no cdigo 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 cdigo nativo deve ser criado para que o cdigo Java consiga
execut-lo.
Declaraes do cdigo nativo

4/19

Existem duas formas de declarar no cdigo nativo os mtodos especificados com native no
cdigo Java. A primeira delas envolve seguir algumas convenes da JNI e a forma mais rpida e
fcil de segui-las utilizar um utilitrio que acompanha o Java SDK e permite a gerao do
cabealho do cdigo-fonte nativo: o javah.
A Listagem 4 apresenta alguns exemplos de uso deste utilitrio os comentrios esto no
formato Windows. Para executar os comandos desta listagem, necessrio que a pasta bin do
SDK esteja na varivel de ambiente PATH e as classes especificadas como parmetro estejam na
varivel CLASSPATH. Os arquivos gerados pelo utilitrio so gravados na pasta de trabalho atual.
Listagem 4. Exemplo de chamada do programa javah.
:: Assinatura do comando.
javah [opes] <caminho completo da classe>[...]
:: Criando o cabealho C para a classe br.com.fatec.projetomm.LadoJava.
javah jni br.com.fatec.projetomm.LadoJava
:: A opo jni padro, 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 */
#ifndef _Included_br_com_fatec_projetomm_LadoJava
#define _Included_br_com_fatec_projetomm_LadoJava
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class:
br_com_fatec_projetomm_LadoJava
* Method:
fazAlgumaCoisa
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_br_com_fatec_projetomm_LadoJava_fazAlgumaCoisa
(JNIEnv *, jobject);
/*
* Class:
br_com_fatec_projetomm_LadoJava
* Method:
fazAlgumaOutraCoisa
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_br_com_fatec_projetomm_LadoJava_fazAlgumaOutraCoisa
(JNIEnv *, jclass);
#ifdef __cplusplus
}
#endif
#endif

Como pode ser percebido no cdigo gerado pelo javah, alm da incluso da biblioteca
jni.h e de algumas definies, todo mtodo nativo inicia com JNIEXPORT <tipo de retorno>
JNICALL e nomeado com o prefixo Java_, seguido por um nome de pacote, de classe e de
mtodo. Os pontos . presentes nos nomes devem ser substitudos pelo sinal underscore _. Entre
os nomes do pacote, da classe e do mtodo tambm deve existir um underscore. Estes nomes
correspondem aos da classe Java na qual foi implementado o mtodo com o modificador native.
As referncias a JNIEXPORT e JNICALL podem ser omitidas no Linux, pois no tm
funo, mas so geralmente mantidas para compatibilidade com o Windows, onde eliminam a
necessidade de arquivos adicionais para a definio de mdulo, entre outras coisas.
A outra forma de declarar os mtodos nativos seria, ao invs de seguir este padro de
nomeao, sobrescrever o mtodo JNI_OnLoad(JavaVM* vm, void* reserved), executado
automaticamente quando a biblioteca carregada, e nele registrar os mtodos nativos por meio da

5/19

funo JNI RegisterNatives. Neste caso, os nomes dos mtodos poderiam ser arbitrrios no cdigo
nativo. Esta funo deve retornar um nmero inteiro com a verso do JNI exigida pela biblioteca.
Alm destas convenes, para permitir que mtodos nativos acessem os recursos disponveis
na mquina virtual onde foi iniciada a sua execuo, dois parmetros so passados para eles: um
ponteiro de interface, que aponta para um array de funes JNI, e uma referncia para o objeto
(jobject) onde a chamada foi realizada no cdigo Java veja a definio de fazAlgumaCoisa.
Caso o cdigo nativo seja executado a partir de um contexto esttico no cdigo Java, no h
objeto associado a chamada e, neste caso, a classe (jclass) do objeto passada por parmetro veja
a definio de fazAlgumaOutraCoisa. No cdigo nativo em C, jobject pode ser utilizado no lugar de
jclass, porque este apenas um apelido para jobject. Este no o caso do C++, onde h uma
definio de tipos e subtipos.
No exemplo apresentado, o objeto passado para fazAlgumaCoisa seria da classe LadoJava,
onde foi declarada a assinatura do mtodo nativo.
Estes parmetros so transparentes para o cdigo Java que invoca o mtodo nativo
(Listagem 6), ou seja, no precisam ser explicitamente passados pelo programador. Desta forma, as
chamadas ao cdigo nativo podem ser feitas como se a interao fosse com outro cdigo Java
qualquer.
Listagem 6. Demonstrao da passagem implcita de parmetros na invocao de cdigo
nativo.
//...
public class LadoJava {
// ... Carregamento da biblioteca ...
// ... Declarao da assinatura dos mtodos nativos ...
void outroMetodoQualquer() {
// Perceba que, embora a implementao nativa do mtodo "fazAlgumaCoisa" receba 2 parmetros,
// eles so passados implicitamente pelo Java.
System.out.println(fazAlgumaCoisa());
}
}

O ponteiro de interface do tipo JNIEnv e possibilita o acesso a diversas funes JNI. Entre
outras coisas, estas funes permitem a criao de objetos na memria controlada pela mquina
virtual. Alguns exemplos de funes JNI so apresentados no cdigo da Listagem 7.
Mapeamento de dados e parmetros
Para que haja compatibilidade entre as variveis criadas no cdigo Java e no nativo, foram
criados mapeamentos no cdigo nativo para os tipos primitivos e objetos Java, conforme listado na
Tabela 2.
Tipo Java
Object
Class
boolean
byte
char
short
int
long
float
double
Object[]
boolean[]
byte[]

Tipo Nativo
jobject
jclass
jboolean
jbyte
jchar
jshort
jint
jlong
jfloat
jdouble
jobjectArray
jbooleanArray
jbyteArray

6/19

char[]
jcharArray
short[]
jshortArray
int[]
jintArray
long[]
jlongArray
float[]
jfloatArray
double[]
jdoubleArray
Throwable
jthrowable
String
jstring
Tabela 2. Mapeamento entre tipos Java e nativos.
Conforme comentado anteriormente para o relacionamento entre jclass e jobject em C, todos
os tipos de classes (arrays, jstring, jthrowable) so apenas apelidos para jobject.
Antes de prosseguir, vejamos um exemplo que envolve o que foi apresentado at ento.
Suponha que seja necessrio criar e preencher uma ArrayList com algumas Strings no cdigo nativo
e retorn-la para a aplicao Java que o executou (Listagem 7).
Listagem 7. Cdigo nativo que cria e preenche uma ArrayList.
//...
JNIEXPORT jobject JNICALL Java_br_com_fatec_projetomm_Principal_criarArrayList(
JNIEnv *env, jobject objeto) {
// Recupera uma referncia para a classe java.util.ArrayList.
jclass classeArrayList = (*env)->FindClass(env, "java/util/ArrayList");
// Recupera uma referncia para o construtor vazio da classe.
jmethodID metodoConstrutorVazio = (*env)->GetMethodID(env, classeArrayList,
"<init>", "()V");
// Cria uma nova ArrayList.
jobject objetoArrayList = (*env)->NewObject(env, classeArrayList,
metodoConstrutorVazio);
// Recupera uma referncia para o mtodo add da classe ArrayList.
jmethodID metodoAdd = (*env)->GetMethodID(env, classeArrayList, "add",
"(Ljava/lang/Object;)Z");
// Insere dez inteiros na ArrayList.
int i = 0;
for(i = 0; i < 10; i++) {
jstring elemento = (*env)->NewStringUTF(env, "Elemento");
(*env)->CallObjectMethod(env, objetoArrayList, metodoAdd, elemento);
}
// Retorna a ArrayList.
return objetoArrayList;
}

Neste cdigo, so utilizadas diversas funes JNI: FindClass, GetMethodID, NewObject,


NewStringUTF e CallObjectMethod, alm de vrios tipos: jclass, jmethodID, jobject e jstring.
Como pode ser observado, todos os mtodos invocados a partir do ponteiro de funes JNI
recebem o ponteiro como primeiro parmetro.
As duas primeiras funes JNI invocadas so FindClass e GetMethodID, que recuperam,
respectivamente, uma referncia para uma classe e um mtodo, necessrias para a criao de um
objeto. Desta forma, para criar uma ArrayList, foram inicialmente recuperadas referncias para a
classe java.util.ArrayList e para o mtodo construtor vazio.
A recuperao da referncia para a classe utiliza barras / ao invs de pontos .. No caso
de mtodos, seu nome deve ser utilizado, mas, por tratar-se de um construtor, foi utilizado o nome
<init>. Alm disso, para mtodos, deve ser informada a assinatura, com parmetros entre
parnteses, seguidos do tipo de retorno, aps os parnteses. Para o exemplo apresentado, a
assinatura ()V, indicando que ele no recebe parmetros, (), e no tem tipo de retorno, V de
void.

7/19

Em seguida, um objeto criado por meio da funo NewObject, que recebe as referncias e
eventuais parmetros no caso, nenhum foi passado por tratar-se do construtor vazio.
Por fim, recuperada uma referncia para o mtodo add e so adicionadas 10 Strings criadas
em um loop ArrayList. As strings so criadas por meio da funo NewStringUTF, que recebe
como parmetro o valor da String.
Perceba que, na assinatura utilizada para a recuperao da referncia para o mtodo add, foi
utilizado o nome da classe, precedido de L e sucedido por ;. Sempre que existirem classes na
assinatura, esse padro deve ser seguido.
importante que fique claro que escrever cdigos nativos que simplesmente invocam
funes Java, como no exemplo apresentado, no resulta em um ganho de desempenho - pelo
contrrio. No entanto, na maioria das vezes, ser necessrio criar algum objeto ou executar algumas
funes do Java a partir do cdigo nativo.
Sobrecarga de mtodos nativos
Caso exista sobrecarga de mtodos, devem ser acrescentados ao nome do mtodo nativo
dois caracteres underscore __, seguidos dos tipos dos parmetros que o mtodo recebe. Como
exemplo, veja o resultado gerado pelo comando javah (Listagem 9) a partir do cdigo Java da
Listagem 8.
Listagem 8. Cdigo Java com assinatura de mtodos nativos sobrecarregados.
//...
public class LadoJavaSobrecarga {
//...
public native int metodoSobrecarregado();
public native int metodoSobrecarregado(int a);
public native int metodoSobrecarregado(int a, int b);
public native int metodoSobrecarregado(double a, int b);
public native int metodoSobrecarregado(boolean a);
public native int metodoSobrecarregado(short a);
public native int metodoSobrecarregado(long a);
public native int metodoSobrecarregado(char a);
public native int metodoSobrecarregado(String a);
//...
}

Listagem 9. Cdigo nativo gerado pelo programa javah para o cdigo Java da Listagem
8.
//...
JNIEXPORT jint JNICALL Java_br_com_fatec_luque_wm_LadoJavaSobrecarga_metodoSobrecarregado__
(JNIEnv *, jobject);
JNIEXPORT jint JNICALL Java_br_com_fatec_luque_wm_LadoJavaSobrecarga_metodoSobrecarregado__I
(JNIEnv *, jobject, jint);
JNIEXPORT jint JNICALL Java_br_com_fatec_luque_wm_LadoJavaSobrecarga_metodoSobrecarregado__II
(JNIEnv *, jobject, jint, jint);
JNIEXPORT jint JNICALL Java_br_com_fatec_luque_wm_LadoJavaSobrecarga_metodoSobrecarregado__DI
(JNIEnv *, jobject, jdouble, jint);
JNIEXPORT jint JNICALL Java_br_com_fatec_luque_wm_LadoJavaSobrecarga_metodoSobrecarregado__Z
(JNIEnv *, jobject, jboolean);
JNIEXPORT jint JNICALL Java_br_com_fatec_luque_wm_LadoJavaSobrecarga_metodoSobrecarregado__S
(JNIEnv *, jobject, jshort);
JNIEXPORT jint JNICALL Java_br_com_fatec_luque_wm_LadoJavaSobrecarga_metodoSobrecarregado__J
(JNIEnv *, jobject, jlong);
JNIEXPORT jint JNICALL Java_br_com_fatec_luque_wm_LadoJavaSobrecarga_metodoSobrecarregado__C
(JNIEnv *, jobject, jchar);
JNIEXPORT jint JNICALL
Java_br_com_fatec_luque_wm_LadoJavaSobrecarga_metodoSobrecarregado__Ljava_lang_String_2
(JNIEnv *, jobject, jstring);
//...

Como pode ser observado no cdigo desta listagem, aps os dois underscores, a assinatura
dos parmetros definida. Quando se trata de tipo primitivo, apenas uma letra utilizada, conforme
Tabela 3. Para o caso de classes, o nome completo dela utilizado, precedido de L, substituindo o
ponto . por underscore - como no caso da String, Ljava_lang_String e inserindo um _2 no

8/19
final. O _2 representa um caractere ;, que no pode aparecer em nomes de mtodos. Alm dele,
_1 utilizado para _ e _3 para [.
Tipo Nativo
jboolean
jbyte
jchar
jshort
jint
jlong
jfloat
jdouble
void
Tabela 3. Assinaturas de alguns tipos nativos.

Assinatura
Z
B
C
S
I
J
F
D
V

O contedo abordado at ento apresentou uma viso geral de JNI e como ela pode ser
utilizada para criar projetos Java que interagem com cdigo nativo. No caso do Android, para que
isso seja possvel, necessrio preparar o ambiente para o NDK. A seguir, veremos como isso pode
ser feito e estudaremos uma aplicao exemplo que usa o que foi discutido at ento para melhorar
o desempenho de aplicaes Android.

Preparao do Ambiente
Nesta seo, sero apresentados todos os passos para a configurao de um projeto que usa o
NDK.
Estamos assumindo que voc j possui o ambiente para desenvolvimento com o Android
SDK instalado se este no for o caso, acesse o site do desenvolvedor Android, copie o Android
Developer Tools - ADT bundle e o instale. Estamos assumindo tambm que a verso de referncia
aquela distribuda com o Eclipse, no com o IntelliJ IDEA.
Para instalar o NDK, o mesmo site do desenvolvedor pode ser acessado e a ltima verso do
NDK pode ser copiada e instalada a instalao envolve simplesmente a descompactao do
arquivo copiado em alguma pasta. Aps terminar a instalao, inicie o Eclipse ADT, acesse o menu
de preferncias (Window> Preferences) e, dentro da guia Android, selecione NDK. Defina a pasta
onde voc o descompactou.
O prximo passo envolve a criao de uma pasta chamada JNI no seu projeto. Nesta pasta,
sero colocados os arquivos-fonte nativos e o makefile, com informaes sobre a compilao do
cdigo nativo. Esta pasta deve ficar no nvel logo abaixo do projeto e no em src.
Em seguida, converta o projeto em um projeto misto Java e C/C++. Para isso, pressione
Ctrl+N ou selecione File > New > Other, e escolha a opo C/C++ > Convert to a C/C++ Project
(Adds C/C++ Nature) (Figura 1). Clique em prximo (Next >) e selecione o projeto que deseja
converter e o tipo C Project.
Na prxima tela, selecione Makefile Project e Other Toolchain e, ento, Finish (Figura 2).
Isso far com que a IDE pergunte a voc se deseja abrir a perspectiva C/C++.
Mesmo aps esta etapa, voc continuar podendo trabalhar com cdigo Java no seu projeto
ela apenas adiciona a possibilidade de trabalhar com cdigo nativo em C/C++.

9/19

Figura 1. Seleo do projeto que ser convertido para C/C++.

Figura 2. Configurao do projeto que ser convertido para C/C++.


Para poder configurar o script de construo do cdigo C/C++ no Eclipse, voc deve inserir
na varivel de ambiente PATH o caminho onde este script se encontra a pasta de instalao do
NDK. Tendo feito isso, acesse o menu de propriedades do seu projeto Alt+Enter sobre o projeto
ou boto direito > Properties -, selecione C/C++ Build e, na aba Builder Settings, desmarque a caixa
de verificao Use default build command e preencha a caixa Build command com ndk-build
(Figura 3).

10/19

Figura 3. Configurao do script para a construo do cdigo nativo no Eclipse.


Em seguida, selecione a aba Behaviour e marque a caixa de verificao Build on resource
save (Auto build), para que o cdigo nativo seja automaticamente reconstrudo quando o projeto
for gravado (Figura 4).

Figura 4. Configurao da construo do cdigo nativo no Eclipse.


Aps selecionar OK, caso voc no tenha configurado a varivel PATH para o script, o
Eclipse informar a existncia de um erro no projeto: Error: Program "ndk-build" is not found in
PATH.

11/19

Agora, precisamos incluir as bibliotecas em C/C++ para a escrita do cdigo fonte. Para isso,
selecione Properties > C/C++ General> Paths and symbols. Clique em Add, marque as os locais
all-configurations e all-languages e insira o caminho da pasta include contida dentro do
NDK. Como utilizaremos a verso 9 do Android no nosso projeto, esta pasta est localizada em
<raiz do NDK>\platforms\android-9\arch-arm\usr\include.
Crie um arquivo chamado Android.mk na pasta jni do projeto, com instrues para a
compilao do cdigo nativo. Um exemplo de arquivo apresentado na Listagem 10. Entre outras
coisas, esto definidos nele o nome da biblioteca, que ser utilizado no comando
System.loadLibrary, os arquivos fontes e as bibliotecas utilizadas nos arquivos fontes.
Listagem 10. Exemplo de makefile Android.mk.
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE
:= bibliotecanativa
LOCAL_SRC_FILES := Biblioteca.c
LOCAL_LDLIBS
:= -llog -ljnigraphics
include $(BUILD_SHARED_LIBRARY)

Seguidas estas etapas, o seu ambiente j est pronto para permitir a criao de projetos
Android que utilizam cdigo nativo. A seguir, ser apresentado um exemplo de aplicao deste tipo.

A Aplicao Exemplo
Para exemplificar o uso do Android NDK, desenvolveremos uma aplicao simples para
melhorar a qualidade de imagens por meio de duas tcnicas de Processamento de Imagens:
filtragem da mediana e normalizao. Nesta aplicao, a visualizao e interao com o usurio
ficaro sob responsabilidade do cdigo Java, enquanto o processamento ser realizado pelo cdigo
nativo, escrito em C.
Para o entendimento dos cdigos que sero apresentados, necessrio conhecer alguns
conceitos bsicos de Processamento de Imagens, discutidos a seguir. Uma imagem digital
monocromtica pode ser entendida como uma funo f(x,y) que mapeia uma posio (x,y) para uma
intensidade de brilho, geralmente representada no intervalo [0, 255]. Essa funo pode ser
visualizada como uma matriz, conforme apresentado na Figura 5 e cada clula desta matriz
conhecida como pixel (Picture Element).
10 114 15
2 76
216 68 251 110 22
198 3 134 198 3

Figura 5. Exemplo de matriz de imagem monocromtica.


Um valor prximo a 0 indica uma intensidade de brilho baixa (escuro), enquanto um valor
prximo a 255 indica uma intensidade de brilho alta (claro).
Em se tratando de imagens coloridas, ao invs de um nico valor de intensidade de brilho
para cada pixel, so utilizados valores para vrias cores que juntas compem a cor do pixel. O
modelo mais comum de cores para imagens o RGB, no qual cada pixel possui trs valores no
intervalo [0, 255] um para o vermelho (Red), outro para o verde (Green) e um ltimo para o azul
(Blue). Assim sendo, nestas imagens, ao invs de uma nica matriz, temos trs matrizes, podendo
ser acrescentada estas uma matriz com valores de transparncia, conhecida como alpha.
Existem diversas tcnicas que permitem a correo de caractersticas indesejveis em
imagens por meio da manipulao das suas matrizes. Uma delas a Filtragem da Mediana, que
permite a reduo de rudos variaes aleatrias no padro de cores da imagem. A Figura 6
apresenta um exemplo de imagem com rudos e a mesma imagem aps a filtragem da mediana.

12/19

Figura 6. esquerda, imagem com rudo. direita, a mesma imagem aps a filtragem da
mediana.
O filtro da mediana consiste em substituir a intensidade de cor de cada pixel pela mediana
das intensidades de cor na vizinhana do pixel. A mediana de um conjunto de pixels igual ao
elemento central do conjunto ordenado (se o nmero de elementos for mpar) ou a mdia aritmtica
dos dois elementos centrais (se o nmero de elementos for par).
O tamanho da vizinhana determinado por uma matriz conhecida como mscara. Deve-se
posicionar a mscara centralizada no pixel que se deseja filtrar e todos os pixels que ficarem abaixo
dela participaro do clculo da mediana.
Desta forma, esta tcnica pode ser entendida como o deslocamento de uma janela (mscara)
pelos pixels da imagem e pela substituio de seus valores a partir do clculo da mediana entre seus
vizinhos. Veja o exemplo seguinte para uma mscara de tamanho 3x3. Ser utilizada uma imagem
monocromtica (Figura 7), mas a mesma tcnica poderia ser empregada em imagens coloridas,
bastando que o mesmo procedimento fosse repetido independentemente em cada uma das matrizes
R, G e B.

13/19

Figura 7 Exemplo de aplicao do filtro da mediana com mscara 3x3.


Outra tcnica a normalizao, que permite uma melhor distribuio dos valores dos pixels
no intervalo vlido entre 0 e 255, geralmente resultando em um aumento no contraste da imagem.
Para aplic-la, basta utilizar uma regra de trs simples procurando mapear o intervalo de valores de
uma imagem para o intervalo [0, 255].
Para fazer isso, devemos encontrar o maior (max) e menor (min) valor da matriz da imagem
e, para cada um dos seus pixels (p), calcular um novo valor (x) a partir da frmula da Figura 8.

Figura 8. Frmula de normalizao.


A Figura 9 apresenta um exemplo de imagem com baixo contraste e o resultado aps a
normalizao.

14/19

Figura 9. Exemplo de aplicao da normalizao. Imagem com baixo contraste (


esquerda) e normalizada ( direita).
Entendidos estes conceitos, veremos como funcionar a aplicao. Ela possui um nico
layout (Figura 10), onde so exibidos rtulos, duas imagens uma que aberta pelo usurio e outra
para exibir o resultado e trs botes. Um dos botes permite o carregamento de imagens a partir
da galeria do dispositivo e do GDrive. Os outros dois aplicam a filtragem da mediana e a
normalizao, nesta ordem, sendo que um deles executa todo o processamento em Java e o outro em
C.

Figura 10. Representao grfica do layout da aplicao exemplo.


O tempo de processamento calculado e exibido logo acima da imagem resultante,
permitindo a comparao de desempenho entre o cdigo Java e o nativo.
Uma imagem em Android representada pela classe Bitmap e pode ser desenhada em
ImageViews, como os presentes na interface da aplicao. Um Bitmap possui vrios mtodos que
permitem a recuperao e alterao dos valores dos seus pixels.

15/19

No processamento utilizado, foram utilizados os mtodos getPixels e setPixels. O primeiro


deles retorna os pixels da imagem como uma array de nmeros inteiros, na qual, em cada posio,
esto armazenados os valores R, G e B de cada pixel. Para recuper-los deve ser utilizado o
deslocamento de bits.
O cdigo que aplica o filtro da mediana e a normalizao (Listagem 11 e 12) envolve as
seguintes etapas: recuperao dos pixels da imagem em uma array de inteiros, passagem por todos
os pixels da imagem e clculo da mediana da vizinhana de cada pixel. Aps o clculo da mediana,
o maior e o menor valor da matriz resultante so recuperados. Ento, passe-se novamente pelos
pixels e aplicada a normalizao. Por fim, os pixels so desenhados novamente na imagem.
Listagem 11. Cdigo Java que aplica o filtro da mediana e a normalizao imagem.
/**
* Reduz rudos (filtro da mediana) e tenta aumentar o contraste da imagem
* por meio da normalizao - verso nativa.
* @param imagem um array com os pixels da imagem original.
* @param largura a largura da imagem original;
* @param altura a altura da imagem original.
* @param linhas o nmero de linhas da matriz da mscara.
* @param colunas o nmero de colunas da matriz da mscara.
* @return um array com os pixels da imagem original aps ela ter sido
*
filtrada com o filtro da mediana e seu contraste ter sido
*
aumentado com a normalizao.
*/
public static native int[] melhorarQualidadeNativo(int[] pixels,
int largura, int altura, int linhas, int colunas);
public static int[] melhorarQualidade(int[] pixels, int largura,
int altura, int linhas, int colunas) {
// Calcula o nmero de pixels da imagem.
int tamanho = largura * altura;
// Calcula quantos pixels acima, abaixo, direita e esquerda devero
// ser visitados para calcular a mediana.
int deltaLinha = (linhas - 1) / 2;
int deltaColuna = (colunas - 1) / 2;
int tamanhoMascara = linhas * colunas;
// Cria um array onde sero armazenados os pixels resultantes.
int pixelsResultantes[] = new int[tamanho];
// Variveis para armazenar o maior e menor elemento para normalizar a imagem.
int maiorR = Integer.MIN_VALUE;
int menorR = Integer.MAX_VALUE;
// ... o mesmo para G e B.
// Para cada pixel da imagem, calcula a mediana entre os pixels sobrepostos pela mscara.
int i = 0, x = 0, y = 0;
int conjuntoMedianaR[] = new int[tamanhoMascara];
// conjuntoMedianaG, conjuntoMedianaB ...
for (i = 0; i < tamanho; i++) {
// Nmero de pixels vlidos que sero utilizados no clculo da mediana.
int elementos = 0;
// Passa pelos pixels vizinhos.
for (x = -deltaLinha; x <= deltaLinha; x++) {
for (y = -deltaColuna; y <= deltaColuna; y++) {
// Verifica se o pixel vlido, ou seja, se est dentro da imagem.
int indice = i + x + (y * largura);
if (indice >= 0 && indice < tamanho) {
// Recupera uma referncia para o pixel e seus
// componentes
// R, G e B.
int pixel = pixels[indice];
int R = (pixel >> 16) & 0xff;
int G = (pixel >> 8) & 0xff;
int B = pixel & 0xff;
// Armazena o elemento que ser utilizado no clculo da
// mediana.
conjuntoMedianaR[elementos] = R;
// conjuntoMedianaG, conjuntoMedianaB ...
elementos++;
}
}

16/19
}
// Finaliza o clculo da mediana
int medianaR = mediana(conjuntoMedianaR, elementos);
// ... o mesmo para G e B.
// Encontra o maior
maiorR = medianaR >
menorR = medianaR <
// ... o mesmo para

e menor elemento para normalizar a imagem.


maiorR ? medianaR : maiorR;
menorR ? medianaR : menorR;
G e B.

// Armazena o pixel resultante.


pixelsResultantes[i] = (255 << 24) | (medianaR << 16)
| (medianaG << 8) | medianaB;
}
// Normaliza a imagem
double fatorR = 255.0
double fatorG = 255.0
double fatorB = 255.0

para corrigir o contraste.


/ (maiorR - menorR);
/ (maiorG - menorG);
/ (maiorB - menorB);

for (i = 0; i < tamanho; i++) {


// Recupera uma referncia para o pixel e os valores R, G e B
// para ele.
int pixel = pixelsResultantes[i];
// ... Recupera o valor dos pixels, como anteriormente.
// Calcula o novo valor para o pixel.
int novoValorPixelR = (int) ((R - menorR) * fatorR);
// ... o mesmo para G e B.
pixelsResultantes[i] = (255 << 24) | (novoValorPixelR << 16)
| (novoValorPixelG << 8) | novoValorPixelB;
}
return pixelsResultantes;
}

Comparando o cdigo Java e o nativo, a nica diferena existente aquela relacionada


recuperao e retorno da array. No cdigo nativo, para no ter que invocar funes JNI de
recuperao do valor de cada pixel, invocamos inicialmente uma funo que armazena a array em
formato diretamente acessvel pelo cdigo nativo. No retorno, fazemos o contrrio, para tambm
no ter que definir o valor dos pixels um a um invocando funes JNI.
Listagem 12. Cdigo nativo que aplica o filtro da mediana e a normalizao imagem.
#include <jni.h>
#include <android/bitmap.h>
// ...
// Os parmetros so equivalentes ao cdigo j apresentado em Java.
// Alguns comentrios foram omitidos por serem equivalentes aos j apresentados
// no cdigo Java.
JNIEXPORT jobject JNICALL Java_br_com_fatec_projetomm_ImagemUtil_melhorarQualidadeNativo(
JNIEnv *env, jclass classe, jintArray imagem, jint largura, jint altura,
jint linhas, jint colunas) {
// Calcula algumas medidas importantes.
int tamanho = largura * altura;
int deltaLinha = (linhas - 1) / 2;
int deltaColuna = (colunas - 1) / 2;
int tamanhoMascara = linhas * colunas;
// Recupera os pixels da imagem.
jint* pixels = (*env)->GetIntArrayElements(env, imagem, 0);
jint pixelsResultantes[tamanho];
int maiorR = INT32_MIN;
int menorR = INT32_MAX;
// ...
// Para cada pixel da imagem,
int i = 0, x = 0, y = 0;
int conjuntoMedianaR[tamanhoMascara];
// ...
for (i = 0; i < tamanho; i++) {
int elementos = 0;
for (x = -deltaLinha; x <= deltaLinha; x++) {

17/19
for (y = -deltaColuna; y <= deltaColuna; y++) {
jint indice = i + x + (y * largura);
if (indice >= 0 && indice < tamanho) {
jint pixel = pixels[indice];
int R = (pixel >> 16) & 0xff;
int G = (pixel >> 8) & 0xff;
int B = pixel & 0xff;
conjuntoMedianaR[elementos] = R;
// ...
elementos++;
}
}
}
int medianaR = mediana(conjuntoMedianaR, elementos);
// ...
maiorR = medianaR > maiorR ? medianaR : maiorR;
maiorG = medianaG > maiorG ? medianaG : maiorG;
// ...
pixelsResultantes[i] = (ALFA << 24) | (medianaR << 16) | (medianaG << 8)
| medianaB;
}
double fatorR = 255.0 / (maiorR - menorR);
// ...
for (i = 0; i < tamanho; i++) {
int pixel = pixelsResultantes[i];
// ... Recupera os valores dos pixels, como anteriormente.
int novoValorPixelR = (int) ((R - menorR) * fatorR);
// ...
pixelsResultantes[i] = (ALFA << 24) | (novoValorPixelR << 16)
| (novoValorPixelG << 8) | novoValorPixelB;
}
// Preenche um array com os pixels resultantes.
jintArray novosPixels = (*env)->NewIntArray(env, tamanho);
(*env)->SetIntArrayRegion(env, novosPixels, 0, tamanho, pixelsResultantes);
return novosPixels;
}

Como o processamento para a criao de objetos na mquina virtual a partir do cdigo


nativo maior do que aquele necessrio para fazer o mesmo a partir do Java, optou-se pelo mtodo
no retornar uma nova imagem Bitmap, mas sim uma array de pixels, que , nos dois casos,
desenhada na imagem por meio do cdigo Java (Listagem 13).
Listagem 13. Cdigo que invoca o processamento para o clculo da mediana e
normalizao.
/**
* Trata o evento de clique no boto de melhoria da qualidade.
*
* @param origem O boto onde o evento foi gerado.
*/
public void tratarEventoMelhoriaQualidade(View origem) {
// Se o usurio ainda no tiver selecionado uma imagem, exibe
// uma mensagem de erro e cancela a operao.
if (!validarImagemOrigem()) {
return;
}
// Marca o tempo de incio do processamento.
long inicio = System.currentTimeMillis();
// Recupera os pixels da imagem.
int[] pixels = new int[imagemCarregada.getWidth()
* imagemCarregada.getHeight()];
imagemCarregada.getPixels(pixels, 0, imagemCarregada.getWidth(), 0, 0,
imagemCarregada.getWidth(), imagemCarregada.getHeight());
// Tenta melhorar a qualidade grfica da imagem
// >>> Aqui vai a chamada para o mtodo de processamento escrito em Java ou nativo.
int[] resultado = ImagemUtil.melhorarQualidade(pixels,

18/19
imagemCarregada.getWidth(), imagemCarregada.getHeight(), 3, 3);
// Exibe a imagem resultante.
Bitmap resultante = Bitmap.createBitmap(imagemCarregada.getWidth(),
imagemCarregada.getHeight(), Bitmap.Config.ARGB_8888);
resultante.setPixels(resultado, 0, imagemCarregada.getWidth(), 0, 0,
imagemCarregada.getWidth(), imagemCarregada.getHeight());
componenteImagemResultante.setImageBitmap(resultante);
// Marca o tempo de trmino do processamento.
long termino = System.currentTimeMillis();
// Exibe o tempo total de processamento em milisegundos.
tempoProcessamento.setText("Demorou " + (termino - inicio)
+ " milisegundos.");
}

Em testes realizados em um Samsung Galaxy Tab, o cdigo nativo foi duas vezes mais
rpido que o Java para mscaras com tamanho 3x3. Esta diferena tende a crescer com o aumento
do tamanho da mscara.
O cdigo completo do projeto est disponvel no site da revista.

Concluses
Este artigo apresentou uma introduo ao NDK e como ele pode contribuir para a melhoria
do desempenho de suas aplicaes Android. A JNI, utilizada no NDK, muito extensa e apenas
uma breve introduo foi apresentada. Recomendamos a leitura dos artigos e livros na seo de
links para um aprofundamento sobre o tema.

Leandro Luque
leandro.luque@gmail.com
professor da FATEC Mogi das Cruzes, onde desenvolve pesquisas na rea de Interao
Humano-Computador, Engenharia de Software e Processamento de Imagens. Bacharel em
Cincia da Computao pela Universidade de Mogi das Cruzes e mestre em Computao
Aplicada pelo Instituto Nacional de Pesquisas Espaciais (INPE), trabalha com Java h 13 anos,
atuando no desenvolvimento de aplicaes de grande porte, tanto no segmento empresarial
quanto governamental.

19/19

Eron Silva
eron.silva@fatec.sp.gov.br
graduando em Anlise e Desenvolvimento de Sistemas pela FATEC Mogi das Cruzes. Tem dois
anos de experincia em desenvolvimento Android, principalmente de aplicaes para o
processamento de imagens e reconhecimento de cdigos de barras.

Links
Especificao da JNI
http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/jniTOC.html
Pgina de download do Android NDK
http://developer.android.com/tools/sdk/ndk/index.html
Artigo da IBM com um exemplo de aplicao desenvolvida passo-a-passo para o Android
NDK
http://www.ibm.com/developerworks/br/opensource/tutorials/os-androidndk/index.html

Livros
Pro Android C++ with the NDK
Livro Pro Android C++ with the NDK sobre programao em C++ com o NDK.
Android Native Development Kit Cookbook
Livro de receitas sobre o Android NDK.