Android智能电视应用读取USB串口及获取文件技术问询
Android智能电视USB存储访问解决方案
问题核心
你当前的实现错误地使用了UsbManager的低层级USB设备通信API(如bulkTransfer、claimInterface)来访问USB存储设备。实际上,Android智能电视会自动将USB存储设备挂载为文件系统,无需通过UsbDeviceConnection这类针对自定义USB设备的接口,直接用标准文件API或存储访问框架(SAF)即可访问。
另外你的代码存在几个关键问题:
- 权限请求是异步的,你在调用
requestPermission后立即执行文件访问逻辑,此时权限还未真正授予 - 硬编码USB路径
/mnt/usb_storage/不具备通用性,不同电视厂商的挂载路径可能不同 - 混淆了USB存储设备(系统自动挂载)和自定义USB设备(需要低层级通信)的访问方式
正确实现步骤
1. 申请必要权限
根据Android版本添加对应的权限:
- Android 9及以下:在
AndroidManifest.xml中添加<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> - Android 10-12:添加
READ_EXTERNAL_STORAGE,或使用MANAGE_EXTERNAL_STORAGE(针对电视这类非移动设备更友好) - Android 13+:添加
READ_MEDIA_IMAGES等媒体权限,或MANAGE_EXTERNAL_STORAGE
2. 检测USB存储挂载路径
不要硬编码路径,通过以下方式获取:
- 方式一:扫描系统存储目录
大部分电视的USB存储挂载在/storage/目录下,命名为usb0、usb1等,可通过遍历/storage/目录筛选出USB设备 - 方式二:使用存储访问框架(SAF)让用户选择USB存储根目录,兼容性最好
3. 访问USB文件
用标准File API或ContentResolver读取文件即可,无需涉及UsbManager的低层级操作。
代码示例
示例1:自动检测并访问USB存储文件
import android.content.Context import android.util.Log import java.io.File import java.io.FileInputStream import android.graphics.BitmapFactory class UsbStorageReader { private val TAG = "UsbStorageReader" // 获取所有挂载的USB存储目录 fun getUsbMountPaths(context: Context): List<File> { val usbPaths = mutableListOf<File>() // 常见的USB挂载目录 val possiblePaths = listOf( "/storage/usb0", "/storage/usb1", "/mnt/usb_storage", "/mnt/usbdisk", "/storage/external_storage" ) possiblePaths.forEach { path -> val file = File(path) if (file.exists() && file.isDirectory && file.canRead()) { usbPaths.add(file) } } // 额外扫描/storage目录下的所有子目录,适配不同厂商 val storageDir = File("/storage/") storageDir.listFiles()?.forEach { dir -> if (dir.isDirectory && dir.canRead() && dir.name.startsWith("usb")) { usbPaths.add(dir) } } return usbPaths } // 读取USB中的图片文件 fun readImagesFromUsb(context: Context): List<Any> { val imageList = mutableListOf<Any>() val usbDirs = getUsbMountPaths(context) usbDirs.forEach { usbDir -> scanImages(usbDir, imageList) } return imageList } private fun scanImages(dir: File, imageList: MutableList<Any>) { dir.listFiles()?.forEach { file -> if (file.isDirectory) { scanImages(file, imageList) } else { val extension = file.name.lowercase() if (extension.endsWith(".jpg") || extension.endsWith(".png") || extension.endsWith(".jpeg")) { try { val inputStream = FileInputStream(file) val bitmap = BitmapFactory.decodeStream(inputStream) inputStream.close() imageList.add(bitmap) Log.d(TAG, "读取图片成功: ${file.absolutePath}") } catch (e: Exception) { Log.e(TAG, "读取图片失败: ${file.absolutePath}", e) } } } } } }
示例2:使用SAF让用户选择USB存储
import android.app.Activity import android.content.Intent import android.net.Uri import android.os.Bundle import android.provider.DocumentsContract import android.widget.Button import androidx.appcompat.app.AppCompatActivity class UsbSafActivity : AppCompatActivity() { private val REQUEST_CODE_PICK_USB = 1001 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_usb_saf) val selectUsbBtn = findViewById<Button>(R.id.select_usb_btn) selectUsbBtn.setOnClickListener { // 打开SAF选择器,指定选择目录 val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, Uri.parse("/storage/")) startActivityForResult(intent, REQUEST_CODE_PICK_USB) } } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if (requestCode == REQUEST_CODE_PICK_USB && resultCode == Activity.RESULT_OK) { data?.data?.let { uri -> // 保存uri权限,以便后续访问 contentResolver.takePersistableUriPermission( uri, Intent.FLAG_GRANT_READ_URI_PERMISSION ) // 现在可以通过ContentResolver访问该目录下的文件 scanImagesFromUri(uri) } } } private fun scanImagesFromUri(uri: Uri) { // 使用ContentResolver遍历目录,可参考DocumentsContract API实现文件遍历 } }
关键注意事项
- 权限异步处理:如果使用
UsbManager.requestPermission,必须等待BroadcastReceiver回调确认权限授予后再执行操作,不能同步调用 - 厂商兼容性:不同电视厂商的USB挂载路径可能不同,优先使用SAF方式,或多路径扫描
- 避免低层级API:
UsbDeviceConnection、bulkTransfer等API仅适用于串口、打印机这类非存储类USB设备,USB存储直接用文件系统访问即可 - Android版本适配:针对不同Android版本的存储权限规则,做好动态权限申请
内容的提问来源于stack exchange,提问作者Mirza
相关产品推荐
相关产品推荐

