Architecture Overview
Torream follows modern Android architecture patterns and best practices to create a scalable, maintainable, and testable application. This document provides a high-level overview of the application's architecture.
Architecture Pattern
MVVM (Model-View-ViewModel)
Torream implements the MVVM (Model-View-ViewModel) architecture pattern, which provides clear separation of concerns and improves testability.
┌─────────────────────────────────────────────────────┐
│ UI Layer │
│ (Activities, Fragments, Views, Adapters) │
│ - Observes ViewModel │
│ - Displays data │
│ - Handles user interactions │
└────────────────┬────────────────────────────────────┘
│
↓
┌─────────────────────────────────────────────────────┐
│ ViewModel Layer │
│ (ViewModels with LiveData/StateFlow) │
│ - UI state management │
│ - Business logic coordination │
│ - Lifecycle awareness │
└────────────────┬────────────────────────────────────┘
│
↓
┌─────────────────────────────────────────────────────┐
│ Repository Layer │
│ (Single Source of Truth) │
│ - Coordinates data sources │
│ - Caching strategy │
│ - Data transformation │
└─────────┬────────────────────────┬──────────────────┘
│ │
↓ ↓
┌──────────────────┐ ┌──────────────────────┐
│ Local Data │ │ Remote Data │
│ (Room Database) │ │ (Network/Downloads) │
│ - Cached data │ │ - API calls │
│ - User data │ │ - File downloads │
│ - Settings │ │ - Torrent data │
└──────────────────┘ └──────────────────────┘
Layer Breakdown
1. UI Layer (ui/ package)
The UI layer contains all user-facing components:
Components:
- Activities: Main container activities
- Fragments: Individual screens
HomeFragment: Main feed/browseLibraryFragment: Media library managementDownloadFragment: Download managementSettingsFragment: App preferencesPlayerFragment: Video playback
- Adapters: RecyclerView adapters for lists
- ViewHolders: Item view rendering
- Custom Views: Reusable UI components
Responsibilities:
- Display data from ViewModels
- Handle user input
- Navigation between screens
- UI state rendering
- Animation and transitions
2. ViewModel Layer
ViewModels manage UI state and coordinate with repositories:
Key ViewModels:
FeedViewModel: Manages home feed dataLibraryViewModel: Media library stateDownloadViewModel: Download operations statePlayerViewModel: Playback state and controlsSettingsViewModel: User preferences
Characteristics:
- Survive configuration changes
- Lifecycle-aware
- No Android framework dependencies
- Expose data via LiveData/StateFlow
- Handle business logic
3. Repository Layer (media/repository/, download/)
Repositories act as the single source of truth:
Main Repositories:
MediaRepository: Media library dataDownloadRepository: Download managementFeedRepository: Content feedsFavoritesRepository: User favoritesSettingsRepository: App settings
Responsibilities:
- Coordinate between data sources
- Implement caching strategies
- Data transformation
- Handle data conflicts
- Error handling
4. Data Layer
Local Data (media/db/, datastore/)
Room Database:
AppDatabase: Main database instance- DAOs (Data Access Objects):
MediaDao: Media items CRUDPlaylistDao: Playlist managementDownloadDao: Download trackingFeedDao: Feed data caching
- Entities:
MediaItem: Video/audio metadataPlaylist: Playlist informationDownloadTask: Download stateFeedItem: Feed content
DataStore:
- User preferences
- App settings
- Account information
Remote Data (network/, download/)
Network Operations:
- HTTP requests via NiceHttp
- Content scraping
- Metadata fetching
- Thumbnail downloads
Download System:
- HTTP downloads
- HLS segmented downloads
- Torrent downloads
- Stream management
Dependency Injection
Hilt/Dagger Architecture
Torream uses Hilt for dependency injection:
@HiltAndroidApp
Application
│
├─► @AndroidEntryPoint
│ Activities/Fragments
│ │
│ └─► @HiltViewModel
│ ViewModels
│ │
│ └─► @Inject
│ Repositories
│ │
│ └─► @Inject
│ Data Sources
│
└─► @HiltWorker
Background Workers
DI Modules:
DatabaseModule: Room database instanceNetworkModule: HTTP clientsRepositoryModule: Repository bindingsDownloadModule: Download system componentsPlayerModule: Media player componentsAdModule: Ad providers
Benefits:
- Compile-time dependency validation
- Automatic lifecycle management
- Easy testing with mock dependencies
- Reduced boilerplate
Package Structure
cloud.streamless.torream/
├── ads/ # Ad integration
│ ├── AdManager
│ ├── AdPreloadManager
│ ├── AdWaterfallManager
│ └── providers/ # Ad network providers
├── datastore/ # Local preferences
│ ├── account/
│ └── app/
├── download/ # Download system
│ ├── http/ # HTTP downloads
│ ├── hls/ # HLS downloads
│ ├── torrent/ # Torrent downloads
│ └── worker/ # Background workers
├── media/ # Media database
│ ├── dao/ # Room DAOs
│ ├── db/ # Database setup
│ ├── entities/ # Database entities
│ ├── repository/ # Data repositories
│ └── di/ # DI modules
├── model/ # Data models
├── network/ # Networking utilities
├── ui/ # UI components
│ ├── adapter/ # RecyclerView adapters
│ ├── browse/ # Browse screens
│ ├── dialog/ # Dialog fragments
│ ├── download/ # Download UI
│ ├── feed/ # Feed/home UI
│ ├── home/ # Home screen
│ ├── library/ # Library UI
│ ├── player/ # Player UI
│ ├── settings/ # Settings UI
│ └── widget/ # Custom widgets
└── utils/ # Utility classes
Key Architectural Decisions
1. Single Activity Architecture
Torream primarily uses a single-activity architecture with the Navigation component:
Benefits:
- Simplified navigation
- Shared ViewModels
- Consistent transitions
- Reduced complexity
2. Reactive Programming
Uses Kotlin Flow and LiveData for reactive data streams:
Advantages:
- Automatic UI updates
- Lifecycle awareness
- Backpressure handling
- Composition of async operations
3. Repository Pattern
Centralized data access through repositories:
Why?
- Single source of truth
- Easier testing
- Centralized caching logic
- Clean API for ViewModels
4. Paging 3 Integration
For efficient large dataset handling:
Implementation:
PagingSourcefor data loadingPagingDatafor UI updates- Automatic loading states
- Memory efficiency
5. WorkManager for Downloads
Background downloads use WorkManager:
Reasons:
- Guaranteed execution
- System optimizations
- Constraint-based scheduling
- Easy cancellation and retry
Data Flow
Typical Data Flow Example
Scenario: User opens the library screen
1. LibraryFragment created
│
↓
2. LibraryViewModel injected via Hilt
│
↓
3. ViewModel requests data from MediaRepository
│
↓
4. Repository checks Room database
│
├─► If data exists: Return from cache
│ │
│ └─► Transform to UI models
│
└─► If stale/missing: Fetch from network
│
├─► Fetch data
├─► Store in Room
└─► Return to ViewModel
│
↓
5. ViewModel exposes data via LiveData
│
↓
6. Fragment observes and updates UI
State Management
ViewModel State
Each ViewModel manages its screen state:
data class LibraryUiState(
val media: List<MediaItem> = emptyList(),
val isLoading: Boolean = false,
val error: String? = null,
val selectedTab: LibraryTab = LibraryTab.ALL
)
State Updates:
- Immutable state objects
- Single source of truth
- Reactive updates via StateFlow/LiveData
Download State
Download system maintains separate state:
sealed class DownloadStatus {
object Queued : DownloadStatus()
data class Downloading(val progress: Int) : DownloadStatus()
data class Paused(val progress: Int) : DownloadStatus()
object Completed : DownloadStatus()
data class Failed(val error: String) : DownloadStatus()
}
Threading Model
Coroutines & Dispatchers
// Main dispatcher: UI updates
viewModelScope.launch(Dispatchers.Main) {
_uiState.value = Loading
}
// IO dispatcher: Network, database
withContext(Dispatchers.IO) {
database.mediaDao().getAll()
}
// Default dispatcher: CPU-intensive work
withContext(Dispatchers.Default) {
processLargeData()
}
Threading Rules:
- UI updates on Main dispatcher
- Database operations on IO dispatcher
- Network calls on IO dispatcher
- Heavy computations on Default dispatcher
Error Handling
Layered Error Handling
- Data Layer: Catch and wrap exceptions
- Repository Layer: Transform to domain errors
- ViewModel Layer: Convert to UI states
- UI Layer: Display user-friendly messages
sealed class Result<out T> {
data class Success<T>(val data: T) : Result<T>()
data class Error(val exception: Exception) : Result<Nothing>()
object Loading : Result<Nothing>()
}
Testing Strategy
Unit Tests
- ViewModels: Business logic
- Repositories: Data coordination
- Utilities: Helper functions
Integration Tests
- Room DAOs: Database operations
- Repositories: Full data flow
UI Tests
- Espresso: User interactions
- Fragment scenarios
- Navigation testing
Performance Considerations
Optimizations
- Lazy Loading: Paging 3 for large lists
- Image Caching: Coil with memory/disk cache
- Database Indexing: Optimized queries
- WorkManager: Battery-efficient background work
- ViewBinding: Fast view access
- Kotlin Coroutines: Efficient async operations
Memory Management
- Proper lifecycle handling
- Bitmap recycling
- Clearing resources in onDestroy
- Avoiding memory leaks with ViewModels
Scalability
The architecture supports:
- Adding new features as modules
- Swapping implementations via DI
- Independent testing of components
- Multiple UI variants (phone/tablet/TV)
Conclusion
Torream's architecture provides:
- ✅ Clear separation of concerns
- ✅ Easy testing and maintenance
- ✅ Scalable code organization
- ✅ Efficient resource usage
- ✅ Modern Android best practices
The combination of MVVM, Repository pattern, and Hilt DI creates a robust foundation for a complex media application.