Build Android apps with the Zig build system. No Gradle, no Android Studio required.
zig-android is a Zig build-system package that handles Android SDK/NDK discovery,
cross-compilation, manifest generation, and APK packaging — letting you build, sign,
and deploy Android apps entirely from zig build.
- Automatic SDK/NDK discovery — finds your Android SDK and NDK via environment
variables, default install paths, or
PATH - Cross-compilation — builds native shared libraries for
x86_64andaarch64Android targets with proper NDK sysroot configuration - Type-safe manifest generation — write your
AndroidManifest.xmlas Zig structs with compile-time validation, predefined constants for permissions, actions, and categories - Full APK pipeline —
aapt package→zipalign→apksignerin a singlezig buildinvocation - Multi-ABI support — build native libraries for multiple architectures and bundle them into a single APK
-
Zig 0.15.1 or later
-
Android SDK and NDK — install via Android Studio:
- Install Android Studio
- Open Settings → Languages & Frameworks → Android SDK
- Under SDK Platforms, install the API level you're targeting (e.g., Android 9.0 / API 28)
- Under SDK Tools, make sure these are installed:
- Android SDK Build-Tools (includes
aapt,zipalign,apksigner) - Android SDK Platform-Tools (includes
adb) - NDK (Side by side)
- Android SDK Build-Tools (includes
- Note your SDK path (shown at the top of the settings page), or set the
ANDROID_HOMEenvironment variable to it
You can also install the SDK without Android Studio using the command-line tools and
sdkmanager.
The SDK is discovered automatically. Set ANDROID_HOME or ANDROID_SDK_ROOT if
auto-detection doesn't work. Similarly, set ANDROID_NDK_HOME to override NDK
discovery.
Add zig-android as a dependency in your build.zig.zon:
zig fetch --save git+https://github.com/shreyassanthu77/zig-androidThen in your build.zig:
const std = @import("std");
const android = @import("zig_android");
pub fn build(b: *std.Build) void {
const sdk = android.Sdk.init(b, 28); // API level 28
const app = sdk.createApp(.{
.manifest = .{
.package = "com.example.myapp",
.uses_sdk = .{
.android_minSdkVersion = 24,
.android_targetSdkVersion = 29,
},
.application = .{
.android_label = "My App",
.android_hasCode = false,
.activity = &.{.{
.android_name = "android.app.NativeActivity",
.android_configChanges = &.{
.orientation,
.keyboardHidden,
.screenSize,
},
.meta_data = &.{.{
.android_name = "android.app.lib_name",
.android_value = "main",
}},
.intent_filter = &.{android.Manifest.IntentFilter.main_launcher},
}},
},
},
});
// Build the native library for both x86_64 and arm64
const targets: []const std.Target.Query = &.{
.{ .cpu_arch = .x86_64, .os_tag = .linux, .abi = .android },
.{ .cpu_arch = .aarch64, .os_tag = .linux, .abi = .android },
};
for (targets) |query| {
_ = app.addLibrary(.{
.name = "main",
.root_module = .{
.root_source_file = b.path("src/main.zig"),
.target = b.resolveTargetQuery(query),
.optimize = b.standardOptimizeOption(.{}),
},
});
}
app.installApk(".");
}And in src/main.zig, implement the NativeActivity entry point:
const c = @cImport({
@cInclude("android/native_activity.h");
@cInclude("android/log.h");
});
fn logInfo(msg: [*:0]const u8) void {
_ = c.__android_log_print(c.ANDROID_LOG_INFO, "myapp", msg);
}
export fn ANativeActivity_onCreate(
activity: [*c]c.ANativeActivity,
_: ?*anyopaque,
_: usize,
) callconv(.c) void {
logInfo("App started!");
activity.*.callbacks.*.onStart = struct {
fn f(_: [*c]c.ANativeActivity) callconv(.c) void {
logInfo("onStart");
}
}.f;
activity.*.callbacks.*.onResume = struct {
fn f(_: [*c]c.ANativeActivity) callconv(.c) void {
logInfo("onResume");
}
}.f;
activity.*.callbacks.*.onNativeWindowCreated = struct {
fn f(_: [*c]c.ANativeActivity, _: ?*c.ANativeWindow) callconv(.c) void {
logInfo("onNativeWindowCreated");
}
}.f;
}zig buildThis produces a signed, aligned APK at zig-out/<package>.apk.
adb install zig-out/com.example.myapp.apkadb logcat -s myappThe main entry point. Handles SDK/NDK discovery and provides build helpers.
const sdk = android.Sdk.init(b, api_level);sdk.createApp(opts)— creates anApplicationbuilder with the given manifestsdk.addLibrary(opts)— cross-compiles a library with NDK include/lib paths configuredsdk.addRunBuildTool(name)— returns a*std.Build.Step.Runfor an SDK build tool (e.g.,"aapt","zipalign")sdk.addRunPlatformTool(name)— returns a*std.Build.Step.Runfor a platform tool (e.g.,"adb")sdk.getDebugKeystore()— returns aLazyPathto a generated debug keystore for APK signing
The APK builder. Created via sdk.createApp().
app.addLibrary(opts)— adds a native shared library to the APK. Automatically links-lc,-llog, and-landroid. Returns*std.Build.Step.Compileso you can link additional system libraries (e.g.,EGL,GLESv2)app.installApk(dest)— installs the signed APK tozig-out/<dest>/
A type-safe representation of AndroidManifest.xml. All fields map directly to Android manifest attributes and elements, with Zig naming conventions (android_label becomes android:label, intent_filter becomes <intent-filter>).
Includes predefined constants for common values:
// Permissions
.uses_permission = &.{
android.Manifest.UsesPermission.internet,
android.Manifest.UsesPermission.camera,
},
// Intent filters
.intent_filter = &.{android.Manifest.IntentFilter.main_launcher},
// Actions and categories
const Action = android.Manifest.Action;
const Category = android.Manifest.Category;The SDK is located in this order:
ANDROID_HOMEenvironment variableANDROID_SDK_ROOTenvironment variable- Default paths:
- Linux:
~/Android/Sdk - macOS:
~/Library/Android/sdk - Windows:
%LOCALAPPDATA%\Android\Sdk
- Linux:
- Searching
PATHforadboraapt
NDK discovery:
ANDROID_NDK_HOMEenvironment variableANDROID_NDK_PATHenvironment variableANDROID_NDK_ROOTenvironment variable<sdk>/ndk/<latest-version>/
MIT