Repository navigation
Conversation
…once per process onAttachedToEngine read libapp.so's build ID on the main thread for every engine. With minSdk >= 24 the native libraries are not extracted, so that opened base.apk and its splits as zip files during startup, which is jank and a StrictMode disk-read violation, repeated for each background engine. Read it on first use instead, in recordError, which already runs on the cached thread pool, and share the result across engines.
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize the Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counterproductive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. |
Reading it on the first `recordError` meant the read could happen while the app is short of memory or file descriptors, often the reason it reports an error, and a failed read was cached for the whole process. It is now read once when the plugin attaches, on the thread pool, so the first report doesn't wait for it either.
Description
FlutterFirebaseCrashlyticsPlugin.onAttachedToEngineread the ELF build ID oflibapp.so(ElfBuildIdReader.readBuildId) on the main thread, every time an engine attached. With minSdk >= 24 the native libraries are not extracted (extractNativeLibs=falseis the AGP default), sonativeLibraryDir/libapp.sodoes not exist and the reader opensbase.apk, then each split, as aZipFileto look forlib/<abi>/libapp.so. That is disk I/O during app startup on the main thread, a StrictMode violation, and it is repeated for every background engine (messaging, workmanager, ...).Reproduced on the Pixel 9a emulator with the crashlytics example (debug build, minSdk 24) and a local, uncommitted
StrictMode.ThreadPolicy(detectDiskReads().penaltyLog()) inMainActivity.onCreate:mainFlutterFirebaseCrashlyticsPlugin.onAttachedToEngine->ElfBuildIdReader.readBuildId->readBuildIdFromApk->ZipFile(access,open,fstat,lseek,read,mmapof the central directory)The build ID is only used by
recordError, which already runs oncachedThreadPool. It is now read there on first use, byElfBuildIdReader.cachedBuildId, and cached for the process:libapp.socannot change while the process runs, so every engine shares one read.recordErrorstill prefers the ELF build ID over the Dart snapshot one, as before.Tests
JUnit, run by
android-unit-tests(:firebase_crashlytics:testDebugUnitTest), newFlutterFirebaseCrashlyticsPluginTest:onAttachedToEngine_doesNotReadLibappOnTheMainThread: attaches the plugin to two engines with a context that records which threads look upapplicationInfo(where every read starts). Fails onmain(read on the attaching thread), passes here.cachedBuildId_readsLibappOncePerProcess: three lookups from two engines readlibapp.soonce and return its build ID. Fails if the cache is removed (3 reads).The existing
ElfBuildIdReaderTesthelpers that build an ELF image are now package-private so the new test can reuse them.Related Issues
None found.
Checklist
///).melos run analyze) does not report any problems on my PR.Breaking Change