DEV Community

Cover image for Debugging Android Backdrop Persistence: A Capacitor Story
ekko1500
ekko1500

Posted on

Debugging Android Backdrop Persistence: A Capacitor Story

Porting a complex React app like Scratch-GUI to mobile using Capacitor can surface bugs that don't appear on desktop. Recently, we fixed a frustrating issue: backdrop edits (drawings and erasures) weren't saving in .sb3 files on Android.

Here's what went wrong and how we fixed it.


1. The Main Problem: Edits Weren't Saving

Two issues caused backdrop changes to disappear when saving.

A. Storage Configuration Typo

In storage.js, we had a typo in the asset storage config. It was trying to use invalid "create" and "update" helpers, triggering failed network requests in the Capacitor environment.

Fix: Corrected the typo and simplified addWebStore to only use the GET helper for local assets.

B. Save Button Didn't Commit Pending Edits

The Paint Editor (from scratch-paint) normally saves changes when you click away from the canvas. On Android, clicking the "Save" button didn't trigger that "focus lost" event, so the latest drawing wasn't being saved.

Fix: Added a manual sync step before saving — calling document.activeElement.blur() and waiting 100ms to ensure all edits were committed.


2. The reader.addEventListener Error

We hit a strange error: TypeError: reader.addEventListener is not a function — and it only appeared on Android.

It turns out that in some environments, FileReader doesn't fully support addEventListener. It only has onload and onerror callbacks.

Fix: Added a lightweight polyfill at app startup that adds addEventListener support to FileReader.prototype, mapping it to the existing handler properties.


3. Canvas Performance Warning

Android's WebView kept showing this warning:

Canvas2D: Multiple readback operations using getImageData are faster with the willReadFrequently attribute set to true.

This happens when the canvas is read often — like in Scratch's "touching color" blocks and paint editor fill tools.

Fix: Updated all getContext('2d') calls (in the loupe, video provider, and costume display) to include { willReadFrequently: true }. This tells the browser to optimize for frequent reads, making things much faster.


4. Touch Scrolling Felt Laggy

Chrome and WebView warn when non-passive event listeners are used on scroll-blocking events like touchstart. This can make scrolling feel sluggish.

Fix: Updated global touch handlers to use { passive: true }, letting the browser scroll immediately without waiting for JavaScript.


What We Learned

Cross-platform development isn't just about writing code — it's about understanding the subtle differences in runtime environments. A standard browser API like FileReader or Canvas2D can behave slightly differently in a WebView than in a desktop browser, and those differences can cause major issues in complex apps.

Key takeaway: Make sure your "save" logic works reliably across all input types (touch, mouse, keyboard), and don't hesitate to polyfill environment gaps to keep your libraries happy.


Top comments (0)