Android音乐播放器:如何预检查本地专辑封面是否存在?
我明白你的痛点——从MediaStore拿到的专辑封面Uri永远不为空,但实际有的根本没有对应资源,而且逐个查Cursor又慢得离谱。这里有几个优化方案帮你解决这个问题:
解决方案
1. 批量缓存专辑封面信息(最推荐,效率最高)
每次单独查询单个专辑的封面会频繁操作Cursor,导致性能低下。我们可以一次性把所有本地专辑的封面路径缓存到HashMap里,之后遍历歌曲时直接从缓存读取,避免重复查询:
// 先在后台线程批量获取所有专辑的封面信息 private HashMap<Long, String> albumArtCache = new HashMap<>(); private void loadAlbumArtCache() { new Thread(() -> { String[] projection = { MediaStore.Audio.Albums._ID, MediaStore.Audio.Albums.ALBUM_ART }; Cursor albumCursor = getActivity().getContentResolver().query( MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI, projection, null, null, null ); if (albumCursor != null) { while (albumCursor.moveToNext()) { long albumId = albumCursor.getLong(albumCursor.getColumnIndex(MediaStore.Audio.Albums._ID)); String albumArtPath = albumCursor.getString(albumCursor.getColumnIndex(MediaStore.Audio.Albums.ALBUM_ART)); albumArtCache.put(albumId, albumArtPath); } albumCursor.close(); } }).start(); }
之后在遍历歌曲Cursor的时候,直接从缓存里取封面路径,判断是否存在:
// 遍历歌曲Cursor时的逻辑 Long albumId = cursor.getLong(cursor.getColumnIndex(MediaStore.Audio.Media.ALBUM_ID)); String localAlbumArtPath = albumArtCache.get(albumId); String finalArtUri; if (localAlbumArtPath != null && !localAlbumArtPath.isEmpty()) { // 本地存在封面,用MediaStore生成的Uri finalArtUri = String.valueOf(ContentUris.withAppendedId(Uri.parse("content://media/external/audio/albumart"), albumId)); } else { // 本地无封面,替换为你从网络获取的封面链接 finalArtUri = fetchNetworkAlbumArt(albumId); // 这里实现你的网络封面获取逻辑 } SongInfoModel s = new SongInfoModel(id, name, artist, null, album, null, duration, data, albumId, finalArtUri); SongList.add(s);
2. 直接检查Uri对应的资源是否存在
如果不想批量缓存,也可以通过尝试打开Uri的输入流来判断资源是否存在(必须在子线程执行,避免阻塞UI):
private boolean isAlbumArtExists(long albumId) { Uri albumArtUri = ContentUris.withAppendedId(Uri.parse("content://media/external/audio/albumart"), albumId); try (InputStream inputStream = getActivity().getContentResolver().openInputStream(albumArtUri)) { return inputStream != null; } catch (IOException e) { return false; } }
使用时,在后台线程中对每个歌曲的albumId调用这个方法,根据返回值决定使用本地Uri还是网络封面。
3. 结合Glide的加载监听(备用方案)
如果你不想提前检查,也可以利用Glide的listener在加载失败时自动替换为网络封面,适配你已经在使用占位图的场景:
Glide.with(context) .load(albumArtUri) .placeholder(R.drawable.default_art) .listener(new RequestListener<Drawable>() { @Override public boolean onLoadFailed(@Nullable GlideException e, Object model, Target<Drawable> target, boolean isFirstResource) { // 本地封面加载失败,替换为网络封面 Glide.with(context) .load(fetchNetworkAlbumArt(albumId)) .into(target); return true; } @Override public boolean onResourceReady(Drawable resource, Object model, Target<Drawable> target, DataSource dataSource, boolean isFirstResource) { return false; } }) .into(albumArtImageView);
不过这个方案是在加载时才处理,如果你需要提前把网络链接存入列表,还是前两种方案更合适。
内容的提问来源于stack exchange,提问作者Rektirino
相关产品推荐
相关产品推荐

