Fix StrictMode DiskReadViolations during SDK initialization
Problem
SDK users are experiencing StrictMode DiskReadViolations when initializing the Mixpanel SDK on the main thread. These violations occur at three specific points during initialization, causing issues for apps that enforce strict StrictMode policies.
Solution
This PR addresses all three DiskReadViolation sources by deferring disk I/O operations to background threads or using lazy initialization patterns:
1. PersistentIdentity.getTimeEvents() violation
- Issue: Blocking on
Future.get()when loading SharedPreferences on main thread - Fix: Added main thread detection to return empty map immediately and load cache asynchronously
- Impact: Prevents blocking while maintaining eventual consistency of time events data
2. MPDbAdapter initialization violation
- Issue:
getDatabasePath()called synchronously in constructor - Fix: Implemented lazy initialization pattern for database file path resolution
- Impact: Database path is now resolved only when first accessed, not during construction
3. MixpanelAPI constructor violation
- Issue: Synchronous database file existence check for first launch detection
- Fix: Moved first launch check to background thread via new
checkFirstLaunchAsync()method - Impact: First open event tracking now happens asynchronously without blocking
Testing
- Added comprehensive
StrictModeTestthat verifies no violations occur during SDK initialization - Test captures StrictMode output and validates no disk read violations are triggered
- All existing tests continue to pass, confirming backward compatibility
Impact
- Eliminates StrictMode violations for SDK users who enforce strict policies
- Maintains full backward compatibility - no API changes
- Performance improvement: initialization no longer blocks on disk I/O operations
- Thread-safe implementation using proper synchronization mechanisms
GitHub Copilot Summary
This pull request addresses StrictMode disk I/O violations by ensuring that Mixpanel's initialization and first-launch checks avoid disk reads on the main thread. The changes introduce lazy initialization for database file access, move first-launch tracking to a background thread, and add instrumentation tests to verify compliance with StrictMode policies.
StrictMode compliance and testing:
- Added a new instrumentation test
StrictModeTestto verify that initializingMixpanelAPIon the main thread does not trigger StrictMode disk read violations, and to ensure SDK initialization completes successfully.
Database file access improvements:
- Refactored
MPDbAdapter.MPDatabaseHelperto lazily initialize themDatabaseFilefield using a newgetDatabaseFile()method, deferring disk I/O until actually needed. Updated all usages to call this method instead of directly accessing the field. [1] [2] [3] [4]
First-launch logic refactor:
- Moved the first-launch check and automatic event tracking (
$app_openandFIRST_OPEN) into a new asynchronous methodcheckFirstLaunchAsync(), which runs on a background thread to avoid main-thread disk I/O. The synchronous check was removed from the constructor. [1] [2]
Addressed GitHub Copilot Feedback
Refactored the first launch check to use the established message passing pattern instead of creating a new Thread:
Changes made:
- Added
CHECK_FIRST_LAUNCHmessage type toAnalyticsMessagesfor handling first launch checks - Created
FirstLaunchDescriptionclass to carry the message data including the MixpanelAPI instance reference - Implemented handler in Worker to process first launch checks on the existing background HandlerThread
- Added package-private methods (
isFirstLaunch()andsetHasLaunched()) to MixpanelAPI to allow AnalyticsMessages to perform the operations - Updated
checkFirstLaunchAsync()to usemMessages.checkFirstLaunchMessage()instead of creating a new Thread
This approach maintains the SDK's single HandlerThread architecture for all background operations, ensuring consistency and avoiding the overhead of creating additional threads.
The StrictMode tests continue to pass, confirming that the disk I/O violations are still resolved while properly following the SDK's architectural patterns.