Android通过连接USB读写SD卡(libaums方案)

Android通过连接USB读写SD卡

最近有一个需求是要求通过Usb扩展读取到SD卡的内容。可以从Usb存储设备拷贝文件到内置卡,也可以从内置卡文件拷贝到Usb存储。

连接方式

1. 相关的引入包

 implementation 'androidx.core:core-ktx:1.7.0'implementation 'androidx.appcompat:appcompat:1.4.1'implementation 'com.google.android.material:material:1.5.0'implementation 'androidx.constraintlayout:constraintlayout:2.1.3'testImplementation 'junit:junit:4.13.2'androidTestImplementation 'androidx.test.ext:junit:1.1.3'androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'implementation 'androidx.activity:activity-ktx:1.5.1'implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.5.1"implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1"implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.5.1"implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0"// 工具类implementation "com.blankj:utilcodex:1.30.0"// USB管理implementation 'me.jahnen.libaums:core:0.10.0'

2. Mainfest配置

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"><uses-permission android:name="android.permission.USB_PERMISSION" /><uses-feature android:name="android.hardware.usb.host" /><uses-permissionandroid:name="android.hardware.usb.host"android:required="true" /><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /><uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /><!--Android 13 权限适配--><uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" /><applicationandroid:name=".MainApp"android:allowBackup="true"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:requestLegacyExternalStorage="true"android:supportsRtl="true"android:theme="@style/Theme.AndroidProGuard"><activityandroid:name=".MainActivity"android:exported="true"><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity></application></manifest>

MainApp

class MainApp : Application() {override fun onCreate() {super.onCreate()Utils.init(this)}
}

3. 读写Usb的文件

1. 先获取Usb文件系统列表

    /*** 获取USB文件系统列表*/private fun getUsbFileSystem(): Array<FileSystem>? {// 判断是否有文件权限if (!hasFilePermission()) {requestFilePermission()return null}// 是否插入了USB设备val devices = UsbMassStorageDevice.getMassStorageDevices(this)if (devices.isEmpty()) {ToastUtils.showShort("没有插入USB存储设备")return null}// 判断是否有USB权限if (!hasUsbPermission(devices)) {requestUsbPermission(devices)return null}// 获取USB文件系统return devices.map {it.init()it.partitions[0].fileSystem}.toTypedArray()}/*** 是否有文件权限*/private fun hasFilePermission(): Boolean {return when {Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> // Android 10以上return Environment.isExternalStorageManager()Build.VERSION.SDK_INT >= Build.VERSION_CODES.M -> // Android 6以上PermissionUtils.isGranted(Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.READ_EXTERNAL_STORAGE)else -> true}}/*** 请求文件权限*/private fun requestFilePermission() {when {Build.VERSION.SDK_INT >= Build.VERSION_CODES.R -> {// Android 10以上if (!Environment.isExternalStorageManager()) {AlertDialog.Builder(this).setTitle("提示").setMessage("请前往开启文件访问权限,否则无法使用此功能!").setNegativeButton("取消") { dialog, _ ->dialog.dismiss()}.setPositiveButton("前往") { dialog, _ ->dialog.dismiss()startActivity(Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION))}.create().show()}}Build.VERSION.SDK_INT >= Build.VERSION_CODES.M -> {// Android 6以上if (!PermissionUtils.isGranted(Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.READ_EXTERNAL_STORAGE)) {PermissionUtils.permission(Manifest.permission.WRITE_EXTERNAL_STORAGE,Manifest.permission.READ_EXTERNAL_STORAGE).callback { isAllGranted, _, _, _ ->if (!isAllGranted) {ToastUtils.showShort("没有开启文件权限")}}.request()}}else -> {}}}/*** 是否有USB权限*/private fun hasUsbPermission(devices: Array<UsbMassStorageDevice>): Boolean {val usbManager = getSystemService(Context.USB_SERVICE) as UsbManagerfor (device in devices) {if (!usbManager.hasPermission(device.usbDevice)) {return false}}return true}/*** 请求USB权限*/private fun requestUsbPermission(devices: Array<UsbMassStorageDevice>) {val permissionIntentFlag = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {PendingIntent.FLAG_IMMUTABLE} else {PendingIntent.FLAG_ONE_SHOT}val permissionIntent = PendingIntent.getActivity(this,0,Intent(ACTION_USB_PERMISSION),permissionIntentFlag)val usbManager = getSystemService(Context.USB_SERVICE) as UsbManagerdevices.forEach {usbManager.requestPermission(it.usbDevice, permissionIntent)}}

2. 获取到的Usb文件系统进行读写操作(ViewModel中处理)

2.1. 从USB拷贝文件到内置卡

	private val sBufferSize = 524288/*** 从USB拷贝文件到内置卡*/fun copyFileFromUsb(devices: Array<FileSystem>) {flow {if (devices.isEmpty()) {throw IllegalStateException("没有插入USB存储设备")}val sdPath = getExternalStorageDirectory()if (sdPath.isNullOrEmpty()) {throw IllegalStateException("没有文件读取权限")}val fileSystem = devices[0]val newFile = File(sdPath, "demo.jpeg")var copySuccess = falsefor (childFile in fileSystem.rootDirectory.listFiles()) {if (childFile.name == "demo.jpeg") {// 测试文件copySuccess = copyUsbFile(newFile, childFile)}}emit(copySuccess)}.flowOn(Dispatchers.IO).catch {ToastUtils.showShort("拷贝错误:$it")}.onEach {ToastUtils.showShort("拷贝${if (it) "成功" else "失败"}")}.launchIn(viewModelScope)}/*** 手机内置目录*/private fun getExternalStorageDirectory(): String? {val extFileStatus = Environment.getExternalStorageState()val extFile = Environment.getExternalStorageDirectory()if (extFileStatus == Environment.MEDIA_MOUNTED && extFile.exists() && extFile.isDirectory&& extFile.canWrite()) {return extFile.absolutePath}return null}/*** 拷贝文件*/private fun copyUsbFile(newFile: File, child: UsbFile): Boolean {var out: OutputStream? = nullvar inputStream: InputStream? = nulltry {FileUtils.createOrExistsFile(newFile)out = BufferedOutputStream(FileOutputStream(newFile))inputStream = UsbFileInputStream(child)val bytes = ByteArray(sBufferSize)var count: Intvar total: Long = 0while (inputStream.read(bytes).also { count = it } != -1) {out.write(bytes, 0, count)total += count.toLong()}} catch (e: Exception) {e.printStackTrace()return false} finally {try {out?.close()} catch (e: IOException) {e.printStackTrace()}try {inputStream?.close()} catch (e: Exception) {e.printStackTrace()}}return true}

2.2. 读取Usb文件的内容

 private val sBufferSize = 524288/*** 读取文件*/fun readFile(devices: Array<FileSystem>) {flow {if (devices.isEmpty()) {throw IllegalStateException("没有插入USB存储设备")}val fileSystem = devices[0]var text = ""for (childFile in fileSystem.rootDirectory.listFiles()) {if (childFile.name == "demo.txt") {// 测试文件text = readFile2String(childFile) ?: ""}}emit(text)}.flowOn(Dispatchers.IO).catch {ToastUtils.showShort("读取错误:$it")}.onEach {ToastUtils.showShort("读取内容:$it")}.launchIn(viewModelScope)}private fun readFile2String(file: UsbFile): String? {val bytes = readFile2BytesByStream(file) ?: return nullreturn String(bytes)}private fun readFile2BytesByStream(file: UsbFile): ByteArray? {return try {var os: ByteArrayOutputStream? = nullval usbFis: InputStream = UsbFileInputStream(file)try {os = ByteArrayOutputStream()val b = ByteArray(sBufferSize)var len: Intwhile (usbFis.read(b, 0, sBufferSize).also { len = it } != -1) {os.write(b, 0, len)}os.toByteArray()} catch (e: IOException) {e.printStackTrace()null} finally {try {usbFis.close()} catch (e: IOException) {e.printStackTrace()}try {os?.close()} catch (e: IOException) {e.printStackTrace()}}} catch (e: FileNotFoundException) {e.printStackTrace()null}}

2.3. 在Usb写入文件

 /*** 写入文件*/fun writeFile(devices: Array<FileSystem>) {flow {if (devices.isEmpty()) {throw IllegalStateException("没有插入USB存储设备")}val fileSystem = devices[0]val newFile = fileSystem.rootDirectory.createFile("hello.txt")val os = UsbFileOutputStream(newFile)os.write("Hello World".toByteArray())os.close()emit(true)}.flowOn(Dispatchers.IO).catch {ToastUtils.showShort("写入错误:$it")}.onEach {ToastUtils.showShort("写入${if (it) "成功" else "失败"}")}.launchIn(viewModelScope)}

2.4. 内置卡文件拷贝到Usb存储

/*** 拷贝文件到USB*/fun copyFileToUsb(devices: Array<FileSystem>) {flow {if (devices.isEmpty()) {throw IllegalStateException("没有插入USB存储设备")}val sdPath = getExternalStorageDirectory()if (sdPath.isNullOrEmpty()) {throw IllegalStateException("没有文件读取权限")}val fileSystem = devices[0]val baseFile = File(sdPath, "Hello.jpg")val root = fileSystem.rootDirectoryemit(copyFileToUsb(baseFile, root))}.flowOn(Dispatchers.IO).catch {ToastUtils.showShort("拷贝错误:$it")}.onEach {ToastUtils.showShort("拷贝${if (it) "成功" else "失败"}")}.launchIn(viewModelScope)}/*** 手机根目录*/private fun getExternalStorageDirectory(): String? {val extFileStatus = Environment.getExternalStorageState()val extFile = Environment.getExternalStorageDirectory()if (extFileStatus == Environment.MEDIA_MOUNTED && extFile.exists() && extFile.isDirectory&& extFile.canWrite()) {return extFile.absolutePath}return null}private fun copyFileToUsb(baseFile: File, root: UsbFile): Boolean {if (!baseFile.exists()) return falsevar out: OutputStream? = nullvar inputStream: InputStream? = nulltry {val newUsbFile = root.createFile(baseFile.name)inputStream = FileInputStream(baseFile)out = BufferedOutputStream(UsbFileOutputStream(newUsbFile))val bytes = ByteArray(sBufferSize)var count: Intvar total: Long = 0while (inputStream.read(bytes).also { count = it } != -1) {out.write(bytes, 0, count)total += count.toLong()}} catch (e: Exception) {e.printStackTrace()return false} finally {try {out?.close()} catch (e: IOException) {e.printStackTrace()}try {inputStream?.close()} catch (e: Exception) {e.printStackTrace()}}return true}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/5344.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

02-基础入门-数据包拓展

基础入门-数据包拓展 基础入门-数据包拓展1、http/https数据包&#xff08;1&#xff09;HTTP协议是什么&#xff1f;&#xff08;2&#xff09;HTTP原理&#xff08;3&#xff09;HTTP特点&#xff08;4&#xff09;URI和URL的区别&#xff08;5&#xff09;HTTP报文组成&…

数据安全服务,美创科技为“数字国贸” 筑牢安全防线

在数字经济蓬勃发展的当下&#xff0c;国有企业作为国民经济的“中流砥柱”&#xff0c;正以主力军和先行者之姿&#xff0c;以数字化转型创新作为引擎&#xff0c;驱动高质量发展。数字化进程持续深入&#xff0c;伴随数据要素多样流动&#xff0c;降低数据安全风险&#xff0…

【ES三周年】| 基于国产化操作系统搭建ELK日志分析平台

引入 鲲鹏认证-Kylin麒麟操作系统-ELK日志分析平台 开篇 何为ELK Stack&#xff1f;它又能够给我们带来什么&#xff1f; 综述 ELK为三个开源项目的首字母缩写&#xff0c;分别对应是&#xff1a;Elasticsearch、Logstash、Kibana&#xff0c;由这三个软件及其相关的组件可…

互联网+洗鞋店预约小程序新模式;

互联网洗鞋店预约小程序 1、线上线下业务的结合。 传统的线下业务消费者到店可以向其推介线上的预约到家服务&#xff0c;让线下的消费者成为小程序内的会员&#xff0c;留存客户之后线上可直接触达&#xff0c;减少与消费者的距离&#xff0c;从等待客户到可以主动出击&…

css内阴影

CSS内阴影及特定方向内阴影 基本参数&#xff1a; /* x 偏移量 | y 偏移量 | 阴影颜色 */ box-shadow: 60px -16px teal;/* x 偏移量 | y 偏移量 | 阴影模糊半径 | 阴影颜色 */ box-shadow: 10px 5px 5px black;/* x 偏移量 | y 偏移量 | 阴影模糊半径 | 阴影扩散半径 | 阴影…

5.7.2 UDP协议格式(一)——UDP差错控制

5.7.2 UDP协议格式&#xff08;一&#xff09;——UDP差错控制 前面我们学习了UDP数据报格式&#xff08;5.7.1 UDP概述&#xff09;我们知道UDP只是在IP数据报服务基础上增加了端口的复用和分用功能&#xff0c;以及差错控制的功能&#xff0c;这里我们就一起来学习一下UDP的…

【UE5 Cesium】03-Cesium for Unreal 添加本地数据集

上一篇&#xff1a;【UE5 Cesium】02-Cesium for Unreal 添加在线数据集 步骤 1. 在官网&#xff08;Adding Datasets – Cesium&#xff09;上下载一个示例 下载的是一个名为“Tileset.zip”的压缩文件 解压后文件内容如下 2. 打开虚幻编辑器&#xff0c;点击“Blank 3D Tiles…

基于SpringBoot的在线拍卖系统【附ppt和万字文档(Lun文)和搭建文档】

主要功能 主要功能 前台登录&#xff1a; ①首页&#xff1a;轮播图、竞拍公告、拍卖商品展示 ②拍卖商品&#xff1a;分类&#xff1a;手机、数码、电器等&#xff0c;可以点击商品竞拍 ③竞拍公告&#xff1a;可以查看竞拍的信息 ④留言反馈&#xff1a;用户可以提交留言 ⑤…

Windows同时安装两个版本JDK,并实现动态切换

1、载安装两个版本的JDK 安装后&#xff0c;默认路径C:\Program Files\Java。 实际上JDK8有两个包一个jdk1.8.0_311&#xff0c;一个jre1.8.0_311。 JDK11只有一个jdk-11.0.16.1。 2、系统环境配置 设置JAVA_HOME 在环境变量中选中Path&#xff0c;点击编辑 点击新建&…

kubernetes核心概念 service

kubernetes核心概念 Service 一、 service作用 使用kubernetes集群运行工作负载时&#xff0c;由于Pod经常处于用后即焚状态&#xff0c;Pod经常被重新生成&#xff0c;因此Pod对应的IP地址也会经常变化&#xff0c;导致无法直接访问Pod提供的服务&#xff0c;Kubernetes中使…

Verilog基础之十三、ROM实现

目录 一、前言 二、非IP核设计 2.1 工程设计文件读取初始化 2.2 测试代码 2.3 仿真结果 三、IP核创建ROM 3.1 IP核生成ROM 3.2 设计代码 3.3 测试代码 3.4 仿真结果 四、modelsim设置 4.1 模拟信号显示 4.2 信号范围显示设置 五、数据文件 一、前言 对于工程中的…

HashMap源码分析

文章目录 1、put方法流程2 、扩容机制3 、get方法 分析源码我们一般从三个方面入手&#xff1a; 常见属性&#xff08;成员变量&#xff09;构造方法关键方法 下面分析一下HashMap源码&#xff1a; 首先常见属性有&#xff1a; DEFAULT_INITIAL_CAPACITY 1 << 4; // a…