Skip to content

Fix: TransactionTooLargeException crash #6063

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: develop
Choose a base branch
from

Conversation

0nko
Copy link
Member

@0nko 0nko commented May 14, 2025

Task/Issue URL: https://app.asana.com/1/137249556945/project/1202552961248957/task/1210238988495963?focus=true

Description

This PR saves the TabPagerAdapter to disk instead of the state instance Bundle, which should potentially prevent any/most TransactionTooLargeException crashes when the saveStateToDataStore feature flag is enabled.

Although the tab swiping feature reduces the limit of active tabs from 40 to 20, which should prevent the original crash, the ViewPager2 adapter needs to save its state, which again increases the amount of data saved and causes the crash.

Steps to test this PR

  • Generate 100 tabs in DDG developer settings
  • Enable Don't keep activities in the device Developer options
  • Swipe between tabs (~40 times)
  • Switch to a different app
  • Reopen DDG
  • Notice the app doesn't crash

@0nko 0nko requested review from malmstein and nalcalag as code owners May 14, 2025 09:24
@@ -319,7 +323,13 @@ open class BrowserActivity : DuckDuckGoActivity() {
super.onSaveInstanceState(outState)

if (swipingTabsFeature.isEnabled) {
outState.putParcelable(KEY_TAB_PAGER_STATE, tabPagerAdapter.saveState())
if (swipingTabsFeature.isSaveStateToDataStoreEnabled) {
runBlocking {
Copy link
Contributor

Choose a reason for hiding this comment

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

why do we have a runblocking here? this will block main thread and cause ANRs

Copy link
Member Author

Choose a reason for hiding this comment

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

The TabsDataStore.saveState() function is suspended so I was worried that when the activity is finishing and onSaveInstanceState() would launch a coroutine and then continue, the app might get killed before it can finish saving data. Previously, the state was also getting saved synchronously. But maybe I'm wrong..

Copy link
Member Author

@0nko 0nko May 14, 2025

Choose a reason for hiding this comment

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

AI agrees :)

Launching a coroutine in onSaveInstanceState() is not safe

onSaveInstanceState() is called when an Activity might be destroyed, and the Android system expects you to quickly save minimal state data synchronously in this method. The Bundle you provide must be completely populated by the time the method returns.

Problems with launching a coroutine in onSaveInstanceState():

  • Coroutines run asynchronously, so any state saved after the method returns won't make it into the Bundle
  • The Activity might be destroyed before the coroutine completes
  • The lifecycle of the coroutine wouldn't be properly managed

Instead, you should:

  • Save state synchronously in onSaveInstanceState()
  • Do any heavy operations earlier in the Activity lifecycle
  • Use ViewModel with SavedStateHandle for more complex state saving
  • If you need to save state that requires async operations, it's better to do that proactively during normal Activity operation, not during the saving process.

Copy link
Contributor

Choose a reason for hiding this comment

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

Just flagging this is danger territory and maybe the approach is not correct. The activity is being destroyed but you are blocking the UI thread until operation finishes and that will produce an ANR.

I see you want to persist the ViewState, and it's suspend because you rely on DataStore. But is dataStore the place where to store that state? do you need to persist into disk? do you want to recover from app being killed?

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.

2 participants