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
相关产品推荐
相关产品推荐

