Skip to main content

Overview

Android provides several native APIs that are specific to the Android platform. These APIs allow you to interact with Android system services, manage app resources, and integrate with the Android runtime.
All Android-specific APIs require linking against their respective libraries. Add them to your build configuration (CMake or ndk-build).

Native Activity

Native Activity allows you to create Android apps entirely in native code without writing Java/Kotlin code.

Key features

android_native_app_glue
Library
Static helper library that simplifies NativeActivity lifecycle management. Recommended for all NativeActivity applications.

API reference

android_app
struct
Main application structure containing activity state, window, input queue, and callbacks.
activity
ANativeActivity*
Pointer to the ANativeActivity instance representing the Android activity.
window
ANativeWindow*
Native window for rendering. NULL when no window is available.
inputQueue
AInputQueue*
Input event queue for processing touch, key, and motion events.

Code example: Basic NativeActivity

#include <android_native_app_glue.h>
#include <android/log.h>

#define LOG_TAG "NativeActivity"

static void handle_cmd(struct android_app* app, int32_t cmd) {
    switch (cmd) {
        case APP_CMD_INIT_WINDOW:
            // Window is being shown, initialize rendering
            if (app->window != NULL) {
                __android_log_print(ANDROID_LOG_INFO, LOG_TAG, 
                    "Window initialized");
                // Initialize graphics here
            }
            break;
            
        case APP_CMD_TERM_WINDOW:
            // Window is being hidden or closed
            __android_log_print(ANDROID_LOG_INFO, LOG_TAG, 
                "Window terminated");
            // Cleanup graphics here
            break;
            
        case APP_CMD_GAINED_FOCUS:
            // App gained focus, resume rendering
            break;
            
        case APP_CMD_LOST_FOCUS:
            // App lost focus, pause rendering
            break;
    }
}

static int32_t handle_input(struct android_app* app, AInputEvent* event) {
    if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION) {
        float x = AMotionEvent_getX(event, 0);
        float y = AMotionEvent_getY(event, 0);
        __android_log_print(ANDROID_LOG_INFO, LOG_TAG, 
            "Touch at %.2f, %.2f", x, y);
        return 1; // Event handled
    }
    return 0; // Event not handled
}

void android_main(struct android_app* app) {
    app->onAppCmd = handle_cmd;
    app->onInputEvent = handle_input;
    
    __android_log_print(ANDROID_LOG_INFO, LOG_TAG, "App started");
    
    // Main loop
    while (1) {
        int events;
        struct android_poll_source* source;
        
        // Poll for events
        while (ALooper_pollAll(0, NULL, &events, (void**)&source) >= 0) {
            if (source != NULL) {
                source->process(app, source);
            }
            
            if (app->destroyRequested != 0) {
                __android_log_print(ANDROID_LOG_INFO, LOG_TAG, "App destroyed");
                return;
            }
        }
        
        // Render frame here
    }
}

CMake configuration

# Link against native_app_glue and android libraries
target_link_libraries(${CMAKE_PROJECT_NAME}
    android
    native_app_glue
    log
)

Asset Manager

The Asset Manager API allows you to access files packaged in your app’s APK.

API reference

AAssetManager
opaque struct
Manager object for accessing assets. Obtained from ANativeActivity.
AAsset
opaque struct
Represents an open asset file. Use for reading asset contents.

Code example: Reading assets

#include <android/asset_manager.h>
#include <android/asset_manager_jni.h>
#include <android/log.h>

#define LOG_TAG "AssetManager"

void load_asset(AAssetManager* mgr, const char* filename) {
    // Open the asset
    AAsset* asset = AAssetManager_open(mgr, filename, AASSET_MODE_BUFFER);
    
    if (asset == NULL) {
        __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, 
            "Failed to open asset: %s", filename);
        return;
    }
    
    // Get asset size
    off_t size = AAsset_getLength(asset);
    __android_log_print(ANDROID_LOG_INFO, LOG_TAG, 
        "Asset %s size: %ld bytes", filename, size);
    
    // Read asset into buffer
    char* buffer = (char*)malloc(size + 1);
    int bytes_read = AAsset_read(asset, buffer, size);
    buffer[size] = '\0'; // Null terminate if text
    
    if (bytes_read > 0) {
        __android_log_print(ANDROID_LOG_INFO, LOG_TAG, 
            "Read %d bytes from %s", bytes_read, filename);
        // Use the buffer...
    }
    
    // Cleanup
    free(buffer);
    AAsset_close(asset);
}

// In NativeActivity context
void load_assets_from_activity(struct android_app* app) {
    AAssetManager* mgr = app->activity->assetManager;
    load_asset(mgr, "config.json");
    load_asset(mgr, "textures/sprite.png");
}

Asset access modes

ModeDescriptionUse Case
AASSET_MODE_UNKNOWNNo specific access modeDefault
AASSET_MODE_RANDOMRandom accessSeeking within file
AASSET_MODE_STREAMINGSequential accessReading file once
AASSET_MODE_BUFFEREntire file in memoryFast access, small files

Logging

The Android logging API provides native logging that integrates with logcat.

API reference

__android_log_print
function
Formatted logging function similar to printf. Most commonly used logging function.
__android_log_write
function
Simple logging function for unformatted messages.

Log levels

ANDROID_LOG_VERBOSE
2
Verbose logging. Typically compiled out in release builds.
ANDROID_LOG_DEBUG
3
Debug logging. Use for development and debugging.
ANDROID_LOG_INFO
4
Informational messages. Use for normal operation messages.
ANDROID_LOG_WARN
5
Warning messages. Use for potential issues.
ANDROID_LOG_ERROR
6
Error messages. Use for error conditions.
ANDROID_LOG_FATAL
7
Fatal error messages. Use for critical failures.

Code example: Logging utilities

#include <android/log.h>

#define LOG_TAG "MyApp"

// Convenience macros
#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, LOG_TAG, __VA_ARGS__)
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)

void example_logging() {
    int user_id = 1234;
    const char* username = "john_doe";
    
    LOGI("User logged in: %s (ID: %d)", username, user_id);
    LOGW("Memory usage: %.2f%%", 85.5);
    LOGE("Failed to connect to server: %s", "Connection timeout");
}

// Assert-style logging
#define ASSERT_LOG(condition, ...) \
    if (!(condition)) { \
        LOGE(__VA_ARGS__); \
        abort(); \
    }

void example_assert() {
    void* ptr = malloc(1024);
    ASSERT_LOG(ptr != NULL, "Memory allocation failed");
    // Use ptr...
    free(ptr);
}

CMake configuration

# Link against log library
target_link_libraries(${CMAKE_PROJECT_NAME}
    log
)

Storage Access Framework

Access files through Android’s Storage Access Framework (SAF) using file descriptors.
Direct file system access is restricted on Android 10 (API 29) and higher. Use SAF or app-specific directories.

Code example: Working with file descriptors

#include <android/asset_manager.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

// File descriptor obtained from Java layer via SAF
void read_from_fd(int fd) {
    struct stat stat_buf;
    if (fstat(fd, &stat_buf) == 0) {
        off_t size = stat_buf.st_size;
        
        char* buffer = (char*)malloc(size);
        ssize_t bytes_read = read(fd, buffer, size);
        
        if (bytes_read > 0) {
            // Process the data
        }
        
        free(buffer);
    }
    
    close(fd);
}

Configuration and System Info

Access device configuration and system information.

Code example: Device configuration

#include <android/configuration.h>
#include <android/native_activity.h>

void get_device_info(ANativeActivity* activity) {
    AConfiguration* config = AConfiguration_new();
    AConfiguration_fromAssetManager(config, activity->assetManager);
    
    // Get screen density
    int32_t density = AConfiguration_getDensity(config);
    LOGI("Screen density: %d dpi", density);
    
    // Get screen size
    int32_t screen_size = AConfiguration_getScreenSize(config);
    const char* size_name[] = {
        "small", "normal", "large", "xlarge"
    };
    LOGI("Screen size: %s", size_name[screen_size - 1]);
    
    // Get orientation
    int32_t orientation = AConfiguration_getOrientation(config);
    LOGI("Orientation: %s", 
        orientation == ACONFIGURATION_ORIENTATION_PORT ? "portrait" : "landscape");
    
    // Get language
    char lang[3] = {0};
    AConfiguration_getLanguage(config, lang);
    LOGI("Language: %s", lang);
    
    AConfiguration_delete(config);
}

Additional Android-specific APIs

Access and share hardware graphics buffers for efficient rendering.Available from API 26+. Used for Vulkan, OpenGL ES interop, and camera integration.
Synchronize rendering with display refresh rate.Available from API 24+. Essential for smooth animations and game loops.
Add native trace events visible in Android Studio Profiler.Available from API 23+. Critical for performance profiling.
Manipulate Android Bitmap objects from native code.Available from API 8+. Useful for image processing.

Library reference

Android-specific libraries to link in your build configuration:
LibraryPurposeHeader
androidNative Activity, Configuration, etc.<android/*.h>
logLogging to logcat<android/log.h>
native_app_glueNativeActivity helper (static)<android_native_app_glue.h>
jnigraphicsBitmap access<android/bitmap.h>

Next steps

Bionic status

Check POSIX compliance and available C library functions

Platform APIs

Learn about platform API availability and compatibility

Graphics guide

OpenGL ES and Vulkan rendering

Audio guide

High-performance audio with AAudio

Build docs developers (and LLMs) love