Skip to content

fix(android): keep a strong reference to the fallback font picker - #405

Merged
mfazekas merged 6 commits into
mainfrom
claude/fix-369-font-picker-gc
Oct 29, 2026
Merged

mfazekas merged 6 commits into
mainfrom
claude/fix-369-font-picker-gc

Conversation

@mfazekas

@mfazekas mfazekas commented Oct 3, 2026

Copy link
Copy Markdown
Collaborator

rive-android keeps FontFallbackStrategy.stylePicker only in a WeakReference (still the case in 11.12.1), and HybridRiveFontConfig.applyFallbackFonts() passed it an anonymous object nothing else held. A GC shortly after setFallbackFonts() collected the picker, so text needing a fallback (Thai, Hebrew, Arabic, CJK) rendered blank with FindFontFallback - no fallback found. This keeps a strong reference until clearFallbackFonts(). Affects both Android runtimes, since the file is shared.

Root cause and fix by @edgartoomikw in #369, thanks!

Adds an Issue369FontPickerGC reproducer page: it sets Kanit as the fallback, loads the font another 250 times to make the Java heap collect, then mounts a view showing Thai text. On the Pixel 6 emulator with rive-android 11.12.1, the text is blank without the fix and renders in Kanit with it.

Fixes #369

rive-android stores FontFallbackStrategy.stylePicker in a WeakReference,
so the anonymous picker was collected at the next GC and fallback fonts
silently stopped working (#369).
@mfazekas
mfazekas force-pushed the claude/fix-369-font-picker-gc branch from 4eb9204 to cff1c48 Compare October 28, 2026 08:41
@HayesGordon
HayesGordon requested a balanced review from Copilot October 28, 2026 09:56

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Overlapping font updates can leave the installed picker unretained, and stale reproducer runs can overwrite Reset or restart state because cancellation and font setup are not safely coordinated.

2 open issues
What changed in this PR

This PR addresses #369 by retaining the Android fallback-font picker, preventing garbage collection from disrupting non-Latin text rendering. The shared implementation covers both Android runtimes.

Changes:

  • Keep a strong reference to the fallback picker until clearFallbackFonts().
  • Add a Thai-text reproducer that repeatedly loads Kanit to trigger garbage collection before rendering.
File Description
example/​src/​reproducers/​Issue369FontPickerGC.tsx Adds a GC-stress reproducer with Start and Reset controls.
android/​src/​main/​java/​com/​margelo/​nitro/​rive/​HybridRiveFontConfig.kt Retains the installed fallback picker and releases it when fonts are cleared.

Review effort: Balanced

Comment thread android/src/main/java/com/margelo/nitro/rive/HybridRiveFontConfig.kt Outdated
Comment thread example/src/reproducers/Issue369FontPickerGC.tsx Outdated
Concurrent applyFallbackFonts/clearFallbackFonts calls run on
Dispatchers.Default and could interleave the strong-ref and stylePicker
writes, leaving rive-android weakly holding a picker nobody else retains.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

Reset and restart can let a cancelled run resume after an await, corrupt progress, or mount ThaiText before the replacement run completes setup, requiring per-run cancellation checks or serialized restarts.

No open issues 🎉

1 previously missed issue

In code that hasn't changed since last review

Medium severity Prevent stale run from resuming after reset and restart

example/​src/​reproducers/​Issue369FontPickerGC.tsx:43

Reset followed by Start reuses running.current, allowing an older run to resume after an await and update progress or mount ThaiText before the replacement run finishes fallback setup and GC churn. Reset alone can also leave the cancelled run reporting churning with 250 loads. Use a per-run generation token invalidated by reset/unmount and checked after each await, or serialize restarts until the previous run exits.

2 resolved since last review

Review effort: Balanced

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 Needs a closer look

The reproducer can restart font setup after Reset or unmount and leaves process-wide fallback fonts installed when navigating away.

No open issues 🎉

2 previously missed issues

In code that hasn't changed since last review

Medium severity Clear global fallback fonts when leaving the page

example/​src/​reproducers/​Issue369FontPickerGC.tsx:83

Navigating away without pressing Reset leaves Kanit installed as the process-wide fallback: cancel() only invalidates the run, while the new native strong reference keeps the picker and its fonts alive. Subsequent example pages therefore inherit this reproducer's font configuration. Please also enqueue clearFallbackFonts() during cleanup, after cancelling the run, so leaving the page removes its global side effects.

Medium severity Prevent reset or unmount from restarting after font clearing

example/​src/​reproducers/​Issue369FontPickerGC.tsx:103

If Reset is pressed while this Start handler awaits native font clearing, the handler still resumes and creates a fresh run, overriding Reset and eventually mounting the view again. Unmounting during the same wait can also restart font setup. Capture the run ID after invoking reset() and check it before calling start() so cancellation also covers this pending restart.

Review effort: Balanced

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

No active review issues remain for the Android fallback-font picker retention change.

No open issues 🎉

Review effort: Balanced

@HayesGordon HayesGordon left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@mfazekas
mfazekas merged commit 0512e45 into main Oct 29, 2026
15 checks passed
@mfazekas
mfazekas deleted the claude/fix-369-font-picker-gc branch October 29, 2026 15:40
mfazekas pushed a commit that referenced this pull request Oct 29, 2026
🤖 I have created a release *beep* *boop*
---


##
[0.5.9](v0.5.8...v0.5.9)
(2026-10-29)


### Bug Fixes

* **android:** keep a strong reference to the fallback font picker
([#405](#405))
([0512e45](0512e45)),
closes
[#369](#369)

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Android] RiveFonts: loadFont({name}) fails for script fonts on OEM fonts.xml; systemFallback() is not script-aware (tofu for Thai/Hebrew/Arabic/CJK)

3 participants