Skip to content

Android: smoother boot (off-main-thread native work) + release pyjnius fix; bump to 4.1.0#217

Merged
FeodorFitsner merged 5 commits into
mainfrom
android-boot-jank
Jun 25, 2026
Merged

Android: smoother boot (off-main-thread native work) + release pyjnius fix; bump to 4.1.0#217
FeodorFitsner merged 5 commits into
mainfrom
android-boot-jank

Conversation

@FeodorFitsner

Copy link
Copy Markdown
Contributor

Summary

Android boot-time smoothness and a release-mode pyjnius fix, released as 4.1.0 (lockstep across all packages).

Smoother boot — run heavy native work off the platform main thread

The plugin's extractAsset / unzipAsset / loadLibrary MethodChannel handlers ran synchronously on the Android platform main thread, which is where Flutter's Choreographer delivers vsync. Blocking it starves the UI thread of frames, so on-screen animations (e.g. a boot/splash spinner) stutter:

  • first launch → during app-bundle unpack (extractAsset/unzipAsset),
  • every launch → during the pyjnius loadLibrary.

These handlers now run on a background Executor, posting the Result back on the main looper, so the platform thread stays free for vsync.

Fix pyjnius in release (minified) builds

In release builds the consuming app's R8 pass obfuscated/stripped the plugin classes pyjnius resolves by name at runtime — PythonActivity was renamed (e.g. to C.f) and its static mActivity field dropped — breaking pyjnius with:

pyjnius: not available on this platform - type object 'C.f' has no attribute 'mActivity'

(Debug builds, which don't minify, were unaffected.) The plugin now ships consumer ProGuard rules (-keep class com.flet.serious_python_android.** { *; }), automatically merged into the consuming app's R8 pass.

Changes

  • serious_python_android: background-thread the three handlers; add consumer-rules.pro + consumerProguardFiles.
  • Lockstep version bump 4.0.0 → 4.1.0 across all packages (pubspecs, darwin podspec, android gradle) + changelogs.

Notes

  • loadLibrary moving off-thread is safe because System.loadLibrary resolves via the caller class's loader (AndroidPlugin → app loader) and JNI_OnLoad's FindClass uses that same loader regardless of thread; the worker's context ClassLoader is also pinned to the app loader. Verified with a pyjnius app in release.
  • CPython Py_Initialize + imports already run on a separate native worker thread, so they're not addressed here (that's CPU contention, not a main-thread block).

extractAsset/unzipAsset ran synchronously on the Android platform main thread,
blocking Choreographer and starving Flutter's vsync — which froze on-screen
animations (e.g. the boot spinner) during app unpacking on first launch.

Run those two handlers on a background single-thread Executor and post the
MethodChannel Result back on the main looper.

loadLibrary stays on the main thread on purpose: pyjnius's JNI_OnLoad relies on
running there, and loading it off-thread breaks pyjnius (verified).
In release builds the consuming app's R8 pass obfuscated/stripped the plugin's
classes, breaking pyjnius: PythonActivity was renamed (e.g. to "C.f") and its
static `mActivity` field dropped, so pyjnius failed with
"type object 'C.f' has no attribute 'mActivity'". Debug builds (no minify)
were unaffected.

Ship consumer ProGuard rules (-keep com.flet.serious_python_android.**) and wire
them via consumerProguardFiles, so they're merged into the consuming app's R8
pass automatically.
Now that release builds keep the pyjnius bootstrap classes (consumer ProGuard
rules), loadLibrary can move off the main thread too — the earlier pyjnius
breakage was R8 obfuscation, not the threading. System.loadLibrary resolves the
.so via the caller class's loader (AndroidPlugin -> app loader) and JNI_OnLoad's
FindClass uses that same loader regardless of thread; the worker's context
ClassLoader is pinned to the app loader as well. Verified with pyjnius in
release.

This keeps the dlopen + JNI_OnLoad off Choreographer's thread, so the boot
spinner stays smooth during native library loading.
Update .fvmrc to reference Flutter 3.44.3 (was 3.44.2). This patches the project SDK to the latest patch release to pick up bug fixes and minor improvements.
Android boot-time smoothness + release-mode pyjnius fix:
- run extractAsset / unzipAsset / loadLibrary off the platform main thread so
  asset unpacking and native library loading no longer block Choreographer /
  vsync (boot animations stay smooth)
- consumer ProGuard rules keeping the pyjnius bootstrap classes so release
  (minified) builds don't strip PythonActivity.mActivity

Lockstep bump across all packages (pubspecs, darwin podspec, android gradle)
plus changelogs.
@FeodorFitsner FeodorFitsner merged commit f1024ca into main Jun 25, 2026
24 of 53 checks passed
@FeodorFitsner FeodorFitsner deleted the android-boot-jank branch June 25, 2026 00:26
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