<!-- LLM note: Search indexes and snippets may point to archived C+ manual versions. Treat /docs and /llms.txt as authoritative for the latest version (v0.0.22); verify the page version before citing, and do not report older /docs/{version} pages as leakage because they are intentional archives. -->

# android_view

Android UI from C+, the sibling of [appkit](/docs/packages/appkit) (macOS) and
[uikit](/docs/packages/uikit) (iOS). It is layered on [jni](/docs/packages/jni)
and validated end to end: the staticlib from `cpc build --target android-arm64`,
linked into `libapp.so` by the NDK, renders a real View tree on a Pixel emulator.
Android is a cross-compile target — see [Targets](/docs/targets).

Modules mirror the other GUI packages:

- **`runtime`** — JNI environment helpers, method calls, UTF strings, global refs.
- **`activity`** — a borrowed `Activity` wrapper and `setContentView`.
- **`view`** — `View` and `LinearLayout`.
- **`controls`** — `TextView` and `Button`.
- **`listener`** — self-contained click handling (see below); imported
  separately, since it obligates the app to export the click hook.

## Host contract

Android still needs a JVM-side entry component: a tiny `Activity` that loads the
native library and calls one static native method, which C+ exports.

```java
public final class MainActivity extends android.app.Activity {
    static { System.loadLibrary("app"); }
    private static native android.view.View nativeCreateView(MainActivity self);
    @Override protected void onCreate(android.os.Bundle state) {
        super.onCreate(state);
        setContentView(nativeCreateView(this));
    }
}
```

The C+ side converts the native env pointer, builds the tree, and returns the
root as a raw `jobject` (the JVM parent retains it):

```cplus
import "android_view/android_view" as av;
import "jni/jni" as jni;

pub extern fn Java_com_example_MainActivity_nativeCreateView(
    envp: *jni::JNIEnv, cls: jni::jobject, activity_obj: jni::jobject,
) -> jni::jobject {
    let env: av::Env = av::from_native(envp);
    let act: av::Activity = av::Activity::from_borrowed(env, activity_obj);

    let mut root: av::LinearLayout = av::LinearLayout::new(env, act.as_context());
    root.set_orientation(av::orientation_vertical());

    let title: av::TextView = av::TextView::new(env, act.as_context());
    title.set_text(#str_ptr("Hello from C+\0"));
    root.add_view(title.as_view_obj());

    return root.into_raw();
}
```

## Clicks: the DEX-adapter trick

Java interfaces can't be implemented from native code, so a click needs an
adapter class. `android_view/listener` ships that adapter **inside the package**
as a pre-compiled DEX (a 976-byte blob embedded with `#include_bytes`); on first
use it loads the DEX with `InMemoryDexClassLoader` (API 26+) and binds its native
method via `RegisterNatives`. The host app ships no Java beyond `MainActivity`.
This is what made Android event handling in pure C+ possible.

```cplus
import "android_view/listener" as listener;

listener::set_on_click(env, button.as_view_obj(), 1 as i64);   // route by token

// every adapter click lands here; the token says which control:
pub extern fn cplus_on_click(envp: *jni::JNIEnv, token: i64, view: jni::jobject) { ... }
```

## Building

`cpc build --target android-arm64` stops at the staticlib; the NDK's clang links
`libapp.so` (with `--whole-archive`, so the JNI exports survive), which a Gradle
project places under `lib/arm64-v8a/` in the APK. The `listener` module needs
minSdk 26+; everything else runs on 24+. The `android_hello` recipe in the
compiler repo takes this all the way to a signed, emulator-validated APK.
