Android音乐播放器如何读取内部存储歌曲?权限适配方案
问题
我为App申请了READ_EXTERNAL_STORAGE权限,使其可读取SD卡中的歌曲。在模拟器上,由于存在SD卡存储,歌曲能够正常显示,但在物理设备上,因为歌曲存储在内部存储而非SD卡中,所以无法显示。请问如何获取内部存储中的歌曲?需要使用什么权限?
权限说明
- 读取内部存储中的媒体文件,
READ_EXTERNAL_STORAGE权限已经足够(Android 12及以下);Android 13及以上建议使用更细分的READ_MEDIA_AUDIO权限,同时保留旧权限做向下兼容。 - 若App目标SDK为Android 10(API 29)及以上,Manifest中已添加的
android:requestLegacyExternalStorage="true"可暂时绕过Scoped Storage限制,继续使用文件遍历方式。
快速修复:遍历公共音乐目录
你当前仅遍历了外部SD卡根目录,物理设备的内部存储媒体文件通常存放在公共音乐目录,修改displaySong方法,同时覆盖公共音乐目录和App私有存储目录:
修改MainActivity中的displaySong方法
public void displaySong() { ArrayList<File> mySongs = new ArrayList<>(); // 遍历系统公共音乐目录(包含内部存储+SD卡的音乐文件夹) File publicMusicDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_MUSIC); if (publicMusicDir.exists()) { mySongs.addAll(findSong(publicMusicDir)); } // 遍历App私有外部存储的音乐目录(可选,若有App自身保存的歌曲) File appPrivateMusicDir = getExternalFilesDir(Environment.DIRECTORY_MUSIC); if (appPrivateMusicDir != null && appPrivateMusicDir.exists()) { mySongs.addAll(findSong(appPrivateMusicDir)); } ArrayList<String> items = new ArrayList<>(); for (int i = 0; i < mySongs.size(); i++) { items.add(mySongs.get(i).getName().replace(".mp3", "").replace(".wav", "")); } ArrayAdapter<String> songAdapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, items); listView.setAdapter(songAdapter); listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) { Intent intent = new Intent(MainActivity.this, PlaySong.class); intent.putExtra("position", i); intent.putExtra("songlist", mySongs); intent.putExtra("songname", items.get(i)); startActivity(intent); } }); }
更稳定方案:使用MediaStore API(推荐)
直接遍历文件的方式在Android 10+会受Scoped Storage限制,改用MediaStore可可靠获取所有系统索引的音频文件,无需手动遍历目录:
修改MainActivity
package com.example.vocals; import androidx.appcompat.app.AppCompatActivity; import android.content.Intent; import android.database.Cursor; import android.os.Bundle; import android.provider.MediaStore; import android.util.Log; import android.view.View; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.ListView; import com.karumi.dexter.Dexter; import com.karumi.dexter.PermissionToken; import com.karumi.dexter.listener.PermissionGrantedResponse; import com.karumi.dexter.listener.PermissionRequest; import com.karumi.dexter.listener.single.PermissionListener; import java.util.ArrayList; import static android.Manifest.permission.READ_EXTERNAL_STORAGE; import static android.Manifest.permission.READ_MEDIA_AUDIO; public class MainActivity extends AppCompatActivity { ListView listView; ArrayList<String> songNames = new ArrayList<>(); ArrayList<String> songPaths = new ArrayList<>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); listView = findViewById(R.id.songs); // 动态申请权限,适配Android 13+ String permission = android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.TIRAMISU ? READ_MEDIA_AUDIO : READ_EXTERNAL_STORAGE; Dexter.withContext(this) .withPermission(permission) .withListener(new PermissionListener() { @Override public void onPermissionGranted(PermissionGrantedResponse permissionGrantedResponse) { loadSongsFromMediaStore(); } @Override public void onPermissionDenied(com.karumi.dexter.listener.PermissionDeniedResponse permissionDeniedResponse) {} @Override public void onPermissionRationaleShouldBeShown(PermissionRequest permissionRequest, PermissionToken permissionToken) { permissionToken.continuePermissionRequest(); } }).check(); } private void loadSongsFromMediaStore() { String[] projection = { MediaStore.Audio.Media.DISPLAY_NAME, MediaStore.Audio.Media.DATA }; String selection = MediaStore.Audio.Media.IS_MUSIC + " != 0"; String sortOrder = MediaStore.Audio.Media.TITLE + " ASC"; Cursor cursor = getContentResolver().query( MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, projection, selection, null, sortOrder ); if (cursor != null) { while (cursor.moveToNext()) { String songName = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DISPLAY_NAME)); String songPath = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DATA)); // 过滤mp3和wav文件 if (songName.endsWith(".mp3") || songName.endsWith(".wav")) { songNames.add(songName.replace(".mp3", "").replace(".wav", "")); songPaths.add(songPath); } } cursor.close(); } else { Log.d("info", "未找到可播放的音频文件"); } ArrayAdapter<String> songAdapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, songNames); listView.setAdapter(songAdapter); listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) { Intent intent = new Intent(MainActivity.this, PlaySong.class); intent.putExtra("position", i); intent.putExtra("songPaths", songPaths); intent.putExtra("songname", songNames.get(i)); startActivity(intent); } }); } }
修改PlaySong Activity
package com.example.vocals; import androidx.appcompat.app.AppCompatActivity; import android.content.Intent; import android.media.MediaPlayer; import android.net.Uri; import android.os.Bundle; import android.view.View; import android.widget.ImageView; import android.widget.SeekBar; import android.widget.TextView; import java.util.ArrayList; public class PlaySong extends AppCompatActivity { @Override protected void onDestroy() { super.onDestroy(); if (mp != null) { mp.stop(); mp.release(); } if (updateSeek != null) { updateSeek.interrupt(); } } TextView txtsong; ImageView next, previous, pause; MediaPlayer mp; Thread updateSeek; int current_position = 0; int position; ArrayList<String> songPaths; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_play_song); txtsong = findViewById(R.id.txtsong); next = findViewById(R.id.next); previous = findViewById(R.id.previous); pause = findViewById(R.id.pause); SeekBar seekBar = findViewById(R.id.seekBar); Intent intent = getIntent(); songPaths = intent.getStringArrayListExtra("songPaths"); String txtcontent = intent.getStringExtra("songname"); txtsong.setText(txtcontent); txtsong.setSelected(true); position = intent.getIntExtra("position", 0); playSong(position); seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int i, boolean b) {} @Override public void onStartTrackingTouch(SeekBar seekBar) {} @Override public void onStopTrackingTouch(SeekBar seekBar) { if (mp != null && mp.isPlaying()) { mp.seekTo(seekBar.getProgress()); } } }); updateSeek = new Thread() { @Override public void run() { super.run(); try { while (mp != null && current_position < mp.getDuration()) { if (mp.isPlaying()) { current_position = mp.getCurrentPosition(); seekBar.setProgress(current_position); } sleep(800); } } catch (Exception e) { e.printStackTrace(); } } }; updateSeek.start(); previous.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if (mp != null) { mp.stop(); mp.release(); } position = (position == 0) ? songPaths.size() - 1 : position - 1; playSong(position); pause.setImageResource(R.drawable.pause); } }); pause.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if (mp != null) { if (mp.isPlaying()) { mp.pause(); pause.setImageResource(R.drawable.play); } else { mp.start(); pause.setImageResource(R.drawable.pause); } } } }); next.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { if (mp != null) { mp.stop(); mp.release(); } position = (position == songPaths.size() - 1) ? 0 : position + 1; playSong(position); pause.setImageResource(R.drawable.pause); } }); } private void playSong(int pos) { String songPath = songPaths.get(pos); Uri uri = Uri.parse("file://" + songPath); mp = MediaPlayer.create(this, uri); mp.start(); txtsong.setText(songPaths.get(pos).substring(songPaths.get(pos).lastIndexOf("/") + 1).replace(".mp3", "").replace(".wav", "")); SeekBar seekBar = findViewById(R.id.seekBar); seekBar.setMax(mp.getDuration()); current_position = 0; } }
修改Manifest权限
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.vocals"> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32"/> <uses-permission android:name="android.permission.READ_MEDIA_AUDIO" /> <application android:requestLegacyExternalStorage="true" android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/Theme.Vocals"> <activity android:name=".PlaySong" android:label="Player" android:parentActivityName=".MainActivity"></activity> <activity android:name=".MainActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
内容的提问来源于stack exchange,提问作者thakur gupta
相关产品推荐
相关产品推荐

