Android模拟器运行时挂载外接USB存储及相关开发问题咨询
USB存储管理相关问题解决方案(API31、Kotlin)
一、Android模拟器模拟USB存储设备挂载
方法1:通过模拟器扩展控件添加虚拟USB
运行模拟器后,点击界面右侧的「扩展控件」按钮(三个点图标),进入Storage选项卡:
- 点击「Add shared folder」,选择本地电脑的一个文件夹作为虚拟USB存储的内容源
- 按需勾选「Mount read-only」(取消勾选则为可读写),点击「OK」即可完成挂载,模拟器会识别为可移动USB存储设备
方法2:通过ADB命令挂载虚拟磁盘
若需要更灵活的模拟,可按以下步骤操作:
- 本地终端创建虚拟磁盘镜像:
dd if=/dev/zero of=usb.img bs=1M count=100 - 将镜像推送到模拟器:
adb push usb.img /data/local/tmp/ - 进入模拟器Shell,挂载镜像为循环设备并格式化:
adb shell losetup /dev/block/loop0 /data/local/tmp/usb.img mkfs.ext4 /dev/block/loop0 - 挂载到模拟器可移动存储目录:
mount /dev/block/loop0 /mnt/usb - 让StorageManager识别该存储卷:
(可通过sm add-volume disk:1,2 /mnt/usbsm list-volumes查看已注册存储卷ID)
二、向已挂载的USB存储设备写入.txt文件
方案1:使用Storage Access Framework(推荐,无需特殊权限审核)
通过用户选择USB目录获取写入权限:
private const val REQUEST_CODE_USB_DIR = 1001 // 启动SAF选择USB目录 fun selectUsbDirectory() { val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) startActivityForResult(intent, REQUEST_CODE_USB_DIR) } // 处理SAF返回结果 override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if (requestCode == REQUEST_CODE_USB_DIR && resultCode == RESULT_OK) { data?.data?.let { uri -> // 持久化写入权限 contentResolver.takePersistableUriPermission( uri, Intent.FLAG_GRANT_WRITE_URI_PERMISSION ) // 创建并写入test.txt val fileUri = DocumentsContract.createDocument( contentResolver, uri, "text/plain", "test.txt" ) fileUri?.let { contentResolver.openOutputStream(it).use { outputStream -> outputStream?.write("Hello USB Storage!".toByteArray()) } } } } }
方案2:使用MANAGE_EXTERNAL_STORAGE权限(需特殊审核)
若已获取MANAGE_EXTERNAL_STORAGE权限,可直接通过存储卷路径写入:
val storageManager = getSystemService(Context.STORAGE_SERVICE) as StorageManager // 过滤已挂载的可移动USB存储 val usbVolume = storageManager.storageVolumes.firstOrNull { it.isRemovable && it.state == StorageVolume.STATE_MOUNTED } usbVolume?.directory?.let { usbDir -> val testFile = File(usbDir, "test.txt") testFile.writeText("Hello USB Storage!") }
三、ViewModel中获取StorageManager(解决Context问题)
ViewModel不能直接持有Activity/Fragment的Context,应使用AndroidViewModel(持有Application级Context,无内存泄漏风险):
class UsbViewModel(application: Application) : AndroidViewModel(application) { private val storageManager: StorageManager by lazy { application.getSystemService(Context.STORAGE_SERVICE) as StorageManager } // 注册USB挂载状态监听 fun registerUsbMountListener() { val callback = object : StorageManager.StorageVolumeCallback() { override fun onVolumeStateChanged( volume: StorageVolume, oldState: Int, newState: Int ) { super.onVolumeStateChanged(volume, oldState, newState) if (volume.isRemovable) { when(newState) { StorageVolume.STATE_MOUNTED -> { // 处理USB挂载逻辑 } StorageVolume.STATE_UNMOUNTED -> { // 处理USB卸载逻辑 } } } } } // 主线程执行回调 storageManager.registerStorageVolumeCallback( application.mainExecutor, callback ) } }
在Activity/Fragment中初始化ViewModel:
val usbViewModel = ViewModelProvider(this)[UsbViewModel::class.java]
额外USB存储管理建议
- 权限优先选SAF:API31+中,
MANAGE_EXTERNAL_STORAGE权限需在Google Play控制台说明用途,审核严格,SAF是更合规的方案 - 监听状态用StorageVolumeCallback:API30+中,
ACTION_MEDIA_MOUNTED等广播仅系统应用可接收,StorageVolumeCallback是官方推荐的监听方式 - 避免硬编码路径:不同设备的USB挂载路径可能不同,始终通过
StorageVolume.directory或SAF Uri操作文件 - 安全卸载:USB存储卸载前,确保所有文件流已关闭,防止数据损坏或丢失
内容的提问来源于stack exchange,提问作者snux_cs
相关产品推荐
相关产品推荐

