EventBus用于RecyclerView适配器与Fragment通信的问题及替代方案咨询
Great question! EventBus can indeed lead to memory leaks if not handled properly—especially in scenarios where components like Fragments are destroyed but still held by EventBus's subscriptions. Let's break down both fixes for your current EventBus setup and solid alternative approaches that are more aligned with modern Android best practices.
First: Fixing EventBus Leaks
If you want to stick with EventBus, you can eliminate leaks by following these strict rules:
- Always pair registration/unregistration: Register EventBus in
onViewCreated()(for Fragments) and unregister inonDestroyView()(not justonDestroy()—this covers configuration changes like screen rotations).override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) EventBus.getDefault().register(this) } override fun onDestroyView() { EventBus.getDefault().unregister(this) super.onDestroyView() } - Avoid sticky events unless necessary: Sticky events persist in memory even after subscribers are unregistered. If you use them, explicitly remove them when they're no longer needed with
EventBus.getDefault().removeStickyEvent(YourEventClass::class.java). - Use weak references for subscribers: Some EventBus variants (like greenrobot's) support weak subscriber modes, which automatically clean up references when the component is destroyed. Check your EventBus implementation's docs for this feature.
Recommended Alternatives (No EventBus Needed)
These approaches are more maintainable, lifecycle-aware, and less prone to leaks:
1. Interface Callbacks (Lightweight, No Dependencies)
This is the most straightforward vanilla Android approach. Define a callback interface to communicate between your adapter/Fragment and player:
- Step 1: Create a navigation interface
interface VideoNavigationListener { fun onRequestPreviousVideo() fun onRequestNextVideo() fun onVideoSelected(position: Int) } - Step 2: Implement the interface in your host Fragment/Activity
class VideoListFragment : Fragment(), VideoNavigationListener { private lateinit var recyclerView: RecyclerView private lateinit var adapter: VideoAdapter override fun onCreateView(...) { adapter = VideoAdapter(this) // Pass the listener to the adapter recyclerView.adapter = adapter } override fun onRequestPreviousVideo() { val currentPos = // Get current playing position if (currentPos > 0) { recyclerView.scrollToPosition(currentPos - 1) // Trigger player to load the new video } } override fun onRequestNextVideo() { val currentPos = // Get current playing position if (currentPos < adapter.itemCount - 1) { recyclerView.scrollToPosition(currentPos + 1) // Trigger player to load the new video } } override fun onVideoSelected(position: Int) { // Launch player fragment with the selected position } } - Step 3: Use a WeakReference in the adapter to avoid leaks
class VideoAdapter(listener: VideoNavigationListener) : RecyclerView.Adapter<VideoAdapter.VideoViewHolder>() { private val listenerRef = WeakReference<VideoNavigationListener>(listener) // Inside ViewHolder, handle player button clicks: inner class VideoViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { init { itemView.prevBtn.setOnClickListener { listenerRef.get()?.onRequestPreviousVideo() } itemView.nextBtn.setOnClickListener { listenerRef.get()?.onRequestNextVideo() } } } }
2. ViewModel + LiveData (Lifecycle-Aware, Jetpack Recommended)
For a more scalable approach (especially in large apps), use a shared ViewModel tied to the parent Activity. This ensures communication survives configuration changes and avoids leaks:
- Step 1: Create a shared ViewModel
class VideoPlayerViewModel : ViewModel() { private val _currentPlayPosition = MutableLiveData<Int>() val currentPlayPosition: LiveData<Int> = _currentPlayPosition fun updatePlayPosition(position: Int) { _currentPlayPosition.value = position } fun playPrevious(totalVideos: Int) { _currentPlayPosition.value?.let { pos -> if (pos > 0) _currentPlayPosition.value = pos - 1 } } fun playNext(totalVideos: Int) { _currentPlayPosition.value?.let { pos -> if (pos < totalVideos - 1) _currentPlayPosition.value = pos + 1 } } } - Step 2: Observe the ViewModel in your Video List Fragment
class VideoListFragment : Fragment() { // Get ViewModel scoped to the parent Activity private val viewModel: VideoPlayerViewModel by activityViewModels() override fun onCreateView(...) { viewModel.currentPlayPosition.observe(viewLifecycleOwner) { position -> recyclerView.scrollToPosition(position) // Update list selection state } // Handle RecyclerView item clicks adapter.setOnItemClickListener { pos -> viewModel.updatePlayPosition(pos) // Launch player fragment } } } - Step 3: Use the ViewModel in your Player Fragment
class VideoPlayerFragment : Fragment() { private val viewModel: VideoPlayerViewModel by activityViewModels() private var totalVideos = 0 override fun onCreateView(...) { // Set button click listeners prevBtn.setOnClickListener { viewModel.playPrevious(totalVideos) } nextBtn.setOnClickListener { viewModel.playNext(totalVideos) } // Observe position changes to load new videos viewModel.currentPlayPosition.observe(viewLifecycleOwner) { position -> loadVideoFromPosition(position) } } }
3. Kotlin Flow (For Kotlin Projects, Flexible)
If you're using Kotlin, replace LiveData with StateFlow or SharedFlow for more flexibility (e.g., collecting events in coroutines):
class VideoPlayerViewModel : ViewModel() { private val _currentPlayPosition = MutableStateFlow(-1) val currentPlayPosition: StateFlow<Int> = _currentPlayPosition fun updatePlayPosition(position: Int) { _currentPlayPosition.value = position } // ... previous/next logic remains similar }
Then collect the flow in your Fragment using lifecycleScope:
viewModel.currentPlayPosition.collectLatest { position -> if (position != -1) { recyclerView.scrollToPosition(position) } }
Final Recommendation
For large apps, ViewModel + LiveData/Flow is the best choice—it's lifecycle-aware, scalable, and integrates seamlessly with Jetpack components. If you prefer a lightweight solution without Jetpack, interface callbacks with WeakReferences are a safe bet.
内容的提问来源于stack exchange,提问作者Ashish Sharma

