Tutorial Android: Como configurar o NDK no Android Studio com Gradle e um exemplo

Passo a passo de como configurar o Android Studio com Gradle para compilar código nativo C usando o NDK. Como exemplo, será demonstrada uma calculadora simples.

Postado por eMMi's em 08 de setembro de 2016

Olá pessoal,

A dica de hoje será sobre o desenvolvimento de código nativo (em linguagem C) para Android usando o Android Studio como IDE e o Gradle.

Imagine que, há alguns anos atrás, você desenvolveu uma biblioteca muito legal, que faz uma tarefa bem complicada e está muito bem testada e validada. Suponha que esta biblioteca tenha sido desenvolvida na linguagem de programação mais utilizada na época (e continua muito utilizada), que é a linguagem C. Agora, imagine que surgiu um projeto para smartphone Android que necessite exatamente das funcionalidades desta sua bibilioteca. O que você faria?

  1. Desenvolver novamente a biblioteca, mas agora em Java. Rafazer todos os testes, corrigir os bugs e validar com os clientes.
  2. Incluir o código em C no projeto Android.

Você já deve ter percebido os benefícios de reutilizar seu código. Sua biblioteca já esta madura, sem bugs, testada, aprovada... Para que correr o risco de desenvolver novamente? Vai perder tempo, esforço e certamente terá dores de cabeça!

Esta é a razão do Google ter desenvolvido o NDK (Native Development Kit). Você mistura seu código nativo (em C) com o desenvolvimento em Java para Android.

Muitas pessoas já desenvolveram aplicativos Android que misturavam código nativo em C com o Java do Android, mas todos utilizavam o Eclipse como IDE e sem usar o Gradle. Este tutorial será útil para quem quer fazer exatamente a mesma coisa, mas utilizando o Android Studio e o Gradle. Para isso, basta configurar o plugin experimental do Gradle para NDK.

Para simplificar e já colocar a mão na massa, faremos um aplicativo calculadora, onde os cálculos serão feitos nativamente. Vamos começar

Criando o projeto Android

Esta parte na tem segredo. Crie um projeto Android no Android Studio como você costumar fazer.

Download do NDK

Caso você ainda não tenha feito, faça download do NDK do Android através do Android SDK Manager

Acesse o SDK Manager através do seguinte menu no Android Studio: Tools > Android > SDK Manager

Vá em SDK Tools e selecione NDK para fazer o download.

Configurando o plugin do Gradle para NDK

O primeiro passo é saber qual é a versão do Gradle que você está usando.

Vá na pasta raiz do seu projeto, acesse a pasta "gradle" depois "wrapper" e abra o arquivo gradle-wrapper.properties

Verifique a linha distributionUrl

No meu caso, a versão é a 2.10:

./gradle/wrapper/gradle-wrapper.properties


#Wed Apr 10 15:27:10 PDT 2013
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip
                                    

Para cada versão de Gradle, existe uma versão de plugin para NDK, veja a tabela:

Versão do Gradle Versão do Plugin
2.5 0.2.0
2.6 0.3.0-alpha3
2.8 0.6.0-alpha1
2.10 0.7.0
2.14.1 0.7.3

Descoberta a versão do Plugin, vamos para o próximo passo, alterar o arquivo build.gradle da que fica na raiz do seu projeto

./build.gradle

Atenção, existem dois arquivos build.gradle. Um fica no nível da raíz e outro fica dentro da pasta app. Neste ponto, a alteração será no arquivo que fica na raíz do projeto.

Para incluir o plugin, modifique a linha classpath para usar a ferramenta de compilação do gradle experimental, na versão que você já descobriu acima. Veja a parte em destaque abaixo:


// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath "com.android.tools.build:gradle-experimental:0.7.0"

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        jcenter()
    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}
                                    

./app/build.gradle

Para terminarmos a configuração do Plugin, vamos alterar o build.gradle que fica no nível do aplicativo

Na primeira linha do arquivo encontramos o campo apply plugin. Vamos modificá-lo bastando incluir a palavra model entre android e application:

apply plugin: "com.android.model.application"

Agora, vamos envolver com model { } tudo que está dentro de android { } e modificar o minSdkVersion e o targetSdkVersion para incluir o .apiLevel. O proguard também deve ser modificado e ficar da seguinde forma: proguardFiles.add(file("proguard-rules.pro"))

                                        
model {
    android {
        compileSdkVersion 23
        buildToolsVersion "23.0.2"

        defaultConfig {
            applicationId "br.com.emmis.calculadorandk"
            minSdkVersion.apiLevel 16
            targetSdkVersion.apiLevel 24
            versionCode 1
            versionName "1.0"
        }
        buildTypes {
            release {
                minifyEnabled false
                proguardFiles.add(file("proguard-rules.pro"))
            }
        }
    }
}
                                        
                                    

Por fim, vamos incluir as configurações especificas do NDK dentro de android { }

Veja abaixo para mais detalhes

                                        
model {
    android {
        compileSdkVersion 23
        buildToolsVersion "23.0.2"
        
        ndk {
            moduleName "calculadora_ndk"
            ldLibs.addAll(['android', 'log'])
        }

        sources {
            main {
                jni {
                    source {
                        srcDir "jni"
                    }
                }
            }
        }

        defaultConfig {
            applicationId "br.com.emmis.calculadorandk"
            minSdkVersion.apiLevel 16
            targetSdkVersion.apiLevel 24
            versionCode 1
            versionName "1.0"
        }
        buildTypes {
            release {
                minifyEnabled false
                proguardFiles.add(file("proguard-rules.pro"))
            }
        }
    }
}
                                        
                                    

O moduleName pode ser o nome que você desejar dar e o srcDir é o nome do diretório onde estará o seu código em linguagem nativa (C). Neste exemplo, demos o nome de jni para a pasta.

Com isso, finalizamos a configuração do Android Studio para trabalhar com NDK.

Antes de passar para o próximo passo, sugiro fervorosamente fazer uma compilação e verificar se tudo ocorreu bem.

Desenvolvendo o projeto

Terminadas as configurações do ambiente, vamos ao desenvolvimento.

Dentro da pasta app/src/main do seu projeto, vamos criar a pasta jni. Como dito acima, nesta pasta colocaremos o código em linguagem C.

Dentro da pasta jni, vamos criar o header e o source da nossa calculadora em C.

Não vou explicar como programar em C/C++ pois esta fora do escopo do tutorial. Caso queira aprender, segue um a sugestão de um bom curso:

Logica de Programação com C++

Calculadora.h


#ifndef CALCULADORANDK_CALCULADORA_H_H
#define CALCULADORANDK_CALCULADORA_H_H

int somar(int a, int b);
int subtrair(int a, int b);

#endif //CALCULADORANDK_CALCULADORA_H_H
                            

Calculadora.c


#include "Calculadora.h"

int somar(int a, int b) {
    return a + b;
}

int subtrair(int a, int b){
    return a - b;
}
                            

Com nossa calculadora em código nativo pronto, vamos criar agora uma classe em Java no Android que fará uso da nossa calculadora nativa.

A primeira coisa a fazer é carregar nossa biblioteca nativa na classe Java que desejamos utilizar. Para isso utilizamos a função System.loadLibrary passando como parametro o nome do nosso módulo NDK (moduleName que você deu no gradle.properties).

Depois de carregada, vamos criar métodos em Java que representem os métodos em C que desejamos utilizar. A palavra chave native é responsável por isso.

Este é a classe java, com o carregamento da biblioteca e os métodos Java que representam o código nativo:

CalculadoraJNI


package br.com.emmis.calculadorandk;

public class CalculadoraJNI {

    
    static {
        System.loadLibrary("calculadora_ndk");
    }

    public native int somarJNI(int a, int b);

    public native int subtrairJNI(int a, int b);
}
                            

Eu chamei esta classe de CalculadoraJNI, carreguei a biblioteca nativa "calculadora_ndk" e criei dois métodos que representarão em Java o código nativo: somarJNI e subtrairJNI.

Agora falta somente fazer o link entre esta classe em Java com os métodos nativo do Calculadora.c. Isto é feito criando um arquivo em C que represente a classe em Java e chame os métodos da calculadora.

Este arquivo terá o mesmo nome da nossa biblioteca, ou seja calculadora_ndk.c. A primeira coisa a fazer é incluir o jni.h do android. Depois devemos incluir o nosso Calculadora.h.

Inclusões feitas, agora vamos criar nosso método JNI. Não vou explicar detalhadamente, mas o médoto JNI deve sempre ter o nome iniciado por Java, seguido pelo pacote onde está a classe, substituindo o ponto por traço-baixo (underline), depois o nome da classe, depois o nome do método em Java.

Exemplo:

Nosso médoto somarJNI está na classe CalculadoraJNI. A classe por sua vez está no pacote br.com.emmis.calculadorandk. Dado isso, nosso método no arquivo jni será Java_br_com_emmis_calcularodandk_CalculadoraJNI_somarJNI.

O tipo jint é a representação JNI de int. Então nosso arquivo com os métodos JNI (meio de campo entre o C e o Java) fica de seguinte forma:

calculadora_ndk

                                
#include <jni.h>
#include "Calculadora.h"

JNIEXPORT jint JNICALL
Java_br_com_emmis_calculadorandk_CalculadoraJNI_somarJNI(JNIEnv *env, jobject instance, jint a,
                                                         jint b) {
    return somar(a, b);
}

JNIEXPORT jint JNICALL
Java_br_com_emmis_calculadorandk_CalculadoraJNI_subtrairJNI(JNIEnv *env, jobject instance, jint a,
                                                            jint b) {
    return subtrair(a, b);
}
                                
                            

E é isso. Agora é só usar sua classe CalculadoraJNI normalmente, como se fosse Java padrão. A diferença é que o cálculo matemático estará sendo feito nativamente.

Este é um exemplo bem simples que mostra como usar código nativo em Android.

Se desejar, pode fazer o download do exemplo aqui: CalculadoraNDK.zip

Problemas e Soluções

Possivelmente você vai se deparar com algum erro durante a compilação/execução do exemplo. Vou colocar aqui alguns erros mais comuns e suas soluções!

Plugin with id 'com.android.model.application' not found.

Verifique se você mudou o classpath para classpath "com.android.tools.build:gradle-experimental:0.7.0" no arquivo gradle.properties da pasta raíz do projeto.

Plugin with id 'com.android.application' not found.

Verifique se você mudou o apply plugin para apply plugin: "com.android.model.application" no arquivo gradle.properties da pasta app.

Gradle DSL method not found: 'android()'

Verifique se você colocou a tag model envolvendo a tag android no gradle.properties da pasta app.

Error:Cause: org.gradle.api.internal.ExtensibleDynamicObject

Check se você colocou o .apiLevel no minSdkVersion e targetSdkVersion no gradle.properties da pasta app..

Error:No signature of method: org.gradle.model.ModelMap.getDefaultProguardFile() is applicable for argument types: (java.lang.String) values: [proguard-android.txt]

Verifique se você alterou o a configuração do proguardFiles no gradle.properties da pasta app.

Espero que tenham gostado!

Deixe seu Comentário:

{{errorName}}
{{errorEmail}}
{{errorMessage}}
{{successMessage}}

{{x.name}} {{x.datetime}}