Skip to content

Fix ANR when initializing FontLoader#2819

Merged
vegaro merged 2 commits into
mainfrom
git-249-anrs-when-calling-purchasesconfigure
Nov 25, 2025
Merged

Fix ANR when initializing FontLoader#2819
vegaro merged 2 commits into
mainfrom
git-249-anrs-when-calling-purchasesconfigure

Conversation

@vegaro

@vegaro vegaro commented Nov 7, 2025

Copy link
Copy Markdown
Member

Reported here #2818

I believe the cause is we are doing context.cacheDir on initialization (in main thread). Looking at the stacktrace it looks like it does some checks on the disk on main thread that could be causing ANRs.

I tested with StrictMode and after this fix, the warning is gone.

@codecov

codecov Bot commented Nov 7, 2025

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 50.00000% with 5 lines in your changes missing coverage. Please review.
✅ Project coverage is 78.24%. Comparing base (33098b0) to head (6ba0bbd).
⚠️ Report is 5 commits behind head on main.

Files with missing lines Patch % Lines
...in/com/revenuecat/purchases/paywalls/FontLoader.kt 50.00% 2 Missing and 3 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2819      +/-   ##
==========================================
- Coverage   78.24%   78.24%   -0.01%     
==========================================
  Files         325      325              
  Lines       12728    12730       +2     
  Branches     1738     1739       +1     
==========================================
+ Hits         9959     9960       +1     
  Misses       2038     2038              
- Partials      731      732       +1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@OptIn(InternalRevenueCatAPI::class)
internal class FontLoader(
private val context: Context,
private val cacheDir: File = File(context.cacheDir, "rc_paywall_fonts"),

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.

Hmm does creating a File pointer actually perform disk operations? I believe this just creates a pointer, but doesn't actually interact with disk, until you actually want to perform some operations (which I think should be happening in the ioScope?)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Right... I think you're correct 🤔

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

What if the issue is context.cacheDir?

Looking at the stacktrace it looks like it does some checks?

	at android.app.ActivityThread$AndroidOs.access(ActivityThread.java:8582)
	at java.io.UnixFileSystem.checkAccess(UnixFileSystem.java:332)
	at java.io.File.exists(File.java:829)
	at android.app.ContextImpl.ensurePrivateDirExists(ContextImpl.java:819)
	at android.app.ContextImpl.ensurePrivateCacheDirExists(ContextImpl.java:815)
	at android.app.ContextImpl.getCacheDir(ContextImpl.java:926)
	at android.content.ContextWrapper.getCacheDir(ContextWrapper.java:328)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

For reference https://android.googlesource.com/platform/frameworks/base/+/HEAD/core/java/android/app/ContextImpl.java#822

I did check and this PR's changes removes the StrictMode warnings

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.

ohhh right... my guess would be that indeed it has something to do with context.cacheDir as you said. I guess that's checking for the cache dir existance, and so performing disk operations... Good to know!

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

yes! very sneaky haha

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

btw there are so many other warnings with StrictMode related to shared preferences, but that's another story :D

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

checking for the cache dir existance

Yes, I've seen that.

RE StrictMode, I think we should enable it in our Maestro test app (and fix all errors). But indeed, different story.

@vegaro vegaro marked this pull request as ready for review November 25, 2025 10:29
@vegaro vegaro requested a review from a team as a code owner November 25, 2025 10:29
@vegaro vegaro requested review from a team and tonidero November 25, 2025 10:33

@JayShortway JayShortway left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Nice, thanks for fixing this!

@tonidero tonidero 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.

Looks good! Just a question

) {
private var hasCheckedFoldersExist: AtomicBoolean = AtomicBoolean(false)

private val cacheDirectory by lazy(LazyThreadSafetyMode.NONE) {

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.

Should we use a synchronized safety mode? It shouldn't happen that we initialize this from multiple threads... But might be slightly safer.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I actually don't think that would add any safety since there is no need to lock anything. If two threads hit this at the same time without locking they would just return a File pointing to the same directory, since File(context.cacheDir, "rc_paywall_fonts") is idempotent. And if they don't create the File, they would just return providedCacheDir which is also not necessary to synchronize since it's just a get

@vegaro vegaro added this pull request to the merge queue Nov 25, 2025
Merged via the queue into main with commit 49ef51e Nov 25, 2025
23 checks passed
@vegaro vegaro deleted the git-249-anrs-when-calling-purchasesconfigure branch November 25, 2025 19:12
github-merge-queue Bot pushed a commit that referenced this pull request Nov 26, 2025
**This is an automatic release.**

> [!WARNING]  
> If you don't have any login system in your app, please make sure your
one-time purchase products have been correctly configured in the
RevenueCat dashboard as either consumable or non-consumable. If they're
incorrectly configured as consumables, RevenueCat will consume these
purchases. This means that users won't be able to restore them from
version 9.0.0 onward.
> Non-consumables are products that are meant to be bought only once,
for example, lifetime subscriptions.


## RevenueCatUI SDK
### 🐞 Bugfixes
* Fix ANR when initializing FontLoader (#2819) via Cesar de la Vega
(@vegaro)
### Paywallv2
#### 🐞 Bugfixes
* Fix `Template7CustomPackagesTestData` (#2875) via Cesar de la Vega
(@vegaro)
* Fix predownloading of fonts if first offering doesn't have paywall
components (#2873) via Cesar de la Vega (@vegaro)

### 🔄 Other Changes
* Extract parameters for non paid revenue tracking API to use objects
(#2871) via Toni Rico (@tonidero)
* Bump fastlane from 2.229.0 to 2.229.1 (#2869) via dependabot[bot]
(@dependabot[bot])

Co-authored-by: revenuecat-ops <ops@revenuecat.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants