Skip to content

[BugFix][Android] Restore a container whose context died with the process - #141

Open
leeekyrie wants to merge 1 commit into
tiktok:mainfrom
leeekyrie:fix/restore-container-context-after-process-death
Open

leeekyrie wants to merge 1 commit into
tiktok:mainfrom
leeekyrie:fix/restore-container-context-after-process-death

Conversation

@leeekyrie

Copy link
Copy Markdown
Collaborator

Summary

SparklingContextTransferStation is an in-memory map. It does not survive the process; the task Android restores around it does. So after the system kills a backgrounded app, every container in the restored task looks up a containerId that is no longer in the map, gets null, and:

  • SparklingFragment.onCreateView returns super.onCreateView(...), so the container renders nothing;
  • SparklingActivity.initSparklingFragment skips the whole hybridSchemeParam?.let { ... } block, so hideNavBar and screenOrientation are not applied either.

The result is a blank page under the default white "Sparkling Page" toolbar, even for a container whose scheme asked for no toolbar at all.

Users reach these because Sparkling.navigate() starts the launcher's container with FLAG_ACTIVITY_NEW_TASK alone, which puts a working page on top of the restored task rather than replacing it. The app looks fine on launch, and the back gesture walks down into the blanks.

What changed

A context is two strings — scheme and initData — and the Intent is restored with the task.

  • Sparkling.navigate() puts both on the Intent alongside the containerId it already carries.
  • SparklingActivity.onCreate falls back to restoreSparklingContext() when the transfer station has nothing: it rebuilds a SparklingContext from those extras, keyed by the same containerId, parses the scheme through SchemeParser, and saves it back to the station so SparklingFragment finds it.

Before: a container restored after process death is blank and stays blank.
After: it loads the page it was opened with, with its scheme applied.

Scope and limits

  • What cannot cross a process does not come back. sparklingUIProvider and lifecycleDelegate are host objects; a restored container loads without them.
  • A container with no scheme on the Intent — one that was not opened through Sparkling.navigate() — returns null exactly as before, so nothing else changes.
  • Hosts may still want FLAG_ACTIVITY_CLEAR_TASK on their own launcher entry if they mean the first page to be the bottom of the task. This PR does not change navigate()'s flags, since that is a behaviour choice rather than a bug.

Testing

./gradlew :sparkling:testDebugUnitTest \
  --tests 'com.tiktok.sparkling.SparklingTest' \
  --tests 'com.tiktok.sparkling.SparklingActivityTest'

29 tests, 0 failures, including three new ones:

  • navigateCarriesTheContextOnTheIntent — the scheme and init data reach the Intent.
  • onCreateRebuildsAContextLostWithTheProcess — a context is rebuilt from the extras, is in the transfer station afterwards, and the nav bar the scheme asked to hide is hidden.
  • onCreateWithoutASchemeKeepsTheEmptyContainer — no scheme, no rebuild, no change.

scripts/lint.sh kotlin passes with ktlint 1.8.0.

Found and reproduced on a Pixel 9 emulator (API 37) from an app built on this SDK: put a pushed container on the stack, kill the process, relaunch, and the stack comes back with blanks underneath.

…cess

Summary of change:
- SparklingContextTransferStation is an in-memory map, so it is empty in
  a new process, while the task Android restores around it is not. Every
  restored container looked up an id that was no longer there and got
  null, so SparklingFragment rendered nothing and SparklingActivity
  skipped the scheme's hideNavBar and orientation: a blank page under the
  default "Sparkling Page" toolbar. The user reaches these by pressing
  back, because the launcher Intent starts a new container on top of the
  restored task rather than replacing it.
- A context is made of two strings, scheme and initData, and the Intent
  is restored with the task. navigate() now puts both on the Intent, and
  SparklingActivity rebuilds a context from them when the transfer
  station has none, keyed by the containerId already on the Intent, and
  saves it so the fragment finds it.
- Previous behavior: a container restored after process death was blank
  and stayed blank. New behavior: it loads the page it was opened with.
- What cannot cross a process does not come back: sparklingUIProvider and
  lifecycleDelegate are host objects, and a restored container loads
  without them. A container opened without a scheme is left exactly as
  before.

TEST: ./gradlew :sparkling:testDebugUnitTest --tests
  'com.tiktok.sparkling.SparklingTest' --tests
  'com.tiktok.sparkling.SparklingActivityTest' (29 tests, 0 failures),
  including three new ones: navigate carries the scheme and initData,
  onCreate rebuilds a context from them and hides the nav bar the scheme
  asked to hide, and a container with no scheme is unchanged.

This branch has not been deployed

No deployments
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