Architecture
Ink is built with a modular architecture that separates the platform integration, core engine, and specific subsystems like rendering and layout.
Architecture Diagram
sequenceDiagram
box "Android App Process" #LightBlue
participant Main as Main Thread<br>(UI)
participant View as InkView<br>(Kotlin)
end
box "Rust Native Layer" #LightYellow
participant JNI as JNI Bridge<br>(android.rs)
participant RenderThread as Render Thread<br>(Rust)
participant AppThread as App Thread<br>(Rust)
participant Engine as Ink Engine<br>(Core)
participant JS as JS Runtime<br>(QuickJS)
end
%% 1. Initialization Phase
Note over Main, JS: 1. Initialization Phase
Main->>View: Constructor
activate View
View->>JNI: nativeInit()
activate JNI
JNI->>Engine: InkEngine::new()
activate Engine
Engine->>JS: Initialize Runtime
Engine->>AppThread: start() -> Spawn Thread
activate AppThread
Note right of AppThread: Event Loop & JS Tick<br>(Update Logic)
Engine-->>JNI: Returns Handle
deactivate Engine
JNI-->>View: Returns nativeHandle
deactivate JNI
deactivate View
%% 2. Surface Creation & Render Loop
Note over Main, JS: 2. Surface Creation & Render Loop
Main->>View: surfaceCreated(holder)
activate View
View->>JNI: nativeSetSurface(surface)
activate JNI
JNI->>RenderThread: Spawn Render Thread
activate RenderThread
loop Render Loop (16ms)
RenderThread->>JNI: Lock Native Window
RenderThread->>Engine: draw_frame(renderer)
activate Engine
Engine->>AppThread: Read App State (Read Lock)
AppThread-->>Engine: Current State
Engine->>Engine: Skia Draw Commands
Engine-->>RenderThread: Render Completed
deactivate Engine
RenderThread->>JNI: Unlock & Post Buffer
Note right of RenderThread: Pixels displayed on Screen
RenderThread->>RenderThread: Sleep 16ms
end
JNI-->>View: Return
deactivate JNI
deactivate View
%% 3. JS API Call Flow
Note over Main, JS: 3. JS API Call Flow (e.g. rokid.speech.playTTS)
rect rgba(200, 200, 200, 0.1)
AppThread->>JS: Execute JS Script
activate JS
JS->>AppThread: Native Binding Call<br>(send_play_tts)
deactivate JS
AppThread->>AppThread: Enqueue EngineEvent::Rpc
Note right of AppThread: Event Loop processes RPC
AppThread->>Engine: handle_engine_event()
activate Engine
Engine->>JNI: AndroidRpcHandler::handle_play_tts()
activate JNI
JNI->>JNI: Attach Current Thread to JVM
JNI->>View: Call Java Method<br>(callback.onPlayTts)
activate View
View->>Main: Handle Logic (e.g. TTS SDK)
View-->>JNI: Return
deactivate View
JNI-->>Engine: Return
deactivate JNI
deactivate Engine
endCore Modules
1. Platform Layer (Android)
The entry point for Android applications. It handles the SurfaceView lifecycle, touch events, and initializes the Rust engine via JNI.
2. Rust Core Engine
The heart of Ink. It coordinates the event loop, manages the component tree, and synchronizes the state between the JavaScript runtime and the renderer.
3. JavaScript Runtime (QuickJS)
Executes the application logic. It provides the rokid global object for accessing system capabilities (network, speech, etc.) and manipulating the UI tree.
4. Layout Engine (Taffy)
Calculates the position and size of every element in the UI tree based on CSS-like style properties (Flexbox).
5. Renderer (Skia)
Takes the calculated layout and the component tree to draw the UI. It uses Skia, the same graphics engine used by Chrome and Android, to ensure high-quality and performant rendering.