Skip to content
APKPY / PYTHON → ANDROID

Write the app in Python.
Keep the Android output.

ApkPy is a source-to-source compiler for native Android apps. You describe screens, state and callbacks in Python; ApkPy generates Activities, XML layouts, drawables, resources and manifest entries that Android understands directly.

1 Python source tree 0 Python runtimes in the APK 185 transpiler checks 83 focused unit tests
writehere.py preview ready
01 from apkpy_lib import Screen, Theme
02 from apkpy_lib import card, button, run
03
04 home = Screen(id="home", scroll=True)
05 card(title="Available balance",
06      content="€ 8,420.16", screen=home)
07 button("Transfer", variant="filled",
08        command=open_transfer, screen=home)
09
10 run(start_screen=home, theme=Theme(mode="dark"))
ApkPy 1.7.0 · available now Motion sensors, confirmed wallpapers and actionable notifications with a redesigned Previewer drawer — examples and testing limits. Available in ApkPy 1.7.0. Read the update
FRIENDLY DIAGNOSTICS

The error should tell you what to change.

Previewer callbacks, imports, Data Core declarations, compilation and Android toolchain checks now share one readable report: a stable code, the useful application line, the original cause and a concrete correction.

source locationdid you mean?safe debug mode
Learn to read an ApkPy error
apkpy preview
APKPY E1102 - This import is not available

Where
  writehere.py:1

Received
  bottom_nva

How to fix
  1. Did you mean:
     from apkpy_lib import bottom_nav
MEASURED ON ANDROID 1.48 MiB signed release, 5.37 MiB debug, in the Benchmark Notes test.

The same 100-note app was built with ApkPy, Flet and BeeWare/Toga, then installed and measured on one emulator. The comparison uses debug builds on every side, so the build class is the same; what you publish is the release, which R8 shrinks to 1.48 MiB. The programs, line counts, raw starts, memory samples and artifact hashes are available for review.

Inspect the benchmark
INSPECTABLE OUTPUT Python in. Native Android out.

Open the generated Java, XML and Gradle project in Android Studio. ApkPy does not hide a web page or Python interpreter inside the APK.

See the validation evidence

Start from the product you are building

If this is your first project, use the complete Knowledge Vault tutorial before choosing a feature-specific guide. It ends with a generated Android project you can open and inspect.

One source file, two useful feedback loops

The Previewer is for short iteration cycles. Android generation is for checking the real platform output. They share the same declarations and callbacks, but each target does the job it is good at.

Ppython writehere.py

Hot Previewer

Open the interface in seconds. Exercise navigation, inputs, callbacks, storage and responsive breakpoints before starting Gradle.

  • Fast UI iteration
  • Desktop state and callback testing
  • Phone, tablet and resizable presets
Aapkpy build

Native Android project

Inspect exactly what will run on the device: Java Activities, XML layouts, Material resources, services and permissions.

  • Openable in Android Studio
  • No WebView or embedded Python interpreter
  • Device APIs stay native

What one button becomes

This is a representative view of the compiler pipeline. Names and generated attributes can vary with the screen and theme, but the mapping is direct: a Python declaration becomes an Android view and its callback becomes a Java listener.

01Python sourcewritehere.py
button(
    "Transfer",
    id="transfer",
    variant="filled",
    command=open_transfer,
    screen=home,
)
02Layout resourcescreen_home.xml
<MaterialButton
    android:id="@+id/transfer"
    android:text="Transfer"
    android:minHeight="48dp"
    android:background="@drawable/…" />
03Activity callbackScreen_homeActivity.java
btn_transfer.setOnClickListener(
    view -> {
        pythonCallback_open_transfer();
    }
);

Generated code is an output. Keep behavior in writehere.py; rebuilding may replace the Android files. Read the source-of-truth rules →

A real Android project, not a screenshot exporter

The compiler creates only the helpers a project needs. A simple screen stays small; media, OAuth, advanced layout or image caching add their runtime pieces when those APIs appear in the source.

generated-project/typical output
app/src/main/
├── AndroidManifest.xml       # activities, services, permissions
├── java/com/example/app/
│   ├── Screen_homeActivity.java
│   ├── Screen_libraryActivity.java
│   └── ApkpyMediaService.java # only when media is used
└── res/
    ├── layout/               # phone layouts
    ├── layout-sw600dp/       # tablet layouts when responsive
    ├── drawable/             # shapes, vectors and local images
    ├── menu/                 # navigation and toolbar actions
    └── values/               # themes, colors and strings

Build more than static screens

InterfaceTheme · cards · lists · grids · flex · responsive · app bars

Compose a reusable hierarchy and override it through component or ID selectors.

StateInputs · callbacks · loops · conditions · content states

Read and update values using normal supported Python control flow.

DataSQLite · REST · encrypted storage · files

Keep local data, call APIs and download private app files without a separate plugin layer.

FeedsPagination · refresh · keyed merge · optimistic rollback

Keep long timelines responsive while the application remains in control of cursors and conflict rules.

MediaQueues · background audio · playlists · mini-player

Generate an Android foreground media service, notification controls and persistent libraries.

IdentityOAuth 2.0 + PKCE · Google · Spotify · GitHub

Use browser authorization and generated deep-link handling without embedding a client secret.

DeviceCamera · gallery · location · notifications · app inspection

Declare the API and let ApkPy add the matching permission and native Android integration.

DocumentsRich spans · Markdown · expandable trees

Build notes, articles, comments and knowledge bases with native selectable text and recycled hierarchy rows.

No browser hidden in the app. rich_text() and markdown() compile to Android Spannable text, while tree_view() uses a native visible-row RecyclerView. Open the native rich-content guide →

Native audio is already a system player

ApkPy's music API is not limited to playing a sound inside the current screen. The generated Android project can keep a queue in a foreground media service, publish a native MediaSession, show metadata and previous/play/next controls in the notification and lock screen, handle audio focus, synchronize a full player and mini-player, and keep favourites and editable playlists.

audio.play_background(
    "https://cdn.example.com/night-drive.mp3",
    title="Night Drive",
    artist="Nova",
    art="https://cdn.example.com/night-drive.jpg",
)

audio.now_playing(progress=seek, time=elapsed, cover=cover,
                  title=title, artist=artist)
audio.controls(play_pause=play_pause, shuffle=shuffle, repeat=repeat)
mini_player(open=player)

See the complete capability matrix, queues, playlists and offline files. The documentation also states the current limits clearly: automatic audio caching, adaptive quality, guaranteed gapless playback, crossfade and DRM are not claimed as supported features.

Data code you can ship

SQLite, REST and cryptography are part of the normal ApkPy workflow. The Previewer uses local Python backends for fast testing; Android generation maps the same calls to SQLiteDatabase, background HttpURLConnection, SharedPreferences and Android Keystore.

DB
SQLiteparameter binding + transactions
db.execute(
    "CREATE TABLE IF NOT EXISTS tracks "
    "(id INTEGER PRIMARY KEY, title TEXT)"
)

db.execute(
    "INSERT INTO tracks(title) VALUES (?)",
    [title_input.get_value()],
)

rows = db.query(
    "SELECT id, title FROM tracks "
    "ORDER BY id DESC"
)
track_list.set_items(rows, title="title", subtitle="id")

The ? placeholder is bound by SQLite instead of concatenated into the query. This handles apostrophes correctly and prevents SQL injection.

Transactions and query results →
HTTP
REST APIsGET, POST, PUT, PATCH and DELETE
def loaded(success, response):
    if success:
        tracks.set_items(
            response,
            title="name",
            subtitle="artist",
            image="cover",
        )
    else:
        snackbar("Could not refresh library")

https.get(
    "https://api.example.com/tracks",
    headers={"Authorization": "Bearer " + auth.token()},
    on_response=loaded,
)

Requests run away from the UI thread. The callback receives the body on success and also receives structured error bodies for HTTP 4xx/5xx responses.

See the complete REST surface →
KEY
EncryptionAES-256-GCM + PBKDF2
# storage is encrypted automatically
storage.set("session", auth.token())
token = storage.get("session", "")

# hash values that must only be verified
password_hash = crypto.hash_password(password)
valid = crypto.verify_password(candidate, password_hash)

# encrypt database fields that must be read later
ciphertext = crypto.encrypt(private_note)
db.execute(
    "INSERT INTO notes(content) VALUES (?)",
    [ciphertext],
)
plain_text = crypto.decrypt(ciphertext)

Android keeps the AES key in Android Keystore. Passwords use salted PBKDF2 with 200,000 iterations and should never be stored with reversible encryption.

Read the threat model →
Client-side security has a boundary.

Do not put permanent service secrets inside an APK. Use HTTPS, short-lived tokens or OAuth with PKCE, and keep privileged authorization on a server you control.

Four small products used as regression tests

The showcase is executable code, not a set of design mockups. Every app has four screens, working navigation and generated Android output. Together they deliberately exercise different palettes and layout choices.

The workflow in three commands

01python writehere.pyIterate

Open the Hot Previewer and test interface behavior.

02apkpy buildInspect

Generate a ZIP project for Android Studio.

03apkpy runInstall

Compile a debug APK; add --qr or --usb.

Questions developers usually ask

Does the APK contain Python?

No. ApkPy compiles the supported source model into native Android Java, XML and resources. The generated application does not bundle a Python interpreter.

Can I open the result in Android Studio?

Yes. apkpy build produces a Gradle project ZIP intended for inspection, emulator testing and normal Android tooling.

Is every Python library supported?

No. ApkPy supports a deliberate Python subset plus its own Android-facing APIs. Arbitrary CPython packages cannot automatically become Java. The API reference is the contract.

Should I trust only the desktop preview?

No. Use the Previewer for fast feedback, then test device-only behavior, permissions, services and final rendering on an emulator or physical Android device. See the renderer comparison.

START WITH A SMALL SCREEN

Install ApkPy, open the Previewer, then inspect what it generated.

Closed source, public contract

ApkPy's compiler is proprietary while active development continues. Open-sourcing may be considered later; if the project is permanently abandoned, the core source will be released as open source so it can be maintained and continued. Until an explicit source release and new licence are published, the current proprietary licence remains in force. Read the project continuity policy.