You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android新手求助:将raw/asset文件夹MP3文件加载至RecyclerView

从raw/Assets文件夹读取MP3文件的Android实现方案

我是Android开发入门新手,之前做了一个读取设备本地音乐的应用,现在想改成读取raw或asset文件夹里的MP3文件,看了很多视频还是没解决,附上我的现有代码,求帮忙。


现有MainActivity代码

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    RecyclerView recyclerView = findViewById(R.id.recyclerview);

    if (checkPermission() == false){
        requesPermission();
        return;
    }

    String[] projection = {
            MediaStore.Audio.Media.TITLE,
            MediaStore.Audio.Media.DATA,
            MediaStore.Audio.Media.DURATION
    };
    String selection = MediaStore.Audio.Media.IS_MUSIC +" != 0";
    Cursor cursor = getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,projection,selection,null,null);
    while (cursor.moveToNext()){
        AudioModel songdata = new AudioModel(cursor.getString(1),cursor.getString(0),cursor.getString(2));
        if (new File(songdata.getPath()).exists())
            songlist.add(songdata);
    }

    recyclerView.setLayoutManager(new LinearLayoutManager(this));
    recyclerView.setAdapter(new MusicListAdapter(songlist,getApplicationContext()));

}

现有MusicListAdapter代码

public class MusicListAdapter extends RecyclerView.Adapter<MusicListAdapter.MyViewHolder> {

    ArrayList<AudioModel> songlist;
    Context context;

    public MusicListAdapter(ArrayList<AudioModel> songlist, Context context) {
        this.songlist = songlist;
        this.context = context;
    }

    @NonNull
    @Override
    public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(context).inflate(R.layout.item_view,parent,false);
        return new MusicListAdapter.MyViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
        AudioModel songData = songlist.get(position);
        holder.track_name.setText(songData.getTitle());

        if (MyMediaPlayer.currentIndex==position){
            holder.track_name.setTextColor(Color.parseColor("#FF0000"));
        }else {
            holder.track_name.setTextColor(Color.parseColor("#000000"));
        }

        holder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {

                MyMediaPlayer.getInstance().reset();
                MyMediaPlayer.currentIndex = position;
                Intent intent = new Intent(context,MusicPlayerActivity.class);
                intent.putExtra("LIST",songlist);
                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                context.startActivity(intent);
            }
        });


    }

    @Override
    public int getItemCount() {
        return songlist.size();
    }

    public class MyViewHolder extends RecyclerView.ViewHolder {

        ImageView track_icon;
        TextView track_name,track_artist;
        public MyViewHolder(@NonNull View itemView) {
            super(itemView);
            track_icon = itemView.findViewById(R.id.track_icon);
            track_name = itemView.findViewById(R.id.track_name);
            track_artist = itemView.findViewById(R.id.track_artist);
        }
    }

}

现有AudioModel代码

public class AudioModel implements Serializable {
    String path;
    String title;
    String duration;
    String icon;

    public AudioModel(String path, String title, String duration) {
        this.path = path;
        this.title = title;
        //this.duration = duration;

    }



    public String getPath() {
        return path;
    }

    public String getTitle() {
        return title;
    }

    public String getDuration() {
        return duration;
    }

    public void setPath(String path) {
        this.path = path;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public void setDuration(String duration) {
        this.duration = duration;
    }
}

解决方案

一、读取res/raw文件夹中的MP3文件

  1. 前置操作:将MP3文件放入res/raw文件夹,文件名只能用小写字母、数字和下划线,不能有空格或特殊字符。
  2. 修改MainActivity逻辑:替换原来的MediaStore查询代码,直接读取raw资源,无需权限申请。

修改后的MainActivity核心代码:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    RecyclerView recyclerView = findViewById(R.id.recyclerview);
    songlist = new ArrayList<>(); // 确保songlist已初始化

    // 读取raw文件夹中的音频文件
    Field[] fields = R.raw.class.getFields();
    for (Field field : fields) {
        try {
            int resId = field.getInt(null);
            String title = field.getName(); // 用文件名作为音频标题
            // 构建raw资源的Uri路径
            String path = "android.resource://" + getPackageName() + "/raw/" + title;
            
            // 获取音频时长(可选,需提前解析)
            MediaPlayer mp = MediaPlayer.create(this, resId);
            String duration = String.valueOf(mp.getDuration());
            mp.release();
            
            AudioModel songdata = new AudioModel(path, title, duration);
            songlist.add(songdata);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    recyclerView.setLayoutManager(new LinearLayoutManager(this));
    recyclerView.setAdapter(new MusicListAdapter(songlist, getApplicationContext()));
}

二、读取Assets文件夹中的MP3文件

  1. 前置操作:将MP3文件放入assets文件夹,可创建子文件夹(如assets/music),文件名无严格限制。
  2. 修改MainActivity逻辑:通过AssetManager获取文件列表。

修改后的MainActivity核心代码:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    RecyclerView recyclerView = findViewById(R.id.recyclerview);
    songlist = new ArrayList<>();

    // 读取assets文件夹中的音频文件(若放在子文件夹music下,list参数传"music",否则传"")
    AssetManager assetManager = getAssets();
    try {
        String[] files = assetManager.list("music");
        for (String fileName : files) {
            if (fileName.endsWith(".mp3")) {
                String title = fileName.substring(0, fileName.lastIndexOf("."));
                // 自定义路径标识,用于播放时区分
                String path = "asset:///" + fileName;
                
                // 获取音频时长(可选)
                AssetFileDescriptor afd = assetManager.openFd("music/" + fileName);
                MediaPlayer mp = new MediaPlayer();
                mp.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
                mp.prepare();
                String duration = String.valueOf(mp.getDuration());
                mp.release();
                afd.close();

                AudioModel songdata = new AudioModel(path, title, duration);
                songlist.add(songdata);
            }
        }
    } catch (IOException e) {
        e.printStackTrace();
    }

    recyclerView.setLayoutManager(new LinearLayoutManager(this));
    recyclerView.setAdapter(new MusicListAdapter(songlist, getApplicationContext()));
}

三、播放逻辑适配(修改MyMediaPlayer)

raw和assets资源的播放方式与本地文件不同,需修改播放逻辑:

public class MyMediaPlayer {
    private static MediaPlayer instance;
    public static int currentIndex = -1;

    public static MediaPlayer getInstance() {
        if (instance == null) {
            instance = new MediaPlayer();
        }
        return instance;
    }

    public void play(String path, Context context) {
        try {
            reset();
            if (path.startsWith("android.resource://")) {
                // 播放raw资源
                setDataSource(context, Uri.parse(path));
            } else if (path.startsWith("asset:///")) {
                // 播放assets资源
                String fileName = path.replace("asset:///", "");
                // 若放在子文件夹,需拼接路径,比如"music/" + fileName
                AssetFileDescriptor afd = context.getAssets().openFd(fileName);
                setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
                afd.close();
            } else {
                // 兼容原有本地文件播放逻辑
                setDataSource(path);
            }
            prepare();
            start();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

四、注意事项

  • 获取音频时长的操作建议放在子线程执行,避免阻塞主线程
  • 使用完MediaPlayer和AssetFileDescriptor后,务必调用release()和close()释放资源
  • raw资源会被编译进APK,无法动态修改;assets资源可保留原始文件结构,支持动态读取

内容的提问来源于stack exchange,提问作者Bra Him

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 12:19:53