MediaBrowserService中onLoadChildren方法如何获取媒体文件?
Hey there! Let's figure out how to properly load local music from MediaStore within your MediaBrowserService's onLoadChildren method—this is exactly where you should handle this logic, but we need to do it right to avoid blocking the main thread.
Key Background First
The onLoadChildren method is called whenever a connected MediaBrowser requests the list of media items under a specific parentMediaId (like the root of your media library, or an album/artist category). Since querying MediaStore is a time-consuming operation, we can't do it directly on the main thread—we need to offload it to a background thread and keep the result pending until we're done.
Step-by-Step Implementation
Here's a complete, working example that follows best practices:
@Override public void onLoadChildren( @NonNull final String parentMediaId, @NonNull final Result<List<MediaBrowserCompat.MediaItem>> result) { // 1. Detach the result so we can process it off the main thread // This tells the system we'll send the result later result.detach(); // 2. Use a background thread to query MediaStore // Note: AsyncTask is deprecated, but it's simple for demonstration—use ExecutorService or Coroutines in production new AsyncTask<Void, Void, List<MediaBrowserCompat.MediaItem>>() { @Override protected List<MediaBrowserCompat.MediaItem> doInBackground(Void... voids) { List<MediaBrowserCompat.MediaItem> mediaItems = new ArrayList<>(); // Define which media fields we want to retrieve String[] projection = { MediaStore.Audio.Media._ID, MediaStore.Audio.Media.TITLE, MediaStore.Audio.Media.ARTIST, MediaStore.Audio.Media.ALBUM }; // Optional: Filter out empty titles to avoid junk entries String selection = MediaStore.Audio.Media.TITLE + " != ?"; String[] selectionArgs = {""}; // Query the external storage for audio files Cursor cursor = getContentResolver().query( MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, projection, selection, selectionArgs, MediaStore.Audio.Media.TITLE + " ASC" // Sort alphabetically by title ); if (cursor != null) { while (cursor.moveToNext()) { // Extract data from the cursor long mediaId = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media._ID)); String title = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.TITLE)); String artist = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ARTIST)); String album = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ALBUM)); // Build a MediaDescription for the item MediaDescriptionCompat description = new MediaDescriptionCompat.Builder() .setMediaId(String.valueOf(mediaId)) .setTitle(title) .setSubtitle(artist) .setDescription(album) .build(); // Create the MediaItem—mark as playable since these are music tracks MediaBrowserCompat.MediaItem mediaItem = new MediaBrowserCompat.MediaItem( description, MediaBrowserCompat.MediaItem.FLAG_PLAYABLE ); mediaItems.add(mediaItem); } cursor.close(); // Always close the cursor to avoid leaks! } return mediaItems; } @Override protected void onPostExecute(List<MediaBrowserCompat.MediaItem> mediaItems) { // 3. Send the result back on the main thread result.sendResult(mediaItems); } }.execute(); }
Critical Notes for Production
- Permissions: Don't forget to request the necessary storage permissions:
- For Android 10 (API 29) and below:
READ_EXTERNAL_STORAGE - For Android 11+ (API 30+):
READ_MEDIA_AUDIO
You'll need to handle runtime permission requests before querying MediaStore, otherwise your cursor will return empty.
- For Android 10 (API 29) and below:
- Replace AsyncTask: Since AsyncTask is deprecated, use
ExecutorServiceor Kotlin Coroutines for background work. For example, with Executors:Executors.newSingleThreadExecutor().execute(() -> { // Do MediaStore query here List<MediaBrowserCompat.MediaItem> items = ...; new Handler(Looper.getMainLooper()).post(() -> result.sendResult(items)); }); - Category-Based Loading: If you want to organize music by albums or artists, use the
parentMediaIdto decide what to load. For example:- If
parentMediaIdis"root", load all albums - If
parentMediaIdis an album ID, load all tracks in that album (adjust your MediaStore query with a selection filter for the album ID)
- If
- Handle Media Updates: When local music files are added/removed, call
notifyChildrenChanged(parentMediaId)to tell connected MediaBrowsers to refresh their list. You can use aContentObserverto listen for changes in MediaStore.
内容的提问来源于stack exchange,提问作者Yeshia

