如何从独立线程向Activity发送连续流?蓝牙线程与Activity通信咨询
Hey there! Let's tackle your problem step by step. You're running a Bluetooth data transfer thread launched from a Service, and need to communicate with an Activity to display a list of input streams—plus you're unsure about EventBus handling high-frequency data. Here are some solid, battle-tested approaches with actionable examples:
Yes, it can—but with key caveats:
- Use
ThreadMode.MAINcautiously: Avoid heavy UI work directly in event handlers, as this can cause main thread jank. - Add throttling for extreme frequency: Batch multiple stream list changes into a single event every 50-100ms instead of firing an event on every tiny update.
That said, EventBus isn't the most idiomatic choice for modern Android development. Let's look at better, lifecycle-aware alternatives.
LiveData is built for cross-component communication and automatically stops updating your Activity when it's in the background—no memory leaks or unnecessary UI refreshes. Perfect for Service ↔ Activity workflows.
Service Side
class BluetoothService : Service() { // MutableLiveData for internal updates, immutable LiveData exposed to Activity private val _streamList = MutableLiveData<List<StreamItem>>(emptyList()) val streamList: LiveData<List<StreamItem>> = _streamList private val bluetoothThread = Thread { // Your Bluetooth data transfer logic here while (isRunning) { // When the stream list updates: val updatedStreams = fetchUpdatedStreamList() // Replace with your actual logic // Use postValue() to safely update LiveData from a background thread _streamList.postValue(updatedStreams) } } // Binder to let the Activity access the Service instance inner class LocalBinder : Binder() { fun getService(): BluetoothService = this@BluetoothService } override fun onBind(intent: Intent): IBinder = LocalBinder() override fun onCreate() { super.onCreate() bluetoothThread.start() } }
Activity Side
class StreamDisplayActivity : AppCompatActivity() { private var bluetoothService: BluetoothService? = null private var isServiceBound = false private val streamAdapter = StreamListAdapter() // Your RecyclerView adapter private val serviceConnection = object : ServiceConnection { override fun onServiceConnected(className: ComponentName, service: IBinder) { val binder = service as BluetoothService.LocalBinder bluetoothService = binder.getService() isServiceBound = true // Observe LiveData to auto-update the UI bluetoothService?.streamList?.observe(this@StreamDisplayActivity) { streams -> streamAdapter.submitList(streams) } } override fun onServiceDisconnected(className: ComponentName) { isServiceBound = false bluetoothService = null } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_stream_display) // Bind to the BluetoothService Intent(this, BluetoothService::class.java).also { intent -> bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE) } // Initialize RecyclerView with your adapter streamRecyclerView.adapter = streamAdapter } override fun onDestroy() { super.onDestroy() if (isServiceBound) { unbindService(serviceConnection) } } }
If you're using Kotlin, StateFlow is even more powerful—it's a reactive stream that holds a single state, integrates seamlessly with coroutines, and supports operators to handle high-frequency data efficiently.
Service Side
class BluetoothService : Service() { // MutableStateFlow for internal updates, immutable StateFlow exposed private val _streamState = MutableStateFlow<List<StreamItem>>(emptyList()) val streamState: StateFlow<List<StreamItem>> = _streamState private val bluetoothThread = Thread { while (isRunning) { val updatedStreams = fetchUpdatedStreamList() // Update StateFlow value (safe from background threads) _streamState.value = updatedStreams } } // Same LocalBinder as the LiveData example to expose the Service inner class LocalBinder : Binder() { fun getService(): BluetoothService = this@BluetoothService } override fun onBind(intent: Intent): IBinder = LocalBinder() override fun onCreate() { super.onCreate() bluetoothThread.start() } }
Activity Side
class StreamDisplayActivity : AppCompatActivity() { private var bluetoothService: BluetoothService? = null private var isServiceBound = false private val streamAdapter = StreamListAdapter() private val serviceConnection = object : ServiceConnection { override fun onServiceConnected(className: ComponentName, service: IBinder) { val binder = service as BluetoothService.LocalBinder bluetoothService = binder.getService() isServiceBound = true // Collect StateFlow using lifecycle-aware coroutines lifecycleScope.launch { // Only collect updates when the Activity is in STARTED/RESUMED state repeatOnLifecycle(Lifecycle.State.STARTED) { bluetoothService?.streamState?.collect { streams -> // Switch to main thread to update UI withContext(Dispatchers.Main) { streamAdapter.submitList(streams) } } } } } override fun onServiceDisconnected(className: ComponentName) { isServiceBound = false bluetoothService = null } } // Same onCreate/onDestroy binding logic as the LiveData example }
If your stream list changes constantly, add throttling to avoid UI jank:
- For StateFlow: Use the
debounceoperator to batch updates (e.g., process changes only every 100ms):bluetoothService?.streamState?.debounce(100)?.collect { streams -> streamAdapter.submitList(streams) } - For LiveData: Create a helper function to batch updates, or use
MediatorLiveDatato throttle emissions.
EventBus works, but LiveData/StateFlow are more aligned with Android's best practices—they handle lifecycle automatically, reduce boilerplate, and make state management simpler. StateFlow is the top pick for Kotlin projects, while LiveData is great for Java or cross-version compatibility.
内容的提问来源于stack exchange,提问作者Avinash Reddy Chintareddy

