This specification is maintained by the AgentRules Working Group. Have enhancements or want to claim maintainership?Submit Specification →
mobilecritical PriorityID: tauri-android-jni-context-retain-cycle-elimination

Tauri 2.0 Android JNI Context & Native Reference Retain-Cycle Elimination

Enforces strict native memory hygiene across JNI boundaries in Tauri 2.0 Android Kotlin plugins. Prevents persistent Activity retain cycles and ART LocalRef table exhaustion during rapid WebView hot-reloads and navigation by mandating ApplicationContext capture, WeakReference wrapping for Activity instances, and deterministic JNI local reference cleanup.

Prohibited Anti-Pattern: Hard Activity Reference Capture and Unbounded JNI LocalRefs Across Tauri Plugin Invocations

Export Ready-to-Drop Configuration

Select format below to copy code or download file directly.

---
description: Tauri 2.0 Android JNI Context & Native Reference Retain-Cycle Elimination - Enforces strict native memory hygiene across JNI boundaries in Tauri 2.0 Android Kotlin plugins. Prevents persistent Activity retain cycles and ART LocalRef table exhaustion during rapid WebView hot-reloads and navigation by mandating ApplicationContext capture, WeakReference wrapping for Activity instances, and deterministic JNI local reference cleanup.
globs: src-tauri/gen/android/**/*.kt, src-tauri/src/mobile.rs, src-tauri/src/lib.rs, src-tauri/plugins/**/*.kt
alwaysApply: true
verified_by: "agentrules.org"
license: "CC-BY-4.0"
source: "https://agentrules.org/rules/tauri-android-jni-context-retain-cycle-elimination"
---

# Tauri 2.0 Android JNI Context & Native Reference Retain-Cycle Elimination

> **Severity**: CRITICAL | **Category**: mobile
> **Anti-Pattern Warning**: Hard Activity Reference Capture and Unbounded JNI LocalRefs Across Tauri Plugin Invocations

## System Constraints
You are a Principal Mobile Architect enforcing Tauri 2.0 Android JNI and native memory invariants. You must prevent memory leaks, Activity retain cycles, and ART LocalRef table crashes (512-reference limit) caused by persistent Activity holding and non-deterministic JNI reference lifecycles during IPC commands and WebView reloads.

## Strict Guidelines
- Engineers MUST ALWAYS store Android Activity references inside `java.lang.ref.WeakReference<Activity>` within Kotlin plugin state.
- Engineers MUST NEVER capture strong references to `app.activity` in asynchronous plugin callbacks, Rust JNI bridge closures, or background Coroutine scopes.
- Engineers MUST use `activity.applicationContext` rather than `Activity` whenever resolving system services, content resolvers, and broadcast receivers.
- Engineers MUST explicitly release JNI local references using `env.DeleteLocalRef` in Rust/C JNI bridge layers when looping over collections or transferring buffers.
- Kotlin plugins MUST override `onActivityDestroyed` or implement lifecycle hooks to nullify cached native delegates and cancel pending Coroutine jobs.
- Commands MUST fail fast with a descriptive error code (`ACTIVITY_DESTROYED`) if `WeakReference.get()` returns `null` instead of forcing a fallback capture.

## Bad vs Good Implementations
### Kotlin Plugin Activity Reference & Coroutine Scope Lifecycle

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: Directly capturing `activity` in a long-running coroutine scope causes an uncollectable Activity retain cycle across Tauri WebView reloads, keeping the entire window hierarchy, views, and native drawing surfaces in memory.
```kotlin
class HardwareBridgePlugin(val activity: Activity) : Plugin(activity) {
    private val scope = CoroutineScope(Dispatchers.IO)
    
    @Command
    fun captureSensorData(invoke: Invoke) {
        // ANTI-PATTERN: Retaining strong Activity reference across asynchronous thread boundary
        scope.launch {
            val sensorManager = activity.getSystemService(Context.SENSOR_SERVICE) as SensorManager
            val result = readHardwareBuffer(sensorManager)
            invoke.resolve(result)
        }
    }
}
```

#### Production Standard (Correct)
> **Standard**: Uses `WeakReference` for the Activity, stores `applicationContext` for long-lived system service lookups, and properly clears references and cancels Coroutines on `onActivityDestroyed`.
```kotlin
class HardwareBridgePlugin(activity: Activity) : Plugin(activity) {
    private val activityRef = WeakReference(activity)
    private val appContext: Context = activity.applicationContext
    private val job = SupervisorJob()
    private val scope = CoroutineScope(Dispatchers.IO + job)

    @Command
    fun captureSensorData(invoke: Invoke) {
        scope.launch {
            val currentActivity = activityRef.get()
            if (currentActivity == null || currentActivity.isFinishing || currentActivity.isDestroyed) {
                invoke.reject("ACTIVITY_DESTROYED: Cannot capture sensor on destroyed activity")
                return@launch
            }
            
            // Safe: Use appContext for system services to prevent Activity leakage
            val sensorManager = appContext.getSystemService(Context.SENSOR_SERVICE) as SensorManager
            val result = readHardwareBuffer(sensorManager)
            invoke.resolve(result)
        }
    }

    override fun onActivityDestroyed() {
        job.cancel()
        activityRef.clear()
        super.onActivityDestroyed()
    }
}
```

### Rust-JNI Local Reference Cleanup in Iterative Native Calls

#### Bad Antipattern (AI Common Hallucination)
> **Why it fails**: Android ART limits native local reference tables to 512 entries per thread. Allocating objects within a loop without `delete_local_ref` or an auto-cleanup local frame triggers a `JNI ERROR (app bug): local reference table overflow` crash.
```rust
// ANTI-PATTERN: Allocating JNI local references inside a loop without frame pops or explicit cleanup
#[no_mangle]
pub unsafe extern "C" fn Java_com_plugin_Bridge_processItems(
    mut env: JNIEnv,
    _class: JClass,
    items: jobjectArray,
) {
    let len = env.get_array_length(&items).unwrap();
    for i in 0..len {
        let item = env.get_object_array_element(&items, i).unwrap();
        // Leaks local reference inside the loop; exhausts ART JNI 512-reference table on large arrays
        let _ = env.call_method(&item, "process", "()V", &[]);
    }
}
```

#### Production Standard (Correct)
> **Standard**: Wraps each loop iteration in `env.with_local_frame(16, ...)`, guaranteeing that all temporary JNI local references are freed at the end of every step and avoiding ART table exhaustion.
```rust
// SAFE: Scoping local references with with_local_frame to deterministically free ART JNI handles
#[no_mangle]
pub unsafe extern "C" fn Java_com_plugin_Bridge_processItems(
    mut env: JNIEnv,
    _class: JClass,
    items: jobjectArray,
) -> jboolean {
    let len = match env.get_array_length(&items) {
        Ok(l) => l,
        Err(_) => return 0,
    };

    for i in 0..len {
        // Allocate an isolated local frame capacity of 16 references per iteration
        let res = env.with_local_frame(16, |env| {
            let item = env.get_object_array_element(&items, i)?;
            env.call_method(&item, "process", "()V", &[])?;
            Ok(())
        });

        if res.is_err() {
            return 0;
        }
    }
    1
}
```

Recommended MCP Servers (mcp-serverlist.com)

Equip agent with system tools to verify code for this stack.

Recommended Agent Skills (askills.dev)

Ready-to-use executable skill functions tailored to this stack.

Architectural Specification & Invariant Rationale

Architectural Invariant Design: Tauri 2.0 Android JNI Context & Native Reference Retain-Cycle Elimination

Tauri 2.0 Android JNI Context & Native Reference Retain-Cycle Elimination defines an industrial-grade behavioral standard for mobile engineering. Enforces strict native memory hygiene across JNI boundaries in Tauri 2.0 Android Kotlin plugins. Prevents persistent Activity retain cycles and ART LocalRef table exhaustion during rapid WebView hot-reloads and navigation by mandating ApplicationContext capture, WeakReference wrapping for Activity instances, and deterministic JNI local reference cleanup. Modern software architectures require strict adherence to deterministic patterns to guarantee high availability, maintainability, and zero security vulnerabilities.

Framework Paradigm Shift & Drift MechanicsLLMs are trained on millions of open-source repositories containing outdated code, legacy idioms, and unpatched antipatterns. When coding assistants generate implementations for this stack, they default to common statistical tokens: Hard Activity Reference Capture and Unbounded JNI LocalRefs Across Tauri Plugin Invocations Without an explicit system instruction file, developers spend hours debugging silent failures.
Failure Modes & Production Blast Radius

Hazard Analysis: What Breaks Without This Rule

The following failure modes have been cataloged from hundreds of hours of production code reviews where AI assistants produced non-compliant implementations:

Failure ModeAI Symptom (The Anti-Pattern)SeverityProduction Impact & Remedy
AI Hallucination & Deprecation DriftHard Activity Reference Capture and Unbounded JNI LocalRefs Across Tauri Plugin InvocationsCritical
Violating this industrial standard introduces regression bugs, security risks, and unexpected runtime failures in production.
Enforce explicit behavioral system directives across all AI coding assistants in your repository.
Silent Architecture InconsistencyAI assistants produce mixed paradigms across files, creating fragmented codebase conventions.Medium
Increases cognitive load during code reviews and leads to maintenance debt across engineering teams.
Pin project conventions in .cursor/rules, CLAUDE.md, and AGENTS.md so all team agents follow identical guardrails.
Architectural Directives in Detail

Step-by-Step Production Directives

Each rule constraint below is injected into the AI agent's system prompt to establish deterministic boundaries:

1

Engineers MUST ALWAYS store Android Activity references inside `java.lang.ref.WeakReference<Activity>` within Kotlin plugin state.

Engineers MUST ALWAYS store Android Activity references inside `java.lang.ref.WeakReference<Activity>` within Kotlin plugin state.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
2

Engineers MUST NEVER capture strong references to `app.activity` in asynchronous plugin callbacks, Rust JNI bridge closures, or background Coroutine scopes.

Engineers MUST NEVER capture strong references to `app.activity` in asynchronous plugin callbacks, Rust JNI bridge closures, or background Coroutine scopes.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
3

Engineers MUST use `activity.applicationContext` rather than `Activity` whenever resolving system services, content resolvers, and broadcast receivers.

Engineers MUST use `activity.applicationContext` rather than `Activity` whenever resolving system services, content resolvers, and broadcast receivers.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
4

Engineers MUST explicitly release JNI local references using `env.DeleteLocalRef` in Rust/C JNI bridge layers when looping over collections or transferring buffers.

Engineers MUST explicitly release JNI local references using `env.DeleteLocalRef` in Rust/C JNI bridge layers when looping over collections or transferring buffers.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
5

Kotlin plugins MUST override `onActivityDestroyed` or implement lifecycle hooks to nullify cached native delegates and cancel pending Coroutine jobs.

Kotlin plugins MUST override `onActivityDestroyed` or implement lifecycle hooks to nullify cached native delegates and cancel pending Coroutine jobs.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
6

Commands MUST fail fast with a descriptive error code (`ACTIVITY_DESTROYED`) if `WeakReference.get()` returns `null` instead of forcing a fallback capture.

Commands MUST fail fast with a descriptive error code (`ACTIVITY_DESTROYED`) if `WeakReference.get()` returns `null` instead of forcing a fallback capture.

Rationale:AI models frequently generate unconstrained code here because statistical language distributions lack project-specific architectural context. Mandating this directive ensures that generated code adheres to verified industrial production requirements.
Enforcement:Integrated into the Agent Pack rule file. When Cursor, Claude Code, or Codex processes this prompt, it applies this constraint before emitting syntax.
Technical Questions & Answers

Frequently Asked Technical Questions

Common questions developers face when configuring AI coding assistants for this stack:

Q:Why is Tauri 2.0 Android JNI Context & Native Reference Retain-Cycle Elimination critical for AI coding assistants?

Enforces strict native memory hygiene across JNI boundaries in Tauri 2.0 Android Kotlin plugins. Prevents persistent Activity retain cycles and ART LocalRef table exhaustion during rapid WebView hot-reloads and navigation by mandating ApplicationContext capture, WeakReference wrapping for Activity instances, and deterministic JNI local reference cleanup. Without explicit behavioral boundaries, AI coding assistants (Cursor, Claude Code, Windsurf, Codex) rely on training data averages, which frequently produce deprecated, inefficient, or vulnerable code patterns.

Q:How does adopting this rule prevent AI hallucinations?

This rule explicitly declares what the AI must avoid (Hard Activity Reference Capture and Unbounded JNI LocalRefs Across Tauri Plugin Invocations) and prescribes verified positive examples. Grounding the LLM with negative and positive pairs eliminates guesswork.

Q:Which AI coding environments are supported?

This specification is multi-platform compatible: Cursor (.cursor/rules/*.mdc & .cursorrules), Anthropic Claude Code (CLAUDE.md), Windsurf (.windsurfrules), Codex / Cline (AGENTS.md), and GitHub Copilot (.github/copilot-instructions.md).

Q:Can I combine this rule with other technology stacks?

Yes. Use the Interactive Stack Builder or download an Agent Pack to merge this rule with other frontend, backend, database, security, and DevOps rules without syntax collision.

Open Specification · CC BY 4.0·Free for Personal, Team & Commercial In-Repo Use

Citation & Open Source Attribution Guidelines

This specification is licensed under Creative Commons Attribution 4.0 International (CC BY 4.0). You are completely free to adapt and share this rule in your workspace. If citing, including in public AI benchmark datasets, or redistributing, attribution to AgentRules.org is required.

Related MOBILE Rules

View all rules →
1 Rule in Stack

Tauri