Conceitos

Antes de começar

Este guia presume que você já conheça bem os conceitos inerentes à programação nativa e ao desenvolvimento para Android.

Introdução

Esta seção traz uma explicação de nível avançado sobre como o NDK funciona. O Android NDK é um conjunto de ferramentas que permitem incorporar C ou C++ (“código nativo”) em apps Android. A capacidade de usar código nativo em apps Android pode ser muito útil para desenvolvedores que querem:

  • portar apps entre plataformas;
  • reutilizar bibliotecas existentes ou fornecer as próprias para reutilização;
  • melhorar o desempenho em determinados casos, em especial nos de computação intensa, como jogos.

Como funciona

Esta seção apresenta os principais componentes usados na compilação de um app nativo para Android e descreve o processo de compilação e empacotamento.

Principais componentes

É necessário conhecer os seguintes componentes durante a compilação do seu app:

  • Bibliotecas compartilhadas nativas: o NDK compila essas bibliotecas ou arquivos .so a partir do código-fonte C/C++.
  • Bibliotecas estáticas nativas: o NDK também pode compilar bibliotecas estáticas ou arquivos .a que podem ser vinculados a outras bibliotecas.
  • Java Native Interface (JNI): a JNI é a interface de comunicação entre os componentes Java e C++. Esse guia requer conhecimentos sobre JNI. Para mais informações sobre esse recurso, consulte a Especificação da Java Native Interface.
  • Interface Binária de App (ABI): a ABI define exatamente como deverá ser a interação entre o código de máquina do app e o sistema no momento da execução. O NDK compila arquivos .so de acordo com essas definições. Diferentes ABIs correspondem a diferentes arquiteturas: o NDK é compatível com ABI para ARM de 32 bits, AArch64, x86 e x86-64. Para mais informações, consulte Gerenciamento de ABI.
  • Manifesto: se você está programando um app sem componente em Java, precisa declarar a classe NativeActivity no manifest. A seção Apps e atividades nativos fornece mais detalhes sobre como fazer isso, em “Usar a interface native_activity.h”.

Fluxo

O fluxo geral para desenvolver um app nativo para Android é o seguinte:

  1. Projete o app e decida que partes serão implementadas em Java e em código nativo.

    Observação: embora seja possível evitar totalmente o uso de Java, a estrutura Java para Android é útil para determinadas tarefas, como controlar a tela e a IU.

  2. Crie um projeto de app para Android normalmente.
  3. Se você está programando um app exclusivamente nativo, declare a classe NativeActivity em AndroidManifest.xml. Para mais informações, consulte Apps e atividades nativos.
  4. Crie um arquivo Android.mk que descreva a biblioteca nativa, incluindo nome, sinalizações, bibliotecas vinculadas e arquivos de origem a serem compilados no diretório “JNI”.
  5. Você também pode criar um arquivo Application.mk configurando as ABIs de destino, conjuntos de ferramentas, modo de liberação/depuração e STL. Para qualquer um desses itens que você não especificar, os valores padrão a seguir serão usados, respectivamente:
    • ABI: todas as ABIs não obsoletas
    • Conjunto de ferramentas: clang
    • Modo: liberação
    • STL: system
  6. Coloque seu código de origem nativo no diretório jni do projeto.
  7. Use o ndk-build para compilar as bibliotecas nativas (.so, .a).
  8. Compile o componente Java, produzindo o arquivo .dex executável.
  9. Empacote tudo em um arquivo do APK que contenha .so, .dex e outros arquivos necessários para a execução do app.

Apps e atividades nativos

O Android SDK fornece uma classe auxiliar, NativeActivity, que permite programar uma atividade completamente nativa. NativeActivity lida com a comunicação entre a estrutura do Android e seu código nativo para que você não precise criar uma subclassificação nem chamar os métodos. Basta declarar seu app como nativo no arquivo AndroidManifest.xml e começar a desenvolver seu app nativo.

Os apps Android que usam NativeActivity ainda podem ser executados na própria máquina virtual, no sandbox de outros apps. Sendo assim, ainda será possível acessar APIs da estrutura do Android por meio da JNI. No entanto, em alguns casos (por exemplo, para sensores, eventos de entrada e recursos), o NDK fornece interfaces nativas que podem ser usadas em vez de precisar chamar na JNI. Para mais informações sobre essas compatibilidades, consulte APIs nativas do Android NDK.

Independentemente de estar desenvolvendo ou não uma atividade nativa, recomendamos que você crie projetos com as ferramentas de compilação tradicionais do Android. Isso ajuda a garantir a compilação e o empacotamento de apps Android com a estrutura correta.

O Android NDK fornece duas possibilidades para implementar atividades nativas:

  • O cabeçalho native_activity.h define a versão nativa da classe NativeActivity. Ele contém a interface de callback e as estruturas de dados necessárias para criar sua atividade nativa. Como a linha de execução principal do app lida com os callbacks, suas implementações de callback não podem gerar bloqueios. Se isso acontecer, você poderá receber erros do tipo "O app não está respondendo" (ANR, na sigla em inglês), porque a linha de execução principal não responderá até o retorno do callback.
  • O arquivo android_native_app_glue.h define uma biblioteca auxiliar estática compilada no topo da interface native_activity.h. Ele gera outra linha de execução, que lida com elementos como callbacks ou eventos de entrada em um loop de evento. Mover esses eventos para uma linha de execução separada impede que qualquer callback bloqueie sua linha de execução principal.

A fonte <ndk_root>/sources/android/native_app_glue/android_native_app_glue.c também está disponível, permitindo modificar a implementação.

Para mais informações sobre como usar essa biblioteca estática, veja o exemplo de app de atividade nativa e a documentação correspondente. Para mais informações, leia também os comentários do arquivo <ndk_root>/sources/android/native_app_glue/android_native_app_glue.h.

Usar a interface native_activity.h

Para implementar uma atividade nativa com a interface native_activity.h:

  1. Crie um diretório jni/ no diretório raiz do projeto. Esse diretório armazenará todos os códigos nativos.
  2. Declare sua atividade nativa no arquivo AndroidManifest.xml.
  3. Como seu app não tem código em Java, configure android:hasCode como false.

        <application android:label="@string/app_name" android:hasCode="false">
        

    Defina o atributo android:name da tag de atividade como NativeActivity.

        <activity android:name="android.app.NativeActivity"
                    android:label="@string/app_name">
        

    Observação: você pode criar uma subclasse para NativeActivity. Se fizer isso, use o nome da subclasse em vez de NativeActivity.

    O atributo android:value da tag meta-data especifica o nome da biblioteca compartilhada que contém o ponto de entrada do app (como main em C/C++), omitindo o prefixo lib e o sufixo .so do nome da biblioteca.

                  <meta-data android:name="android.app.lib_name"
                    android:value="native-activity" />
                    <intent-filter>
                      <action android:name="android.intent.action.MAIN" />
                      <category android:name="android.intent.category.LAUNCHER" />
                    </intent-filter>
                  </activity>
                </application>
              </manifest>
        
  4. Crie um arquivo para a atividade nativa e implemente a função nomeada na variável ANativeActivity_onCreate. O app chama essa função quando a atividade nativa é iniciada. Essa função é semelhante ao main no C/C++. Ela recebe um ponteiro para uma estrutura ANativeActivity, que contém ponteiros de função para as diferentes implementações de callback que você precisa escrever. Defina os ponteiros de função de retorno de chamada aplicáveis em ANativeActivity->callbacks para as implementações dos seus callbacks.
  5. Defina o campo ANativeActivity->instance como o endereço de uma instância de dados específicos que você queira usar.
  6. Implemente tudo o que a atividade fará depois de iniciada.
  7. Implemente os demais callbacks definidos em ANativeActivity->callbacks. Para saber mais sobre quando os callback são chamados, consulte Gerenciamento do ciclo de vida da atividade.
  8. Desenvolva o restante do app.
  9. Crie um arquivo Android.mk file no diretório jni/ do projeto para descrever seu módulo nativo para o sistema de compilação. Para mais informações, consulte Android.mk.
  10. Quando você tiver um arquivo Android.mk, compile seu código nativo usando o comando ndk-build.
  11.     $ cd <path>/<to>/<project>
        $ <ndk>/ndk-build
        
  12. Compile e instale seu projeto para Android normalmente. Se o código nativo estiver no diretório jni/, o script de compilação empacotará automaticamente os arquivos .so compilados a partir dele no APK.

Exemplo adicional de código

Para fazer o download de amostras do NDK, consulte Amostras do NDK.