====== Android-Entwicklung mit Lyx ======
Lyx kompiliert direkt zu Android-nativen ''.so''-Bibliotheken ohne NDK-Toolchain. Die 17 Android-Units decken den vollständigen Workflow ab: NativeActivity, JNI, Logging, Asset-Manager, OpenGL ES 2, APK-Bau und mehr.
→ [[lyx_-_programmiersprache:guides|Guides]] · [[lyx_-_programmiersprache:guides:crosscompiling|Cross-Compilation]] · [[lyx_-_programmiersprache:units:android|std.android Referenz]]
----
===== Architektur =====
android/jni — JNI 1.6 Typ-Aliase (JNIEnv, jobject, jclass, jint, ...)
android/native_activity — ANativeActivity-Struct + Callback-Tabelle
android/app_glue — Worker-Thread-Glue (android_app, APP_CMD_*)
android/log — Logcat-Ausgabe via stderr
android/asset — Asset Manager (APK-Assets lesen)
android/native_window — ANativeWindow (Surface-Handle)
android/input — AInputQueue, AInputEvent (Touch, Keys)
android/sensor — ASensorManager, Beschleunigungssensor, Gyroskop
android/gles2 — OpenGL ES 2.0 (Context-Setup + Render-Loop)
android/looper — ALooper (Event-Loop-Integration)
android/manifest_gen — AndroidManifest.xml-Generator
android/apk_builder — APK-Paket erstellen (ZIP + Manifest + Classes.dex-Stub)
android/zip_writer — ZIP-Schreiber (Grundlage für APK)
android/random — arc4random / Bionic-Random
android/restrictions — Katalog der unter SELinux/Bionic gesperrten Syscalls
android/ioctl — ioctl-Nummern für ASHMEM, ALSA, V4L2
----
===== Schritt 1: Kompilieren für Android =====
# ARM64 Shared Library (Standard — alle Android-Geräte ab API 21):
lyxc mylib.lyx --target=android --output-type=shared-lib -o libmylib.so
# Mit explizitem API-Level (Default: 26 = Android 8.0):
lyxc mylib.lyx --target=android --android-api=29 --output-type=shared-lib -o libmylib.so
# Android x86_64 (Emulator):
lyxc mylib.lyx --target=android-x86_64 --output-type=shared-lib -o libmylib_x86.so
Das erzeugte ''.so'' enthält automatisch:
* ''.note.android.ident'' mit dem API-Level
* Bionic-kompatibles ELF64 (ARM64)
* Kein ''__libc_start_main'' (Shared Library hat keinen ''_start'')
----
===== Schritt 2: JNI-Funktionen exportieren =====
Mit ''@jni'' wird der Funktionsname automatisch in das Java-Format ''Java__'' umgeschrieben:
import std.android.jni;
import std.android.log;
// Aufruf aus Java/Kotlin: com.example.MyApp.hello()
@jni(class="com/example/MyApp", method="hello")
pub fn hello(env: JNIEnv, obj: jobject): void {
// Logcat-Ausgabe:
AndroidLogWrite(ANDROID_LOG_INFO, "MyApp"c, "hello() called"c);
}
// Aufruf aus Java/Kotlin: com.example.MyApp.add(int, int)
@jni(class="com/example/MyApp", method="add")
pub fn add(env: JNIEnv, obj: jobject, a: jint, b: jint): jlong {
return a + b;
}
**Java-Seite:**
public class MyApp {
static { System.loadLibrary("mylib"); }
public native void hello();
public native long add(int a, int b);
}
----
===== Schritt 3: Logging (Logcat) =====
import std.android.log;
// Einfache Ausgabe:
AndroidLogWrite(ANDROID_LOG_DEBUG, "MyTag"c, "Debug-Nachricht"c);
AndroidLogWrite(ANDROID_LOG_ERROR, "MyTag"c, "Fehler aufgetreten"c);
^ Konstante ^ Level ^ Anzeige in logcat ^
| ''ANDROID_LOG_VERBOSE'' | 2 | V |
| ''ANDROID_LOG_DEBUG'' | 3 | D |
| ''ANDROID_LOG_INFO'' | 4 | I |
| ''ANDROID_LOG_WARN'' | 5 | W |
| ''ANDROID_LOG_ERROR'' | 6 | E |
| ''ANDROID_LOG_FATAL'' | 7 | F |
----
===== NativeActivity (ohne Java-Activity) =====
Für rein native Anwendungen (kein Java-Code außer dem Minimal-Glue):
import std.android.native_activity;
import std.android.app_glue;
import std.android.log;
// Einstiegspunkt für NativeActivity:
pub fn android_main(app: int64): void {
// app ist ein Zeiger auf android_app-Struktur
// Ereignisschleife:
var running: int64 := 1;
while running {
// Nachrichten verarbeiten:
var cmd: int64 := peek64(app + 0); // vereinfacht
if cmd == APP_CMD_INIT_WINDOW {
AndroidLogWrite(ANDROID_LOG_INFO, "App"c, "Window initialized"c);
}
if cmd == APP_CMD_TERM_WINDOW {
running := 0;
}
}
}
**APP_CMD_*-Konstanten:**
^ Konstante ^ Wert ^ Bedeutung ^
| ''APP_CMD_INIT_WINDOW'' | 1 | Surface bereit (Render-Start) |
| ''APP_CMD_TERM_WINDOW'' | 2 | Surface wird zerstört |
| ''APP_CMD_GAINED_FOCUS'' | 6 | App hat Fokus |
| ''APP_CMD_LOST_FOCUS'' | 7 | App hat Fokus verloren |
| ''APP_CMD_PAUSE'' | 9 | App wird pausiert |
| ''APP_CMD_RESUME'' | 10 | App wird fortgesetzt |
| ''APP_CMD_STOP'' | 13 | App wird gestoppt |
| ''APP_CMD_DESTROY'' | 14 | App wird zerstört |
----
===== Assets aus APK lesen =====
import std.android.asset;
// Asset-Manager aus ANativeActivity holen (via JNI übergeben):
var assetMgr: AAssetManager := 0; // aus NativeActivity.assetManager
// Asset öffnen (nur aus Lyx-Seite, wenn assetMgr als int64 übergeben):
// AAssetManager_open / AAsset_read werden via FFI aufgerufen
Asset-Zugriffsmodi:
^ Konstante ^ Bedeutung ^
| ''AASSET_MODE_STREAMING'' | Sequentielles Lesen (vorwärts) |
| ''AASSET_MODE_RANDOM'' | Beliebiger Zugriff (seek möglich) |
| ''AASSET_MODE_BUFFER'' | Ganzes Asset in RAM mappen |
----
===== OpenGL ES 2 (gles2) =====
import std.android.gles2;
// Wichtigste GL-Konstanten:
// GL_COLOR_BUFFER_BIT, GL_DEPTH_BUFFER_BIT
// GL_TRIANGLES, GL_TRIANGLE_STRIP
// GL_VERTEX_SHADER, GL_FRAGMENT_SHADER
// GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER
// GL_FLOAT, GL_UNSIGNED_SHORT
// Render-Frame (Beispiel):
// glClearColor(0.0, 0.0, 0.0, 1.0)
// glClear(GL_COLOR_BUFFER_BIT)
// ... Shader, VAO, Draw-Call ...
// eglSwapBuffers(display, surface)
----
===== APK erstellen =====
import std.android.apk_builder;
// Vollständiges APK aus .so + Manifest:
// ApkCreate("myapp.apk"c, "libmylib.so"c, manifest_xml, manifest_len);
Der APK-Builder nutzt ''android/zip_writer'' intern und erzeugt:
* ''AndroidManifest.xml'' (aus ''android/manifest_gen'')
* ''lib/arm64-v8a/libmylib.so''
* Minimaler ''classes.dex''-Stub (für NativeActivity-Aktivierung)
----
===== Eingeschränkte Syscalls (android/restrictions) =====
Unter Android/SELinux sind bestimmte Syscalls gesperrt:
import std.android.restrictions;
// ANDROID_BLOCKED_SYSCALLS enthält die Liste gesperrter Calls:
// fork(), execve(), ptrace(), setuid(), mount(), ...
// Für Netzwerk: bind() auf privilegierten Ports (< 1024) gesperrt
Statt ''fork()'' → Threads via ''std.thread''. Statt ''execve()'' → Nativer JNI-Aufruf oder Intent.
----
===== Welche Unit wann? =====
^ Szenario ^ Unit ^
| Einfache JNI-Funktion aus Java aufrufen | ''android/jni'' + ''@jni''-Annotation |
| Debug-Ausgabe in Logcat | ''android/log'' |
| Rein native App ohne Java | ''android/native_activity'' + ''android/app_glue'' |
| APK selbst bauen | ''android/apk_builder'' |
| Touch-Eingabe verarbeiten | ''android/input'' |
| Sensor-Daten (Gyro, Beschleunigung) | ''android/sensor'' |
| OpenGL ES 2 rendern | ''android/gles2'' |
| APK-Assets lesen | ''android/asset'' |
Letzte Aktualisierung: 2026-06-08