
问题背景与默认行为分析
在开发需要与usb设备交互的android应用时,我们通常会利用android.hardware.usb.action.usb_device_attached这一intent动作来监听设备的连接事件。当usb设备插入时,系统会广播此intent,如果我们的应用在androidmanifest.xml中配置了相应的intent-filter,便能响应这一事件。
然而,一个常见的问题是,当应用已经在前台运行时,如果用户断开并重新连接USB设备,应用可能会意外重启。这是因为Android Activity的默认启动模式(standard)会在每次接收到匹配的Intent时,即使Activity实例已存在,也会在栈顶创建一个新的Activity实例。对于USB_DEVICE_ATTACHED这类外部触发的Intent,系统会尝试启动或重新启动与该Intent关联的Activity,导致不必要的应用状态丢失和用户体验中断。
以下是一个典型的AndroidManifest.xml配置片段,展示了如何监听USB设备连接:
在这种默认配置下,如果MainActivity正在运行,重新连接USB设备会导致其重启。
解决方案:使用android:launchMode="singleTop"
要避免应用在USB设备重新连接时重启,同时又能确保在应用未运行时正常启动,关键在于修改Activity的启动模式。android:launchMode="singleTop"是一个理想的选择。
当Activity的启动模式设置为singleTop时:
- 如果目标Activity的实例已经位于任务栈的顶部,系统将不会创建新的实例,而是直接调用现有实例的onNewIntent()方法,并将新的Intent传递给它。
- 如果目标Activity的实例存在但不在任务栈的顶部,或者根本不存在,系统会像standard模式一样创建一个新的实例。
这种行为完美符合我们的需求:应用未运行时启动,运行中则接收通知而不重启。
配置示例
在AndroidManifest.xml中,为监听USB连接的Activity添加android:launchMode="singleTop"属性:
处理新的Intent:onNewIntent()方法
仅仅设置launchMode="singleTop"是不够的,我们还需要在Activity中重写onNewIntent()方法来处理接收到的新Intent。当Activity被复用时,onCreate()、onStart()等生命周期方法不会再次被调用,只有onNewIntent()会被触发。
在onNewIntent()方法中,我们可以解析传入的Intent,检查其Action是否为USB_DEVICE_ATTACHED,并执行相应的逻辑,例如更新UI、初始化USB通信或通知用户。
代码示例
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 MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
private UsbManager usbManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); // 假设您的布局文件
Log.d(TAG, "onCreate: Activity created.");
usbManager = (UsbManager) getSystemService(USB_SERVICE);
// 在onCreate中处理初始启动时的Intent
handleIntent(getIntent());
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
Log.d(TAG, "onNewIntent: New Intent received.");
// 必须设置新的Intent,否则getIntent()仍然返回旧的Intent
setIntent(intent);
handleIntent(intent);
}
private void handleIntent(Intent intent) {
if (intent != null && UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(intent.getAction())) {
UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
if (device != null) {
Log.i(TAG, "USB Device Attached: " + device.getDeviceName());
// 在这里执行您的USB设备初始化或通知逻辑
// 例如:
// setupUsbDevice(device);
// showToast("USB设备 " + device.getProductName() + " 已连接");
} else {
Log.w(TAG, "USB Device Attached intent received, but no device extra.");
}
} else if (intent != null && Intent.ACTION_MAIN.equals(intent.getAction())) {
Log.d(TAG, "Main launcher intent received.");
// 应用通过启动器图标启动
}
}
// 其他生命周期方法和业务逻辑...
}注意事项:
- 在onNewIntent()中,务必调用setIntent(intent);来更新Activity的当前Intent。否则,后续调用getIntent()仍会返回旧的Intent。
- 在onCreate()中也需要调用handleIntent(getIntent());,以确保应用首次启动时(例如通过USB连接启动)也能正确处理USB设备连接事件。
- device_filter.xml文件用于指定您的应用支持的USB设备类型,通常位于res/xml/目录下。
总结
通过将Activity的android:launchMode设置为singleTop,并正确实现onNewIntent()方法,可以有效地解决Android应用在监听USB设备连接时,因设备反复插拔而导致的意外重启问题。这不仅能保持应用的状态,避免不必要的资源消耗,还能显著提升用户体验。在开发与外部硬件交互的Android应用时,理解并合理运用Activity的启动模式是至关重要的。










