Repository navigation
[shared_preferences] Keep Kotlin classes used through JNI in Android release builds - #13311
Conversation
…builds shared_preferences_android 2.5.0 calls its Kotlin code through JNI. The generated Dart bindings and package:jni look up several Kotlin standard library classes by name, such as kotlin.coroutines.Continuation. R8 renames them in release builds, so every call fails with a NoSuchMethodError and SharedPreferences.getInstance() and SharedPreferencesWithCache.create() throw at startup. Adds consumer R8 rules that keep those classes, as Pigeon's native interop guide describes for plugins, until package:jni ships them (dart-lang/native#3732).
|
Thanks for your pull request! It looks like this may be your first contribution to a Google open source project. Before we can look at your pull request, you'll need to sign a Contributor License Agreement (CLA). View this failed invocation of the CLA check for more information. For the most up to date status, view the checks section at the bottom of the pull request. |
|
It looks like this pull request may not have tests. Please make sure to add tests or get an explicit test exemption before merging. If you are not sure if you need tests, consider this rule of thumb: the purpose of a test is to make sure someone doesn't accidentally revert the fix. Ask yourself, is there anything in your PR that you feel it is important we not accidentally revert back to how it was before your fix? Reviewers: Read the Tree Hygiene page and make sure this patch meets those guidelines before LGTMing. If both you and the PR author believe this PR qualifies for a test exemption, escalate to the tech lead for the changed area to review and apply the |
There was a problem hiding this comment.
Code Review
This pull request updates the shared_preferences_android package to version 2.5.1. It introduces a consumer-rules.pro file with Proguard/R8 keep rules for specific Kotlin classes, including kotlin.Unit, kotlin.Result, and coroutine-related classes, to prevent them from being stripped or renamed in release builds. It also configures build.gradle.kts to apply these consumer Proguard files, and updates pubspec.yaml and CHANGELOG.md accordingly. There are no review comments, and I have no feedback to provide.
…n integrated into the package yet (flutter/packages#13311). Without this patch, the R8 obfuscates methods and the app throws "NoSuchMethod" errors on startup which hang the app because the calls are awaited (even if it did bypass the wait, the shared preferences wouldn't work correctly anyway).
… JNI Kotlin classes, verifier refuses renamed ones alpha.125's pub wave took shared_preferences_android to 2.5.0, whose Pigeon JNI bindings resolve kotlin.coroutines.Continuation/Unit/Result by name. R8 renamed them in the release APK, the first prefs read before runApp threw NoSuchMethodError, and the app never left the Android splash. - pubspec: shared_preferences_android '>=2.4.28 <2.5.0' (pre-JNI impl) - android/app/proguard-rules.pro keeps the six JNI-resolved classes - verify_android_apk.py: inspect_dex_keeps refuses an APK where any of the three was renamed (+3 tests); receipt gains kept_jni_classes - CLAUDE.md: root cause + verification record Upstream: flutter/flutter#194649, flutter/packages#13311. Co-Authored-By: Claude Fable 5.1 <[email protected]>
…in-keep-rules # Conflicts: # packages/shared_preferences/shared_preferences_android/CHANGELOG.md
tarrinneal
left a comment
There was a problem hiding this comment.
Thank you for throwing this together so quickly
|
autosubmit label was removed for flutter/packages/13311, because - This pull request has the |
|
@tarrinneal, @mboetger thanks for the approval! Looks like the bot pulled |
|
@tarrinneal @mboetger thanks for merging! The release run for the merge commit failed at "Wait for test check-in", since the LUCI check it waits for didn't show up within 60 seconds (https://github.com/flutter/packages/actions/runs/39526055358). So 2.5.2 isn't on pub.dev yet. Could one of you re-run it once post-submit has run? |
flutter/packages@618afba...1383427 2026-10-21 [email protected] [shared_preferences] Keep Kotlin classes used through JNI in Android release builds (flutter/packages#13311) 2026-10-20 [email protected] [webview_flutter_wkwebview] Roll back iOS and macOS versions (flutter/packages#13354) 2026-10-20 [email protected] [camera]fix resolution test failure due to hardware restriction (flutter/packages#13356) 2026-10-20 [email protected] [google_maps_flutter_web] Fail fast and deduplicate map setup in integration tests (flutter/packages#13092) 2026-10-20 [email protected] [camera_avfoundation] Fix Swift analyzer issues in tests (flutter/packages#13340) 2026-10-20 [email protected] Sync release-material_ui-1.8.0 to main (flutter/packages#13346) 2026-10-20 [email protected] [material_ui] add a `barFocusNode` to `SearchAnchor.bar` (ref: flutter#186073) (flutter/packages#12863) 2026-10-20 [email protected] [in_app_purchase_storekit] Expose sk2Transaction on SK2PurchaseDetails (flutter/packages#13341) 2026-10-20 [email protected] Sync release-cupertino_ui-1.1.4 to main (flutter/packages#13350) 2026-10-20 [email protected] [google_maps_flutter_web] Don't hang `onMapCreated` when tiles fail to load (flutter/packages#13059) 2026-10-20 [email protected] [material_ui] Unskip web tests that pass on CanvasKit and Skwasm: adaptive_text_selection_toolbar_test, material_button_test, outlined_button_test, text_button_test (flutter/packages#13335) 2026-10-20 [email protected] Sync release-go_router-18.1.1 to main (flutter/packages#13347) 2026-10-20 [email protected] [camera] Add zero-shutter-lag support to camera_android_camerax and camera_avfoundation (flutter/packages#13199) If this roll has caused a breakage, revert this CL and set the roller to dry run mode using the controls here: https://autoroll.skia.org/r/flutter-packages-flutter-autoroll Please CC [email protected] on the revert to ensure that a human is aware of the problem. To file a bug in Flutter: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
2.5.0 (resolved into the lockfile on 2026-10-17) moved to Pigeon JNI interop, and R8 renames the kotlin.coroutines classes it calls, so SharedPreferences.getInstance() in main() throws NoSuchMethodError before runApp() ever renders — the release APK hangs on the splash screen while debug builds work (flutter/flutter#194649, upstream fix flutter/packages#13311). Pin until a fixed version ships, then drop the constraint. The jni FFI plugin leaves the generated plugin lists with the older version.
flutter/packages@618afba...1383427 2026-10-21 [email protected] [shared_preferences] Keep Kotlin classes used through JNI in Android release builds (flutter/packages#13311) 2026-10-20 [email protected] [webview_flutter_wkwebview] Roll back iOS and macOS versions (flutter/packages#13354) 2026-10-20 [email protected] [camera]fix resolution test failure due to hardware restriction (flutter/packages#13356) 2026-10-20 [email protected] [google_maps_flutter_web] Fail fast and deduplicate map setup in integration tests (flutter/packages#13092) 2026-10-20 [email protected] [camera_avfoundation] Fix Swift analyzer issues in tests (flutter/packages#13340) 2026-10-20 [email protected] Sync release-material_ui-1.8.0 to main (flutter/packages#13346) 2026-10-20 [email protected] [material_ui] add a `barFocusNode` to `SearchAnchor.bar` (ref: flutter#186073) (flutter/packages#12863) 2026-10-20 [email protected] [in_app_purchase_storekit] Expose sk2Transaction on SK2PurchaseDetails (flutter/packages#13341) 2026-10-20 [email protected] Sync release-cupertino_ui-1.1.4 to main (flutter/packages#13350) 2026-10-20 [email protected] [google_maps_flutter_web] Don't hang `onMapCreated` when tiles fail to load (flutter/packages#13059) 2026-10-20 [email protected] [material_ui] Unskip web tests that pass on CanvasKit and Skwasm: adaptive_text_selection_toolbar_test, material_button_test, outlined_button_test, text_button_test (flutter/packages#13335) 2026-10-20 [email protected] Sync release-go_router-18.1.1 to main (flutter/packages#13347) 2026-10-20 [email protected] [camera] Add zero-shutter-lag support to camera_android_camerax and camera_avfoundation (flutter/packages#13199) If this roll has caused a breakage, revert this CL and set the roller to dry run mode using the controls here: https://autoroll.skia.org/r/flutter-packages-flutter-autoroll Please CC [email protected] on the revert to ensure that a human is aware of the problem. To file a bug in Flutter: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
flutter/packages@618afba...1383427 2026-10-21 [email protected] [shared_preferences] Keep Kotlin classes used through JNI in Android release builds (flutter/packages#13311) 2026-10-20 [email protected] [webview_flutter_wkwebview] Roll back iOS and macOS versions (flutter/packages#13354) 2026-10-20 [email protected] [camera]fix resolution test failure due to hardware restriction (flutter/packages#13356) 2026-10-20 [email protected] [google_maps_flutter_web] Fail fast and deduplicate map setup in integration tests (flutter/packages#13092) 2026-10-20 [email protected] [camera_avfoundation] Fix Swift analyzer issues in tests (flutter/packages#13340) 2026-10-20 [email protected] Sync release-material_ui-1.8.0 to main (flutter/packages#13346) 2026-10-20 [email protected] [material_ui] add a `barFocusNode` to `SearchAnchor.bar` (ref: flutter#186073) (flutter/packages#12863) 2026-10-20 [email protected] [in_app_purchase_storekit] Expose sk2Transaction on SK2PurchaseDetails (flutter/packages#13341) 2026-10-20 [email protected] Sync release-cupertino_ui-1.1.4 to main (flutter/packages#13350) 2026-10-20 [email protected] [google_maps_flutter_web] Don't hang `onMapCreated` when tiles fail to load (flutter/packages#13059) 2026-10-20 [email protected] [material_ui] Unskip web tests that pass on CanvasKit and Skwasm: adaptive_text_selection_toolbar_test, material_button_test, outlined_button_test, text_button_test (flutter/packages#13335) 2026-10-20 [email protected] Sync release-go_router-18.1.1 to main (flutter/packages#13347) 2026-10-20 [email protected] [camera] Add zero-shutter-lag support to camera_android_camerax and camera_avfoundation (flutter/packages#13199) If this roll has caused a breakage, revert this CL and set the roller to dry run mode using the controls here: https://autoroll.skia.org/r/flutter-packages-flutter-autoroll Please CC [email protected] on the revert to ensure that a human is aware of the problem. To file a bug in Flutter: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
shared_preferences_android2.5.0 calls its Kotlin code through JNI. The generated Dart bindings andpackage:jnilook up some Kotlin standard library classes by name, but nothing keeps them, so R8 renames them in release builds. Frommapping.txtof a release build with 2.5.0:The generated classes are kept by
@Keep, but R8 rewrites their method descriptors with the renamedContinuation. So every call fails with aNoSuchMethodError, for example:and
SharedPreferences.getInstance(),SharedPreferencesAsyncandSharedPreferencesWithCache.create()all throw. Debug and profile builds work.This PR adds
android/consumer-rules.prowith the six keep rules from Pigeon's native interop guide ("Step 5: Android Release Build Configuration (JNI)") and lists it inconsumerProguardFiles, the same waypackages/pigeon/platform_tests/test_plugindoes. R8 then applies the rules to every app that uses the plugin. The six classes are the ones looked up by name:kotlin.coroutines.Continuationin the descriptors of thesuspendmethods andkotlin.Unitinmessages*.g.dart, pluskotlin.Unit,kotlin.Result,kotlin.Result$Failure,kotlin.coroutines.Continuation,CoroutineSingletonsandIntrinsicsKtinlib/src/kotlin.dartofjni1.1.0. The other Kotlin types in the bindings (DefaultConstructorMarker,EnumEntries) only appear in synthetic constructors andgetEntries, which the plugin never calls. A TODO inconsumer-rules.proandbuild.gradle.ktspoints to dart-lang/native#3732, so the rules can be removed oncejniships them.Fixes flutter/flutter#194649
How I verified it: a fresh
flutter create --platforms androidapp withshared_preferences: ^2.5.5that callsSharedPreferencesWithCache.create()inmain()and counts launches. I built it withflutter build apk --release --target-platform android-arm64(Flutter 3.44.2, AGP 9.0.1, Gradle 9.1.0, R8 on by default) and ran it on an API 37 arm64 emulator.shared_preferences_android2.5.0 from pub.dev, the app stays on the splash screen and logcat shows theNoSuchMethodErrorabove fromSharedPreferencesWithCache.create.dependency_overridespath to this branch,configuration.txtof the build lists the rules from the plugin's consumer file,mapping.txtmaps each class to itself (for examplekotlin.coroutines.Continuation -> kotlin.coroutines.Continuation:), and the app starts. It logslaunches=1, andlaunches=2after a restart.getKeys,getAll,removeandclearthroughSharedPreferencesAsyncand the legacySharedPreferencesAPI fails for both APIs with 2.5.0 and passes for both with this branch.I also ran
flutter testin the package (39 tests pass) andvalidate --check-for-missing-changes,analyze,format,dart-test,publish-checkandlicense-checkof the repo tooling for the package. None of them reported an issue.Test exemption: I'd like to request one. The change is build configuration that only affects R8 in an app's release build, and as far as I can tell no CI target runs a release build of a plugin.
drive-examples, the native integration tests and Firebase Test Lab use debug builds, andandroid_build_all_packagesbuilds the all_packages app in release but doesn't run it. If there is a place where a release-mode test would fit, I'm happy to add one.Pre-Review Checklist
[shared_preferences]///).If you need help, consider asking for advice on the #hackers-new channel on Discord.
Note: The Flutter team is currently trialing the use of Gemini Code Assist for GitHub. Comments from the
gemini-code-assistbot should not be taken as authoritative feedback from the Flutter team. If you find its comments useful you can update your code accordingly, but if you are unsure or disagree with the feedback, please feel free to wait for a Flutter team member's review for guidance on which automated comments should be addressed.Footnotes
Regular contributors who have demonstrated familiarity with the repository guidelines only need to comment if the PR is not auto-exempted by repo tooling. ↩ ↩2