Android设备使用RxJava实现USB读卡器后台每秒轮询需求
Got it, let's walk through how to build this USB card reader polling feature that runs reliably even when your app is in the background, using RxJava. Here's a step-by-step breakdown with code examples:
Core Concepts
- Use RxJava's
intervaloperator to handle the 1-second polling interval. - Offload the USB reading work to a background thread (via
Schedulers.io()or a custom scheduler) to avoid blocking the main thread. - Use a Foreground Service to host the polling task—this ensures the system won't kill the thread when the app moves to the background (critical for Android O and above, where background services are restricted).
- Switch back to the main thread with
observeOn(AndroidSchedulers.mainThread())to update UI or process results safely.
Step 1: Add Dependencies
First, make sure you have RxJava and RxAndroid in your build.gradle (Module level):
dependencies { // RxJava 3 implementation 'io.reactivex.rxjava3:rxjava:3.1.5' implementation 'io.reactivex.rxjava3:rxandroid:3.0.2' }
Step 2: Implement USB Reader Polling Logic
Wrap your USB card reading logic into an Observable. This method will handle the actual read operation, and we'll use it with the interval operator:
private Observable<String> pollUsbCardReader() { return Observable.defer(() -> { try { // Replace this with your actual USB card reading logic String cardData = readDataFromUsbReader(); return Observable.just(cardData); } catch (Exception e) { // Handle read errors without breaking the polling loop return Observable.error(e); } }); } // Dummy method for your actual USB reading code private String readDataFromUsbReader() throws Exception { // Implement your USB communication here (e.g., using UsbManager) return "Card ID: 123456789"; }
Step 3: Create a Foreground Service to Host Polling
Foreground services require a persistent notification, so the user is aware the app is running in the background. Here's the service implementation:
import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.Service; import android.content.Intent; import android.os.Build; import android.os.IBinder; import androidx.core.app.NotificationCompat; import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; import io.reactivex.rxjava3.disposables.CompositeDisposable; import io.reactivex.rxjava3.disposables.Disposable; import io.reactivex.rxjava3.schedulers.Schedulers; import java.util.concurrent.TimeUnit; public class UsbPollingService extends Service { private static final int NOTIFICATION_ID = 1001; private static final String CHANNEL_ID = "USB_POLLING_CHANNEL"; private CompositeDisposable compositeDisposable = new CompositeDisposable(); @Override public void onCreate() { super.onCreate(); createNotificationChannel(); startForeground(NOTIFICATION_ID, buildNotification()); startPolling(); } private void startPolling() { Disposable pollingDisposable = Observable.interval(0, 1, TimeUnit.SECONDS) .flatMap(ignored -> pollUsbCardReader()) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe( cardData -> { // Handle successful card read result on main thread // e.g., send to Activity via BroadcastReceiver or LiveData handleCardData(cardData); }, error -> { // Handle read errors (e.g., log, show toast) error.printStackTrace(); } ); compositeDisposable.add(pollingDisposable); } private void handleCardData(String cardData) { // Example: Send data to your Activity using a LocalBroadcast Intent intent = new Intent("USB_CARD_DATA_RECEIVED"); intent.putExtra("CARD_DATA", cardData); sendBroadcast(intent); } private Notification buildNotification() { return new NotificationCompat.Builder(this, CHANNEL_ID) .setContentTitle("USB Card Reader Polling") .setContentText("Running in background") .setSmallIcon(R.drawable.ic_usb_icon) // Replace with your icon .setPriority(NotificationCompat.PRIORITY_LOW) .build(); } private void createNotificationChannel() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationChannel channel = new NotificationChannel( CHANNEL_ID, "USB Polling", NotificationManager.IMPORTANCE_LOW ); NotificationManager manager = getSystemService(NotificationManager.class); manager.createNotificationChannel(channel); } } @Override public void onDestroy() { super.onDestroy(); // Dispose all subscriptions to stop polling and avoid memory leaks compositeDisposable.dispose(); } @Override public IBinder onBind(Intent intent) { return null; // We're using a started service, not bound } }
Step 4: Start the Service from Your Activity
To kick off the polling, start the service from your Activity (or wherever makes sense in your app):
import android.content.Intent; import android.os.Bundle; import androidx.appcompat.app.AppCompatActivity; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Start the polling service Intent serviceIntent = new Intent(this, UsbPollingService.class); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { startForegroundService(serviceIntent); } else { startService(serviceIntent); } } }
Key Notes & Best Practices
- USB Permissions: Don't forget to request USB device permissions in your manifest and at runtime. Add this to
AndroidManifest.xml:<uses-feature android:name="android.hardware.usb.host" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> - Error Handling: Use operators like
onErrorResumeNext()orretry()if you want to automatically recover from read errors without stopping the polling loop. - Memory Leaks: Always dispose of your RxJava subscriptions in the service's
onDestroy()method (we useCompositeDisposablehere to manage multiple subscriptions easily). - Communication: To send results from the service to your UI, use
LocalBroadcastManager, LiveData with a shared ViewModel, or an EventBus library—choose what fits your app architecture best.
内容的提问来源于stack exchange,提问作者Mohammed Daoud

