Para conseguirmos rodar código C/C++ com Android podemos utilizar o NDK que é um conjunto de ferramentas usado para atingir esse objetivo. O NDK fornece bibliotecas para gerenciar atividades nativas e componentes físicos do dispositivo (sensores, dispositivos de toque, GPS e etc). Frequentemente o NDK é utilizado quando queremos mais performance e baixa latência. Ou quando queremos executar rotinas intensas do ponto de vista computacional como jogos ou simulação de física do mundo real. Ou reutilizar bibliotecas específicas do C/C++ ou suportar alguma biblioteca específica de um hardware específico que não tenha biblioteca em outra linguagem.

A ideia deste pequeno exemplo é entender como funciona a integração entre um código Android com Kotlin e um código em C/C++. A ideia do projeto é uma tela simples com uma label e um botão (label do botão é ”mudar”). A label inicialmente com a mensagem “greetings from Kotlin” e ao clicar no botão vai fazer a chamada para o método do C/C++ chamado sayHello que vai pegar a sequência de caracteres “greetings from C/C++”.

Para iniciar essa prática é preciso já ter o Android SDK configurado instalado e funcionando. Agora acesse o menu superior do Android Studio Tools → SDK Manager → SDK Tools selecione NDK e CMake e instale esses componentes. Isso feito podemos fazer nosso projeto.

Inicialmente vamos criar um novo projeto Android, então vamos selecionar File → New Project → Empty Activity. Vai preenchendo os dados e selecionando próximo até criar o projeto. Ele vai criar um esqueleto de um App Hello World padrão. Vamos rodar para ver se está tudo funcionando corretamente.

Agora podemos começar a implementar nosso exemplo. Vamos abrir o arquivo MainActivity.kt.

Vamos modificar o código para:

@Composable
fun Greeting(name: () -> String, modifier: Modifier = Modifier) {
    var toggle by remember {
        mutableStateOf(false)
    }
    val fromKotlin: () -> String = {
        "greetings from Kotlin"
    }
    var text by remember {
        mutableStateOf(fromKotlin())
    }
    Box(modifier = Modifier.fillMaxSize(),
        contentAlignment = Alignment.Center) {
        Column {
            Text(
                text = text,
                modifier = modifier
            )
            Button( onClick = {
                toggle = !toggle
                text = if (toggle) {
                    name()
                } else {
                    fromKotlin()
                }
            }) {
                Text("Muda")
            }

        }
    }
}

Vamos remover esse trecho, pois não vamos utilizá-lo nesse exemplo:

@Preview(showBackground = true)
@Composable
fun GreetingPreview() {
    SampleWithButtonTheme {
        Greeting("Android")
    }
}

Na classe MainActivity vamos carregar dinamicamente a biblioteca que vai ser o código que iremos criar. Mudar a chamada ao composable Greeting e adicionar a assinatura do método que vai ser implementado no C/C++:

class MainActivity : ComponenteActivity() {

    companion object {
	    init {
		    System.loadLibrary("hello")
	    }
    }

    private external fun sayHello(): String

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            SampleWithButtonTheme {
                // A surface container using the 'background' color from the theme
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    Greeting({ sayHello() })
                }
            }
        }
    }
}

Agora vamos criar o nosso código em C/C++. Vamos criar um diretório na pasta dentro da raiz do projeto app/src/cpp. Dentro dessa pasta vamos colar o código:

#include <jni.h>

extern "C" {

    char msg[] = "greetings from C/C++";

    JNICALL JNIEXPORT jstring
    Java_br_com_razecapps_samplewithbutton_MainActivity_sayHello(JNIEnv *env,
                                                                 jobject thiz) {
        return env->NewStringUTF(msg);
    }

}

Em seguida vamos criar o arquivo CMakeLists.txt dentro da pasta app/. Nesse arquivo vamos configurar o caminho do arquivo cpp que criamos, indicar o nome da biblioteca e a versão mínima do CMake que vai ser utilizado.

cmake_minimum_required(VERSION 3.4.1)

PROJECT(hello)

add_library(hello
            SHARED
            src/main/cpp/hello.cpp)

Agora vamos abrir o arquivo build.gradle.kts que está na pasta app/src/ vamos adicionar as linhas que estão dentro do bloco android. Blocos que já existam no seu código por ex: defaultConfig, basta adicionar o comando interno. Não copie e cole esse código pois está incompleto. Observe as versões do CMake e do NDK instaladas no início e preencha conforme a versão instalada na sua máquina.


android {
    defaultConfig {
		externalNativeBuild {
            cmake {
                cppFlags("")
            }
        }
	}


	externalNativeBuild {
        cmake {
            path("CMakeLists.txt")
            version = "3.10.2"
        }
    }

	packaging {
        resources {
            excludes += "/META-INF/{AL2.0,LGPL2.1}"
        }
    }
        
    ndkVersion = "26.0.10792818"
		
}

Por fim, vamos executar. Se tudo correr bem devemos ver o aplicativo em execução e chaveando as chamadas entre o Kotlin e a função em C/C++.