How SmartTube Handles OutOfMemoryError Crashes: A Two-Layer Recovery System

SmartTube prevents app terminations from OutOfMemoryError exceptions by implementing both global and playback-level interceptors that automatically downgrade memory-intensive components, switching from OkHttp data sources to default implementations and reducing video buffer sizes when RAM pressure is detected.

SmartTube's architecture specifically addresses memory constraints common to Android TV devices through a sophisticated error-handling strategy. Rather than allowing the application to crash when memory is exhausted, the codebase implements targeted recovery mechanisms in MainApplication and ErrorFixerController. These components work together to reduce memory footprint dynamically while preserving the current playback session when possible.

Global Crash Filter: Preventive Memory Management

The first line of defense operates at the application level through MainApplication.applyCrashFixes(). Located at lines 88-100 in smarttubetv/src/main/java/com/liskovsoft/smartyoutubetv2/tv/ui/main/MainApplication.java, this method functions as an uncaught-exception handler that monitors for OutOfMemoryError conditions.

When an OOM is detected, the system immediately executes two critical mitigations:

  • Data Source Switching: Changes the player data source from the memory-intensive OkHttp implementation to the lighter default option (PlayerTweaksData.PLAYER_DATA_SOURCE_DEFAULT)
  • Buffer Downgrade: Reduces the video buffer size from BUFFER_HIGH or BUFFER_HIGHEST to BUFFER_MEDIUM, significantly decreasing RAM allocation for video caching

This preventive approach ensures that future playback operations consume less memory, reducing the likelihood of recurring OOM errors during the same application session.

Playback-Engine Error Fixer: Reactive Recovery

The second layer operates within the ExoPlayer error-handling flow through ErrorFixerController.applyEngineErrorAction() at lines 48-57 in common/src/main/java/com/liskovsoft/smartyoutubetv2/common/app/models/playback/controllers/ErrorFixerController.java. This controller catches OOM exceptions that bubble up from the renderer during active playback.

When memory pressure surfaces mid-stream, the controller implements finer-grained recovery options:

  1. Force Lighter Data Source: Compels an immediate switch to faster, less memory-intensive data sources
  2. Lower Buffer Levels: Adjusts runtime buffer configurations to free allocated memory
  3. Disable Section Playlist: Stops heavy loading operations that consume additional RAM

The method then determines whether to perform a soft reload (reloadVideo()) or hard restart (mVideoLoaderController.restartEngine()) based on the severity of the memory condition and the selected mitigation strategy.

Configuration Classes Supporting OOM Recovery

SmartTube's recovery mechanisms rely on specific data models to persist and apply memory optimizations:

  • PlayerTweaksData: Stores mutable player configuration including data source types and buffer preferences used by both recovery layers
  • PlayerData: Manages runtime playback parameters such as BUFFER_HIGH, BUFFER_MEDIUM, and BUFFER_LOW constants that define memory allocation limits

These classes enable the application to maintain persistent preferences across recovery cycles, ensuring that once an OOM triggers a downgrade, the system remembers the lighter configuration for subsequent operations.

Practical Code Examples

Triggering OOM Recovery Manually

Developers can test the recovery mechanisms by forcing an OutOfMemoryError and routing it through SmartTube's error handlers:

// Force an OutOfMemoryError to verify recovery behavior
try {
    // Allocate a huge byte array to provoke OOM
    byte[] huge = new byte[Integer.MAX_VALUE];
} catch (OutOfMemoryError e) {
    // Apply global crash fixes
    MainApplication app = (MainApplication) getApplicationContext();
    app.applyCrashFixes(e);
    
    // Apply playback-level fixes if player is active
    ErrorFixerController ctrl = ErrorFixerController.instance(app);
    ctrl.applyEngineErrorAction(
        PlayerEventListener.ERROR_TYPE_UNEXPECTED,
        PlayerEventListener.RENDERER_INDEX_UNKNOWN,
        e
    );
}

Executing this code triggers the automatic mitigation sequence, switching the data source from OkHttp to default and reducing buffer sizes from BUFFER_HIGH to BUFFER_MEDIUM.

Proactive Memory Configuration

To prevent initial OOM occurrences on low-memory devices, configure the fallback settings before starting playback:

// Configure lighter data source preemptively
PlayerTweaksData tweaks = PlayerTweaksData.instance(context);
tweaks.setPlayerDataSource(PlayerTweaksData.PLAYER_DATA_SOURCE_DEFAULT);
tweaks.persistNow();

This configuration eliminates the need for runtime recovery by selecting memory-efficient defaults from application startup.

Summary

  • Two-layer defense: SmartTube combines global exception handling in MainApplication with playback-specific recovery in ErrorFixerController to intercept OOM errors at different architectural levels according to the SmartTube source code.
  • Automatic resource downgrading: The system switches from OkHttp to default data sources and reduces buffers from BUFFER_HIGH/BUFFER_HIGHEST to BUFFER_MEDIUM when memory pressure is detected.
  • Granular recovery options: The playback controller chooses between engine restart and video reload based on error severity, minimizing user disruption.
  • Persistent configuration: Changes made during OOM recovery are stored in PlayerTweaksData, ensuring subsequent playback operations use memory-efficient settings.

Frequently Asked Questions

What triggers OutOfMemoryError in SmartTube?

OutOfMemoryError exceptions typically occur when SmartTube loads high-resolution video streams using the OkHttp data source while maintaining large buffer sizes (BUFFER_HIGH or BUFFER_HIGHEST). Android TV devices with limited RAM cannot simultaneously handle the memory overhead of OkHttp connections, extensive video caching, and system operations, causing the virtual machine to exhaust available heap space during playback or buffering operations.

How does SmartTube recover without crashing the entire app?

SmartTube implements a global uncaught-exception handler in MainApplication.applyCrashFixes() that intercepts OOM exceptions before they terminate the process. By catching the error at the application level, the code switches to lighter data sources and reduces buffer allocations, then allows execution to continue. If the error occurs during active playback, ErrorFixerController.applyEngineErrorAction() performs targeted recovery by restarting only the ExoPlayer engine rather than the entire application process.

Can users configure settings to prevent OOM errors?

Yes, users can proactively configure memory-efficient settings through PlayerTweaksData by selecting PLAYER_DATA_SOURCE_DEFAULT instead of the OkHttp implementation and choosing smaller buffer sizes like BUFFER_MEDIUM or BUFFER_LOW. These configurations reduce the baseline memory footprint, preventing the conditions that typically trigger OutOfMemoryError exceptions on constrained Android TV hardware.

What is the difference between restartEngine and reloadVideo?

restartEngine() performs a complete restart of the ExoPlayer instance through mVideoLoaderController, clearing all renderer states and reinitializing the playback pipeline, which is necessary when severe memory corruption occurs. reloadVideo() performs a softer recovery by reloading the current media item without destroying the player instance, preserving more playback state and providing faster recovery when the memory issue can be resolved through simple buffer adjustments alone.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →