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

从Firebase Storage获取音频时RecyclerView不显示问题排查求助

Firebase Storage音频文件无法在RecyclerView显示的问题排查与修复

问题描述

尝试从Firebase Storage获取音频文件并展示在RecyclerView中,但RecyclerView始终无内容显示。

核心问题分析

  1. 异步操作顺序错误:Firestore文档读取是异步操作,初始化StorageReference时albumName还未被赋值,导致Storage路径无效,无法获取到音频文件。
  2. Adapter初始化时机错误:在音频文件的下载URL还未全部获取时就初始化了Adapter,且后续的notifyDataSetChanged可能因上下文问题失效。
  3. ViewHolder控件未绑定:ViewHolder中的episodeName文本控件未通过itemView.findViewById完成绑定,既影响列表项显示,也会导致点击事件逻辑出错。

修复后的代码

1. 修复Activity逻辑(调整异步操作顺序)

uid = Objects.requireNonNull(FirebaseAuth.getInstance().getCurrentUser()).getUid();

DocumentReference documentReference = FirebaseFirestore.getInstance()
        .document("albums/" + uid + "/album_list/" + getIntent().getStringExtra("albumId"));

documentReference.get().addOnSuccessListener(documentSnapshot -> {

    albumName = (String) documentSnapshot.get("name");
    albumImageUrl = (String) documentSnapshot.get("albumImage");

    // 初始化Toolbar
    Toolbar toolbar = findViewById(R.id.toolbar);
    toolbar.setTitle(albumName);
    setSupportActionBar(toolbar);

    ActionBar actionBar = getSupportActionBar();
    if (actionBar != null) {
        actionBar.setDisplayHomeAsUpEnabled(true);
        actionBar.setHomeAsUpIndicator(R.drawable.back_action_button);
    }

    // 确保albumName赋值后再访问Storage
    StorageReference storageReference = FirebaseStorage.getInstance().getReference()
            .child("album/" + uid + "/" + albumName);

    storageReference.listAll().addOnSuccessListener(listResult -> {

        episodesList = new ArrayList<>();
        episodesAdapter = new EpisodesAdapter(this, episodesList, albumImageUrl, albumName);
        // 提前初始化RecyclerView
        albumEpisodeRecyclerView = findViewById(R.id.albumEpisodeRecyclerView);
        albumEpisodeRecyclerView.setHasFixedSize(true);
        albumEpisodeRecyclerView.setLayoutManager(new LinearLayoutManager(this));
        albumEpisodeRecyclerView.setAdapter(episodesAdapter);

        long index = 1L;
        int totalItems = listResult.getItems().size();
        int loadedCount = 0;

        for (StorageReference item : listResult.getItems()) {
            String episodeName = item.getName();
            long finalIndex = index;

            item.getDownloadUrl().addOnSuccessListener(uri -> {
                String episodeUrl = uri.toString();
                Episodes episode = new Episodes(finalIndex, episodeName, episodeUrl);
                episodesList.add(episode);
                loadedCount++;

                // 每次添加后通知Adapter更新
                episodesAdapter.notifyItemInserted(episodesList.size() - 1);

                // 全部加载完成后的逻辑(可选)
                if (loadedCount == totalItems) {
                    // 比如显示加载完成提示
                }
            }).addOnFailureListener(e -> {
                loadedCount++;
                Toast.makeText(this,"加载单个音频失败",Toast.LENGTH_SHORT).show();
            });

            index++;
        }

    }).addOnFailureListener(e -> {
        Toast.makeText(this,"加载音频列表失败",Toast.LENGTH_SHORT).show();
    });

}).addOnFailureListener(e -> {
    Toast.makeText(this,"获取专辑信息失败",Toast.LENGTH_SHORT).show();
});

2. 修复Adapter的ViewHolder控件绑定

public class EpisodesAdapter extends RecyclerView.Adapter<EpisodesAdapter.EpisodesViewHolder> {

    Context context;
    ArrayList<Episodes> episodesList;
    String albumUrl;
    static String albumName;

    public EpisodesAdapter(Context context, ArrayList<Episodes> episodesList, String albumUrl, String albumName) {
        this.context = context;
        this.episodesList = episodesList;
        this.albumUrl = albumUrl;
        EpisodesAdapter.albumName = albumName;
    }

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

    @Override
    public void onBindViewHolder(@NonNull EpisodesAdapter.EpisodesViewHolder holder, int position) {
        Episodes episodes = episodesList.get(position);
        holder.episodeNumber.setText(String.valueOf(episodes.getEpisodeNumber()));
        holder.episodeName.setText(episodes.getEpisodeName());

        Glide.with(context)
                .load(albumUrl)
                .centerCrop()
                .into(holder.episodeImageview);
    }

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

    public static class EpisodesViewHolder extends RecyclerView.ViewHolder{

        ImageView episodeImageview,playImageview;
        TextView episodeName,episodeNumber;
        RelativeLayout episode;

        public EpisodesViewHolder(@NonNull View itemView) {
            super(itemView);

            episode = itemView.findViewById(R.id.episode);
            episodeImageview = itemView.findViewById(R.id.episodeImageview);
            playImageview = itemView.findViewById(R.id.playImageview);
            // 修复:绑定episodeName和episodeNumber控件
            episodeName = itemView.findViewById(R.id.episodeName);
            episodeNumber = itemView.findViewById(R.id.episodeNumber);

            playImageview.setOnClickListener(v -> {
                String uid = FirebaseAuth.getInstance().getUid();
                if(uid != null){
                    String currentEpisodeName = episodeName.getText().toString();
                    StorageReference storageRef = FirebaseStorage.getInstance().getReference()
                            .child("album/" + uid + "/" + albumName + "/" + currentEpisodeName);

                    storageRef.getDownloadUrl().addOnSuccessListener(uri -> {
                        String episodeUrl = uri.toString();
                        Episodes.playEpisodes(episodeUrl, itemView.getContext());
                    }).addOnFailureListener(e -> {
                        Toast.makeText(itemView.getContext(),"获取播放链接失败",Toast.LENGTH_SHORT).show();
                    });
                }
            });
        }
    }
}

3. Episodes类优化(可选,管理ExoPlayer实例)

为避免每次点击新建ExoPlayer导致资源泄漏,建议全局维护一个ExoPlayer实例:

public class Episodes {
    Long episodeNumber;
    String episodeName,episodeUrl;
    private static ExoPlayer player; // 全局实例

    public Episodes(Long episodeNumber, String episodeName, String episodeUrl) {
        this.episodeNumber = episodeNumber;
        this.episodeName = episodeName;
        this.episodeUrl = episodeUrl;
    }

    public static void playEpisodes(String episodeUrl, Context context) {
        if (player == null) {
            player = new ExoPlayer.Builder(context).build();
        } else {
            player.stop();
            player.clearMediaItems();
        }
        MediaItem mediaItem = MediaItem.fromUri(episodeUrl);
        player.setMediaItem(mediaItem);
        player.prepare();
        player.play();
    }

    // 提供释放资源的方法,在Activity销毁时调用
    public static void releasePlayer() {
        if (player != null) {
            player.release();
            player = null;
        }
    }

    // getter和setter方法保持不变
    public Long getEpisodeNumber() { return episodeNumber; }
    public void setEpisodeNumber(Long episodeNumber) { this.episodeNumber = episodeNumber; }
    public String getEpisodeName() { return episodeName; }
    public void setEpisodeName(String episodeName) { this.episodeName = episodeName; }
    public String getEpisodeUrl() { return episodeUrl; }
    public void setEpisodeUrl(String episodeUrl) { this.episodeUrl = episodeUrl; }
}

额外注意事项

  • 确保Firebase Storage的安全规则允许当前用户访问指定路径的文件。
  • 检查album_episodes_design_recyclerview布局文件中控件ID与代码中的ID完全一致。
  • 在Activity的onDestroy方法中调用Episodes.releasePlayer()释放ExoPlayer资源。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 13:25:53