Skip to main content

Quick start guide

This tutorial guides you through creating your first Android application with native C++ code. You’ll build a simple “Hello from C++” app that demonstrates how Java/Kotlin code interacts with native code through JNI (Java Native Interface).

Prerequisites

Before starting, ensure you have:
  • Android Studio installed with NDK and CMake
  • Basic knowledge of Android app development
  • Basic C++ knowledge (helpful but not required)
If you haven’t installed the NDK yet, see the installation guide.

Create your first native app

1

Create a new project with native support

Open Android Studio and create a new project:
  1. Click File > New > New Project
  2. Select Native C++ template
  3. Click Next
  4. Configure your project:
    • Name: HelloNDK
    • Package name: com.example.hellondk
    • Language: Kotlin (or Java)
    • Minimum SDK: API 21 or higher
  5. Click Next
  6. Choose C++ Standard: C++14 or higher
  7. Click Finish
Android Studio creates a project with native code support pre-configured.
2

Explore the project structure

Your project now contains both Java/Kotlin and C++ code:
app/
├── src/
│   ├── main/
│   │   ├── java/          # Java/Kotlin code
│   │   ├── cpp/           # C++ native code
│   │   │   ├── native-lib.cpp
│   │   │   └── CMakeLists.txt
│   │   └── AndroidManifest.xml
└── build.gradle
Key files:
  • native-lib.cpp: Your C++ source code
  • CMakeLists.txt: CMake build configuration
  • MainActivity.kt/java: Loads and calls native code
3

Examine the native code

Open app/src/main/cpp/native-lib.cpp. You’ll see a JNI function:
#include <jni.h>
#include <string>

extern "C" JNIEXPORT jstring JNICALL
Java_com_example_hellondk_MainActivity_stringFromJNI(
        JNIEnv* env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}
This function:
  • Is called from Java/Kotlin code
  • Returns a string created in C++
  • Uses JNI to convert C++ string to Java string
The function name follows JNI naming convention: Java_<package>_<class>_<method>
4

Examine the CMake configuration

Open app/src/main/cpp/CMakeLists.txt:
cmake_minimum_required(VERSION 3.18.1)

project("hellondk")

add_library(
    native-lib
    SHARED
    native-lib.cpp
)

find_library(
    log-lib
    log
)

target_link_libraries(
    native-lib
    ${log-lib}
)
This configuration:
  • Creates a shared library named native-lib
  • Compiles native-lib.cpp
  • Links the Android log library
5

Examine the Kotlin/Java code

Open MainActivity.kt (or MainActivity.java):
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        // Load the native library
        System.loadLibrary("native-lib")
        
        // Call the native method
        val textView: TextView = findViewById(R.id.sample_text)
        textView.text = stringFromJNI()
    }
    
    // Declare the native method
    external fun stringFromJNI(): String
}
The code:
  • Loads the native library with System.loadLibrary()
  • Declares the native method with external (Kotlin) or native (Java)
  • Calls the C++ function like any other method
6

Build and run the app

  1. Connect an Android device or start an emulator
  2. Click Run (green play button) or press Shift+F10
  3. Wait for the build to complete
  4. The app displays “Hello from C++” on the screen
The first build may take longer as CMake compiles the native code for multiple architectures (armeabi-v7a, arm64-v8a, x86, x86_64).

Customize your native code

Now let’s modify the native code to perform a simple calculation.
1

Add a new native function

In native-lib.cpp, add a new function that adds two numbers:
extern "C" JNIEXPORT jint JNICALL
Java_com_example_hellondk_MainActivity_addNumbers(
        JNIEnv* env,
        jobject /* this */,
        jint a,
        jint b) {
    return a + b;
}
This function:
  • Takes two integers as parameters
  • Returns their sum
  • Uses jint (JNI integer type)
2

Declare the method in Kotlin/Java

In MainActivity.kt, add the method declaration:
external fun addNumbers(a: Int, b: Int): Int
3

Call the native function

Update onCreate to call the new function:
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    
    val textView: TextView = findViewById(R.id.sample_text)
    val greeting = stringFromJNI()
    val sum = addNumbers(15, 27)
    textView.text = "$greeting\nSum: $sum"
}
4

Build and run again

Run the app again. You should now see:
Hello from C++
Sum: 42

Add logging from native code

Logging is essential for debugging native code. Let’s add Android logging support.
1

Include the log header

In native-lib.cpp, add the Android log header:
#include <jni.h>
#include <string>
#include <android/log.h>

#define TAG "NativeLib"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)
2

Add logging to your function

Update the addNumbers function:
extern "C" JNIEXPORT jint JNICALL
Java_com_example_hellondk_MainActivity_addNumbers(
        JNIEnv* env,
        jobject /* this */,
        jint a,
        jint b) {
    LOGI("Adding %d + %d", a, b);
    jint result = a + b;
    LOGI("Result: %d", result);
    return result;
}
3

View logs

Run the app and check Logcat in Android Studio:
  1. Open Logcat tab at the bottom
  2. Filter by “NativeLib”
  3. You’ll see the log messages from C++:
I/NativeLib: Adding 15 + 27
I/NativeLib: Result: 42

Understanding JNI types

When working with JNI, you need to understand type mappings between Java and C++:
Java TypeJNI TypeC++ Type
booleanjbooleanuint8_t
bytejbyteint8_t
charjcharuint16_t
shortjshortint16_t
intjintint32_t
longjlongint64_t
floatjfloatfloat
doublejdoubledouble
Objectjobject-
Stringjstring-
Always use JNI types in your native function signatures. Using regular C++ types will cause runtime errors.

Build system configuration

The build.gradle file contains NDK configuration:
android {
    defaultConfig {
        // Specify which ABIs to build
        ndk {
            abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
        }
    }
    
    // Link CMake build script
    externalNativeBuild {
        cmake {
            path "src/main/cpp/CMakeLists.txt"
            version "3.18.1"
        }
    }
}
Building for fewer ABIs reduces APK size but limits device compatibility. Most production apps target armeabi-v7a and arm64-v8a.

Debugging native code

Android Studio provides full debugging support for native code:
1

Set breakpoints

Open native-lib.cpp and click in the left gutter to set a breakpoint.
2

Debug the app

Click Debug (bug icon) instead of Run. The debugger will pause at your breakpoint.
3

Inspect variables

Use the Variables pane to inspect values, step through code, and evaluate expressions.

Common issues and solutions

UnsatisfiedLinkError

If you see UnsatisfiedLinkError: No implementation found:
  • Verify the native function name matches the JNI naming convention
  • Ensure System.loadLibrary() is called before the native method
  • Check that CMakeLists.txt includes your .cpp file

Build errors

If the native build fails:
  • Check Build tab for CMake error messages
  • Ensure NDK and CMake are installed via SDK Manager
  • Verify CMake version in CMakeLists.txt matches installed version

Missing symbols

If you get linker errors about missing symbols:
  • Add required libraries to target_link_libraries in CMakeLists.txt
  • For Android APIs, use find_library() to locate system libraries

Next steps

Congratulations! You’ve built your first native Android application. Here’s what to explore next:

Build systems

Learn advanced CMake and ndk-build configurations

JNI guide

Deep dive into Java Native Interface programming

Performance

Optimize your native code for maximum performance

Debugging

Master native debugging and crash analysis

Additional resources

Build docs developers (and LLMs) love