Environment.getExternalStorageDirectory()废弃后MP3播放器扫描音频方案咨询
MP3播放器音频文件读取修复方案
以下提供两种可直接落地的方案,适配所有安卓版本,你可以根据需求选择:
方案1:应用私有存储方案(无需额外权限,满足你说的内部存储需求)
该方案读取的是APP专属的存储目录,不需要申请任何存储权限,卸载APP时文件会同步删除,适合不需要读取系统公共音频的场景。
修改步骤:
- 可直接删除原有Dexter权限申请逻辑(私有目录访问不需要权限)
- 将原有代码中获取根目录的代码:
ArrayList<File> mySongs = fetchSongs(Environment.getExternalStorageDirectory());
替换为:
// 用外部私有存储的音乐目录,也可以换成getFilesDir()用纯内部存储 ArrayList<File> mySongs = fetchSongs(getExternalFilesDir(Environment.DIRECTORY_MUSIC));
- 测试时将你的MP3文件放入设备的
Android/data/com.example.musicplayer/files/Music目录下即可被扫描到。
方案2:扫描系统所有公共MP3文件(兼容全安卓版本)
如果需要读取用户设备里所有存储的音频文件,用官方推荐的MediaStore媒体库方案,比手动遍历文件夹效率高10倍以上,且兼容安卓4.4到最新版本。
修改步骤:
- 先修改
AndroidManifest.xml配置:
在<application>节点添加属性:
android:requestLegacyExternalStorage="true"
在<manifest>节点下添加权限配置:
<!-- 安卓12及以下权限 --> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" /> <!-- 安卓13及以上权限 --> <uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
- 替换MainActivity的逻辑:
将原有权限申请+扫描逻辑替换为以下代码,原有的fetchSongs方法可以直接删除:
package com.example.musicplayer; import androidx.appcompat.app.AppCompatActivity; import android.Manifest; import android.database.Cursor; import android.os.Build; import android.os.Bundle; import android.provider.MediaStore; import android.widget.ArrayAdapter; import android.widget.ListView; import android.widget.Toast; import com.karumi.dexter.Dexter; import com.karumi.dexter.PermissionToken; import com.karumi.dexter.listener.PermissionDeniedResponse; import com.karumi.dexter.listener.PermissionGrantedResponse; import com.karumi.dexter.listener.PermissionRequest; import com.karumi.dexter.listener.single.PermissionListener; import java.util.ArrayList; public class MainActivity extends AppCompatActivity { ListView listview; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); listview = findViewById(R.id.listview); // 动态判断安卓版本申请对应权限 String permission = Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU ? Manifest.permission.READ_MEDIA_AUDIO : Manifest.permission.READ_EXTERNAL_STORAGE; Dexter.withContext(this) .withPermission(permission) .withListener(new PermissionListener() { @Override public void onPermissionGranted(PermissionGrantedResponse permissionGrantedResponse) { Toast.makeText(MainActivity.this, "权限获取成功", Toast.LENGTH_SHORT).show(); ArrayList<String> songNames = fetchAllMp3FromMediaStore(); ArrayAdapter<String> adapter = new ArrayAdapter<>(MainActivity.this, android.R.layout.simple_list_item_1, songNames); listview.setAdapter(adapter); } @Override public void onPermissionDenied(PermissionDeniedResponse permissionDeniedResponse) { Toast.makeText(MainActivity.this, "请授予存储权限才能扫描音频", Toast.LENGTH_SHORT).show(); } @Override public void onPermissionRationaleShouldBeShown(PermissionRequest permissionRequest, PermissionToken permissionToken) { permissionToken.continuePermissionRequest(); } }) .check(); } // 从系统媒体库查询所有MP3文件 private ArrayList<String> fetchAllMp3FromMediaStore() { ArrayList<String> songList = new ArrayList<>(); String[] projection = {MediaStore.Audio.Media.DISPLAY_NAME}; String selection = MediaStore.Audio.Media.IS_MUSIC + " != 0 AND " + MediaStore.Audio.Media.DISPLAY_NAME + " LIKE '%.mp3'"; Cursor cursor = getContentResolver().query( MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, projection, selection, null, MediaStore.Audio.Media.DISPLAY_NAME + " ASC" ); if (cursor != null) { while (cursor.moveToNext()) { String name = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DISPLAY_NAME)); // 去掉.mp3后缀 songList.add(name.replace(".mp3", "")); } cursor.close(); } return songList; } }
注意事项
- 安卓13以上首次启动会弹出音频权限申请弹窗,用户授权后才能扫描所有音频
- 如果需要获取音频的完整路径、时长等信息,只需要在projection数组里添加对应的MediaStore字段即可。
内容的提问来源于stack exchange,提问作者Game Top
相关产品推荐
相关产品推荐

