Build an Android music player with playlist management
An Android music player with playlist management needs two dependable layers: a playback engine that survives screen rotation and background use, and a playlist layer users can edit without losing their place. Media3’s ExoPlayer and MediaSessionService handle the first job, while Room provides a structured SQLite home for playlists, tracks and ordering.
Start with one screen that lists audio from MediaStore, plus a playlist screen where tracks can be added, removed and reordered. Connect both screens to a single playback service instead of starting ExoPlayer in each Activity. This keeps playback continuous when a playlist opens or the phone locks, and gives later features such as sleep timers and downloads a clear place to live.
Australian conditions shape several practical choices. A commuter catching a train in Sydney should be able to resume from a notification without hunting through menus, while listeners at a small Melbourne venue need clean handling of self-released tracks with sparse metadata. Regional listeners around Perth, Brisbane and Alice Springs benefit from dependable offline behaviour, and relaxed Australian wording such as “hang on” for loading and “no worries” for recoverable errors makes messages feel less abrupt.
Design the data and playback architecture
Model a track separately from a playlist. A Room Track entity can hold the MediaStore URI, title, artist, album, duration and artwork reference; Playlist stores identity and display details; PlaylistTrack is a many-to-many join with a position value. This allows one recording to appear in several playlists and preserves a deliberate queue order.
Give each playlist and join row a stable ID rather than using an Adapter position as identity. Use foreign keys with cascading deletion, and update a reorder operation in one Room transaction so a failed write cannot leave duplicate entries. Developers wanting another perspective on project structure can consult Android project guidance while defining those boundaries.
Keep UI state in ViewModels, persistence in DAOs, and playback in the service. The Activity observes a ViewModel and sends commands through a MediaController; it never owns the ExoPlayer directly. This separation makes rotation safe and allows a compact ListView-based tutorial to grow into a RecyclerView application without redesigning its data layer.
Connect the background playback service
Create a MediaSessionService that owns one ExoPlayer instance and publishes playback state, queue metadata and available actions to Android. The interface can obtain a MediaController with a SessionToken, then issue play, pause, skip, seek and queue commands. Use the same session for Bluetooth controls, wired-headset buttons and the media notification.
Request READ_MEDIA_AUDIO on Android 13 and later, and the legacy external-storage permission only where an older release needs it. Declare the foreground-service media-playback type and request notification permission on recent Android versions when appropriate. Let ExoPlayer handle audio focus, ducking and the pause that follows a headset disconnect instead of reimplementing those behaviours.
Map Room rows into Media3 MediaItem objects before sending them to the player. Save the current media ID, queue index and position when the service stops, then restore them on the next launch. SharedPreferences works well for the last volume or shuffle setting; a larger queue belongs in Room or the media session, not a preferences file.
Build playlist screens and editing tools
Use a track browser and a playlist detail screen. Each list needs loading, empty and error states, and every row should expose an accessible action label. Adding a track should update the database immediately, while removing one can offer an undo action. Users should also be able to create, rename and delete playlists without leaving the playback controls.
| Storage choice | Best for | Strength | Limitation |
|---|---|---|---|
| SharedPreferences | Small settings | Simple reads and writes | No relational queries or queue ordering |
| Room and SQLite | Playlists and saved queues | Transactions, joins and migrations | Requires entities and DAO maintenance |
| MediaStore | Existing device audio | Access to titles, artists and files | Limited control over catalogue changes |
Room is the practical choice for user-created collections because it handles relationships cleanly. MediaStore remains the source for recordings already on the phone, while preferences can remember interface choices such as repeat mode. This division prevents a renamed playlist from altering the underlying audio file.
Implement drag-to-reorder with ItemTouchHelper on RecyclerView, or provide explicit move-up and move-down controls in a ListView, and persist every new position. When a user presses play on a playlist, build the queue in that saved order, but offer shuffle as a playback option rather than rewriting the playlist. Support duplicate recordings where the same song genuinely appears twice, using a join-row ID to distinguish those entries.
Store metadata, preferences and downloads
Real-world music libraries are messy. An independent artist may export a song with no album artist, while a DJ set may place ten tracks under one album. Read MediaStore fields defensively, fall back to the filename, and keep manual corrections in a Room metadata-override entity so the source file remains untouched.
Cache artwork by URI and size to avoid decoding full-resolution images while scrolling. Sort tracks with locale-aware comparisons so accented names and Australian artist names appear naturally. Display dates as day/month/year, and use Australian English labels such as “Favourites” where they match the rest of the interface.
Treat downloaded audio separately from the indexed library. Use WorkManager for reliable transfers, show progress in plain language, and preserve enough space for the destination before starting. Many Australian prepaid plans include limited mobile data, so a clear “download over Wi-Fi” preference and resumable transfers are more useful than an always-on background downloader.
Test the experience and prepare for release
Test on physical Android devices as well as an emulator. Exercise headphone and Bluetooth changes, incoming calls, rapid track skipping, low storage, offline mode and process death. Automated DAO tests can verify duplicate entries, cascade deletion and reordered queues, while instrumentation tests should cover adding a track and confirming that the service receives the same order.
Check notification controls from the lock screen and confirm that playback continues when the Activity is destroyed. Use Android Studio’s background restrictions panel to reproduce the phone locking, and verify accessibility labels on small controls used during a Sydney commute. A clean restart must restore the track and position, not begin the queue again.
For Google Play’s Australian audience, describe the app accurately as a player for the user’s own local library rather than implying access to commercial streaming catalogues. With Spotify and Apple Music familiar alternatives and independent Australian releases widely available, smooth handling of incomplete metadata is a genuine advantage. Publish a clear privacy policy, minimise permissions and provide a straightforward way to erase playlists and cached audio before release.