You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android模拟器运行时挂载外接USB存储及相关开发问题咨询

USB存储管理相关问题解决方案(API31、Kotlin)

一、Android模拟器模拟USB存储设备挂载

方法1:通过模拟器扩展控件添加虚拟USB

运行模拟器后,点击界面右侧的「扩展控件」按钮(三个点图标),进入Storage选项卡:

  • 点击「Add shared folder」,选择本地电脑的一个文件夹作为虚拟USB存储的内容源
  • 按需勾选「Mount read-only」(取消勾选则为可读写),点击「OK」即可完成挂载,模拟器会识别为可移动USB存储设备

方法2:通过ADB命令挂载虚拟磁盘

若需要更灵活的模拟,可按以下步骤操作:

  1. 本地终端创建虚拟磁盘镜像:
    dd if=/dev/zero of=usb.img bs=1M count=100
    
  2. 将镜像推送到模拟器:
    adb push usb.img /data/local/tmp/
    
  3. 进入模拟器Shell,挂载镜像为循环设备并格式化:
    adb shell
    losetup /dev/block/loop0 /data/local/tmp/usb.img
    mkfs.ext4 /dev/block/loop0
    
  4. 挂载到模拟器可移动存储目录:
    mount /dev/block/loop0 /mnt/usb
    
  5. 让StorageManager识别该存储卷:
    sm add-volume disk:1,2 /mnt/usb
    
    (可通过sm 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 08:05:27