横竖屏切换时重绘Fragment内RecyclerView项的正确方法及异常解决
Hey there! I’ve dealt with this exact issue before, so let’s break down what’s going wrong and the cleanest ways to get your Fragment’s RecyclerView to redraw all items when the screen rotates.
First, let’s unpack that java.lang.IllegalMonitorStateException you’re seeing. This usually pops up for one of two reasons:
- You’re calling
notifyDataSetChanged()from a non-UI thread (RecyclerView operations must run on the main thread). - You’re triggering the update when the Fragment isn’t in a valid lifecycle state (like after it’s been detached or before its view is fully created).
Now, here are the ideal solutions for this scenario:
1. Use onConfigurationChanged (For No-Recreation Flows)
If you don’t want your Activity/Fragment to rebuild on rotation, add the configChanges attribute to your Activity in AndroidManifest.xml:
<activity android:name=".YourActivity" android:configChanges="orientation|screenSize"> </activity>
Then, in your Fragment, override onConfigurationChanged to safely trigger the RecyclerView update (this method runs on the UI thread, so no thread issues):
@Override public void onConfigurationChanged(@NonNull Configuration newConfig) { super.onConfigurationChanged(newConfig); // Check if the adapter and RecyclerView are valid first if (mRecyclerView != null && mAdapter != null) { mAdapter.notifyDataSetChanged(); // If you need to force a full layout recalculation (e.g., for new screen dimensions) mRecyclerView.requestLayout(); } }
2. Leverage ViewModel (For Recreation-Safe Updates)
If you prefer letting the system recreate your Fragment on rotation (the recommended approach for most cases), use a ViewModel to hold your RecyclerView data. ViewModels survive configuration changes, so your data stays intact, and you can auto-update the RecyclerView when the Fragment rebuilds.
First, create a ViewModel to hold your data:
public class MyListViewModel extends ViewModel { private final MutableLiveData<List<YourItemModel>> mListData = new MutableLiveData<>(); public LiveData<List<YourItemModel>> getListData() { return mListData; } public void updateListData(List<YourItemModel> newData) { mListData.setValue(newData); } }
Then, in your Fragment’s onViewCreated, observe the ViewModel’s data. When the Fragment rebuilds after rotation, this observer will automatically trigger and update the RecyclerView:
@Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); MyListViewModel viewModel = new ViewModelProvider(this).get(MyListViewModel.class); viewModel.getListData().observe(getViewLifecycleOwner(), updatedList -> { // If using ListAdapter (recommended for efficiency) mListAdapter.submitList(updatedList); // If using a regular Adapter: // mAdapter.setData(updatedList); // mAdapter.notifyDataSetChanged(); }); }
This approach avoids manual lifecycle checks entirely and ensures updates happen safely on the UI thread.
3. Fix the Original Interface Approach (If You Want to Keep It)
If you still want to use your interface-based notification system, fix the exception by:
- Ensuring the update runs on the UI thread:
// Inside your interface callback in the Fragment requireActivity().runOnUiThread(() -> { if (isAdded() && !isDetached() && mAdapter != null) { mAdapter.notifyDataSetChanged(); } }); - Adding checks to confirm the Fragment is in an active state (
isAdded()and!isDetached()) before updating the adapter.
Pro Tips
- Use ListAdapter Instead of Regular Adapter:
submitList()automatically calculates differences between old and new data, leading to smoother updates and fewer unnecessary redraws. - Avoid Overusing
notifyDataSetChanged(): If only specific items need to update, use more granular methods likenotifyItemRangeChanged()to boost performance.
内容的提问来源于stack exchange,提问作者Midhun Kumar

