Conceitos

Antes de começar

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

Introdução

Esta seção fornece 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 aplicativos Android. A capacidade de usar código nativo em aplicativos Android pode ser muito útil para desenvolvedores que querem:

  • portar aplicativos 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 intensiva, como jogos.

Como funciona

Esta seção apresenta os principais componentes usados na compilação de um aplicativo 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 aplicativo:

  • Bibliotecas nativas compartilhadas: O NDK compila essas bibliotecas ou arquivos .so a partir do código-fonte em C/C++.
  • Bibliotecas nativas estáticas: O NDK também pode compilar bibliotecas estáticas ou arquivos .a que podem ser vinculados a outras bibliotecas.
  • Interface Java nativa (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 essa ferramenta, consulte a Especificação da interface Java nativa.
  • Interface binária do aplicativo (ABI): A ABI define exatamente como será a interação entre o código do aplicativo e o sistema em tempo de 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 de32 bits, AArch64, x86 e x86-64. Para saber mais, consulte Gerenciamento de ABI.
  • Manifesto: Se você estiver escrevendo um aplicativo sem componentes Java, será necessário declarar a classe NativeActivity no manifesto. O artigo Aplicativos e atividades nativas, na seção “Como usar a interface native_activity.h”, oferece mais detalhes sobre como fazer isso.

Fluxo

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

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

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

  2. Crie um projeto de aplicativo Android normalmente.
  3. Ao programar um aplicativo somente nativo, declare a classe NativeActivity em AndroidManifest.xml. Para mais informações, consulte Aplicativos e atividades nativas.
  4. Crie um arquivo Android.mk que descreva a biblioteca nativa, incluindo nome, sinalizadores, bibliotecas vinculadas e arquivos de origem a serem compilados no diretório “JNI”.
  5. Como opção, você pode criar um arquivo Application.mk que configure as ABIs, a cadeia de ferramentas, o modo de lançamento/depuração e a STL em questão. Para esses elementos que não precisam ser especificados, os valores padrão a seguir serão usados:
    • ABI: todas as ABIs não obseletas
    • cadeia de ferramentas: Clang
    • modo: Lançada
    • STL: system
  6. Coloque seu código nativo na pasta jni do projeto.
  7. Use o ndk-build para compilar bibliotecas nativas (.so e .a).
  8. Compile o componente Java, produzindo o arquivo .dex executável.
  9. Junte tudo em um arquivo APK que contenha .so, .dex e outro arquivos necessários para o aplicativo poder ser executado.

Aplicativos e atividades nativas

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

Os aplicativos Android que usam NativeActivity ainda podem ser executados na própria máquina virtual, que é restrita para outros aplicativos. Assim, é possível acessar as APIs da biblioteca do Android pela 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 das chamadas pela JNI. Para mais informações sobre compatibilidade, consulte as APIs nativas do Android NDK.

É recomendável criar projetos com as ferramentas de compilação tradicionais do Android para o desenvolvimento de atividades nativas e de outros tipos. Isso ajuda a garantir a compilação e o empacotamento de aplicativos 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 o thread principal do aplicativo lida com callbacks, suas implementações de callback não podem fazer bloqueios. Caso contrário, você receberá erros de ANR (o app não está respondendo), porque o thread principal não responderá até o retorno do callback.
  • O arquivo android_native_app_glue.h define uma biblioteca auxiliar estática baseada na interface native_activity.h. Ele gera outro thread que lida com elementos como callbacks ou eventos de entrada em um loop de evento. Mover esses eventos para um thread separado impede que os callbacks bloqueiem seu thread principal.

A origem <ndk_root>/sources/android/native_app_glue/android_native_app_glue.c também está disponível, de modo que é possível modificar a implementação.

Para mais informações sobre como usar essa biblioteca estática, veja o exemplo de aplicativo 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.

Como usar a interface native_activity.h

Para implementar uma atividade nativa com a interface native_activity.h, faça o seguinte:

  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 o aplicativo não tem código Java, defina android:hasCode como false.

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

    É necessário definir o atributo android:name da tag de atividade como NativeActivity.

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

    Observação: É possível criar uma subclasse para NativeActivity. Nesse caso, 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 aplicativo (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 aplicativo faz uma chamada a 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 necessárias. Defina os ponteiros de função de callback 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ê quer 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 Activity.
  8. Desenvolva o restante do aplicativo.
  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 saber mais, consulte Android.mk.
  10. Quando você tiver um arquivo Android.mk, compile o 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 incluirá automaticamente os arquivos .so compilados a partir dele no APK.

Exemplo de código adicional

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