从Firebase Storage获取音频时RecyclerView不显示问题排查求助
Firebase Storage音频文件无法在RecyclerView显示的问题排查与修复
问题描述
尝试从Firebase Storage获取音频文件并展示在RecyclerView中,但RecyclerView始终无内容显示。
核心问题分析
- 异步操作顺序错误:Firestore文档读取是异步操作,初始化StorageReference时
albumName还未被赋值,导致Storage路径无效,无法获取到音频文件。 - Adapter初始化时机错误:在音频文件的下载URL还未全部获取时就初始化了Adapter,且后续的
notifyDataSetChanged可能因上下文问题失效。 - 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
相关产品推荐
相关产品推荐

