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

Tab页面显示空白且报No adapter attached错误的排查求助

解决Tab Fragment中RecyclerView显示空白(No adapter attached; skipping layout)的问题

我来帮你排查这个问题,核心原因是你的loadSongs方法里有一个关键错误,同时还有一些线程和生命周期的小问题,咱们一步步解决:

核心问题:Adapter被重新创建但未绑定到RecyclerView

在你的loadSongs方法末尾,你重新实例化了songAdapter1:

songAdapter1 = new songAdapter(getActivity(),_songs);

但此时RecyclerView绑定的还是之前初始化的旧Adapter实例,这就导致新的Adapter没有和RecyclerView关联,而且你在循环中调用的notifyDataSetChanged是作用在旧Adapter上的,后续操作完全混乱了。直接删掉这行代码即可。

优化Adapter刷新逻辑

现在你在循环里每添加一首歌曲就调用一次notifyDataSetChanged,这会导致RecyclerView频繁刷新,非常影响性能。应该把刷新操作放到循环结束后:

private void loadSongs(){
    Uri uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
    String selection = MediaStore.Audio.Media.IS_MUSIC+"!=0";
    Cursor cursor = getContext().getContentResolver().query(uri,null,selection,null,null);
    if(cursor != null){
        if(cursor.moveToFirst()){
            do{
                String name = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.ALBUM));
                String artist = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.ARTIST));
                String url = cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.DATA));
                songInfo s = new songInfo(name,artist,url);
                _songs.add(s);
                // 移除这里的notifyDataSetChanged
            }while (cursor.moveToNext());
        }
        cursor.close();
        // 循环结束后统一刷新
        songAdapter1.notifyDataSetChanged();
    }
}

修复MediaPlayer空指针问题

在Item点击事件里,你直接判断mediaPlayer.isPlaying(),但mediaPlayer初始值是null,会触发空指针异常。需要先判断mediaPlayer是否不为null:

songAdapter1.setOnItemClickListener(new songAdapter.OnItemClickListener() {
    @Override
    public void onItemClick(RecyclerView.ViewHolder holder, View view, final songInfo obj, int position) {
        // 先判断mediaPlayer是否存在
        if(mediaPlayer != null && mediaPlayer.isPlaying()){
            mediaPlayer.stop();
            mediaPlayer.reset();
            mediaPlayer.release();
            mediaPlayer = null;
        }else {
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    try {
                        mediaPlayer = new MediaPlayer();
                        mediaPlayer.setDataSource(obj.getSongUrl());
                        mediaPlayer.prepareAsync();
                        mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
                            @Override
                            public void onPrepared(MediaPlayer mp) {
                                mp.start();
                                seekBar.setProgress(0);
                                seekBar.setMax(mediaPlayer.getDuration());
                                Log.d("Prog", "run: " + mediaPlayer.getDuration());
                            }
                        });
                        Log.i("devesh","vikas");
                    }catch (Exception e){
                        e.printStackTrace(); // 不要吞掉异常,方便调试
                    }
                }
            };
            myHandler.postDelayed(runnable,100);
        }
    }
});

解决内存泄漏问题

你创建的runThread是无限循环线程,即使Fragment销毁了它还会继续运行,会导致内存泄漏。需要在Fragment的生命周期方法里停止线程和Handler:

private Thread t;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    // ... 其他代码
    t = new runThread();
    t.start();
    return v;
}

@Override
public void onDestroy() {
    super.onDestroy();
    // 停止线程
    if(t != null){
        t.interrupt();
        t = null;
    }
    // 移除Handler的回调
    myHandler.removeCallbacksAndMessages(null);
    // 释放MediaPlayer
    if(mediaPlayer != null){
        mediaPlayer.release();
        mediaPlayer = null;
    }
}

关于日志中的其他警告

  • No adapter attached; skipping layout:修复上述Adapter的问题后,这个警告就会消失。
  • EGL_BAD_MATCH:这是模拟器渲染的小问题,在真机上通常不会出现,暂时可以忽略。
  • Skipped 33 frames:因为你在主线程里遍历Cursor加载歌曲,建议把loadSongs放到子线程里执行,避免主线程卡顿:
private void checkUserPermission(){
    if(Build.VERSION.SDK_INT>=23){
        if(ActivityCompat.checkSelfPermission(getActivity(), Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED){
            requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},123);
            return;
        }
    }
    // 放到子线程加载歌曲
    new Thread(new Runnable() {
        @Override
        public void run() {
            loadSongs();
            // 回到主线程刷新Adapter
            getActivity().runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    songAdapter1.notifyDataSetChanged();
                }
            });
        }
    }).start();
}

这样修改后,你的Tab Fragment里的RecyclerView应该就能正常显示歌曲列表了。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 07:13:04