Skip to content

Fix widget, animation, and controller lifecycle leaks - #227

Open
Xare123 wants to merge 7 commits into
OpenBubbles:rustpushfrom
Xare123:split/ui-lifecycle-render
Open

Fix widget, animation, and controller lifecycle leaks#227
Xare123 wants to merge 7 commits into
OpenBubbles:rustpushfrom
Xare123:split/ui-lifecycle-render

Conversation

@Xare123

@Xare123 Xare123 commented Jul 25, 2026

Copy link
Copy Markdown

Part 2 of 3. This replaces #225, which mixed too many unrelated themes to review. Split into focused PRs so each can be evaluated on its own.

Series: Part 1 - delivery integrity - Part 2 - lifecycle leaks - Part 3 - reply threads and keyboard

What this fixes

Scrolling a long transcript produced gray error blocks where messages should be, and the app leaked subscriptions and animation tickers as rows were recycled.

Recycled rows crashed on remount. updateObx was a late final callback that got re-initialized when a widget was recycled, throwing LateError and rendering an error block instead of the message. On a long transcript this cascaded: once one row failed, scrolling produced more. Controller state is now torn down on recycle and re-initialized cleanly on remount, with a regression test that mounts, disposes, and remounts the same controller.

Subscriptions outlived their widgets. Event listeners on conversation tiles, message holders, attachment and interactive holders, text bubbles, delivered indicators, and the tablet-mode wrapper were never cancelled. They are now cancelled on dispose.

Animation tickers were not disposed. The balloon, celebration, fireworks, laser, love, and spotlight effect classes each leaked their ticker. Screen effects and bubble effects held controllers past their widget's life.

Partial initialization could be observed. Widgets could build against half-initialized state during profile and contact hydration. Initialization is now guarded so a partially built widget cannot be rendered.

Full-resolution images were decoded eagerly. Conversation view decoded attachments at full resolution before they were needed, spiking memory on image-heavy chats. Decoding is now deferred and size-bounded.

Group avatars churned background controllers. Refreshing a group avatar spun up and discarded controllers repeatedly; it now refreshes in place.

Malformed shared profiles could disturb delivery. A bad CloudKit profile payload could escape an unawaited task. Those failures are now isolated and rate-limited with a bounded retry map, so a repeatedly replayed bad payload cannot consume CPU.

Validation

  • Tests pass, including a new regression test for the recycled-controller remount path.
  • flutter analyze: no errors in any file this PR touches. The analyzer reports 31 pre-existing errors in lib/database/html/, vendored rust_builder/cargokit/, and the telephony_plus example, plus two in test_driver/; all are unchanged by this PR and present on the base branch.
  • Device evidence below was gathered on the combined branch (previously Improve Android responsiveness and message delivery reliability #225), which contained all of these changes together. The split branches have not been separately built into an APK and installed; they are verified here by tests and analysis.
  • Exercised on a Pixel 10 Pro: a device log captured before this change showed a repeating cascade of LateError render failures while scrolling a long transcript. After the change, the same device and same build type rendered 1,788 frames with 10 janky frames (0.56%), p50 5 ms, p95 6 ms, p99 12 ms, and zero render errors.

Notes for review

  • This PR does not change the rustpush submodule pointer.
  • It shares three files with Part 1 (messages_view.dart, rustpush_service.dart, conversation_tile.dart) and three with Part 3 (conversation_view.dart, message_holder.dart, conversation_view_controller.dart), but edits different regions of each. Whichever merges first, I will rebase the others.

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.

1 participant