在android开发中,当应用需要响应外部硬件(如usb设备)的连接事件时,通常会在androidmanifest.xml中为相应的activity声明一个intent-filter,监听特定的action,例如android.hardware.usb.action.usb_device_attached。当usb设备连接时,系统会发送一个匹配此action的intent,并尝试启动或唤醒对应的activity。
然而,默认情况下,如果Activity的launchMode为standard(默认值),即使应用已经在运行,当再次接收到相同的Intent时,系统也可能会创建一个新的Activity实例,并将其放置在任务栈的顶部。这会导致用户体验不佳,因为应用会表现为“重启”,丢失当前状态。对于需要持续与USB设备通信的应用,理想的行为是:
要实现上述理想行为,关键在于修改Activity的启动模式。android:launchMode="singleTop"是解决此问题的有效方法。
当一个Activity被设置为singleTop启动模式时:
对于USB设备连接事件,由于通常是由系统发送Intent触发,且我们希望在应用运行时直接处理,singleTop模式非常适用。当应用在运行时,其主Activity(通常也是监听USB事件的Activity)很可能就在任务栈的顶部,此时新的USB连接Intent将直接传递给onNewIntent()。
修改AndroidManifest.xml: 在监听USB_DEVICE_ATTACHED事件的Activity标签中,添加android:launchMode="singleTop"属性。
...
注意: android:exported="true" 是为了确保Activity可以被外部应用(如系统在检测到USB设备连接时)通过Intent启动。对于监听系统广播的Activity,这通常是必需的。
在Activity中处理新的Intent: 在对应的Activity类中,重写onNewIntent(Intent intent)方法。所有通过singleTop模式传递给现有Activity的Intent都将在此方法中接收。
import android.content.Intent; import android.hardware.usb.UsbConstants; import android.hardware.usb.UsbDevice; import android.hardware.usb.UsbManager; import android.os.Bundle; import androidx.appcompat.app.AppCompatActivity; import android.util.Log; public class YourMainActivity extends AppCompatActivity { private static final String TAG = "YourMainActivity"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.d(TAG, "onCreate: Activity created."); // 首次启动时处理Intent handleUsbDeviceAttached(getIntent()); } @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); // 当Activity已在顶部时,新的Intent会通过此方法传递 Log.d(TAG, "onNewIntent: New Intent received."); setIntent(intent); // 更新Activity的当前Intent handleUsbDeviceAttached(intent); } private void handleUsbDeviceAttached(Intent intent) { if (intent != null && UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(intent.getAction())) { UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); if (device != null) { Log.d(TAG, "USB Device Attached: " + device.getDeviceName()); // 在这里处理USB设备的连接逻辑,例如: // - 获取UsbManager服务 // - 请求USB设备的权限 // - 打开USB设备进行通信 // - 更新UI显示连接状态 // 例如: // UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE); // if (manager.hasPermission(device)) { // // 打开设备 // } else { // // 请求权限 // } } } } @Override protected void onResume() { super.onResume(); // 可以在onResume中再次检查USB设备状态,以防万一 Log.d(TAG, "onResume: Activity resumed."); } @Override protected void onDestroy() { super.onDestroy(); Log.d(TAG, "onDestroy: Activity destroyed."); } }
在onNewIntent()中,调用setIntent(intent)是一个好习惯,它会将Activity的当前Intent更新为最新收到的Intent。这样,后续对getIntent()的调用将返回最新的Intent。
通过在AndroidManifest.xml中为监听USB连接事件的Activity设置android:launchMode="singleTop",并正确实现onNewIntent()方法,可以有效地防止Android应用在USB设备重新连接时重复启动。这种方法使得应用能够在保持现有状态的同时,接收并处理新的USB连接通知,从而提供更流畅、更专业的用户体验。理解并恰当运用Activity的启动模式是Android开发中优化应用行为的重要一环。