Skip to content

Architecture

Ink is built with a modular architecture that separates the platform integration, core engine, and specific subsystems like rendering and layout.

Architecture Diagram

mermaid
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
    end

Core 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.

Released under the Apache-2.0 License.