安卓设备通过USB与外设通信有两种形式(无需ROOT):

  1. 与设备之间相互发送命令:用串口通信比较多,建议在github搜索felHR85,使用串口通信的前提是,外设支持串口通信且有串口通信的协议,外设不是单纯的存储设备

  2. 只读取设备的文件,有时需要删除文件:

    1. 方法一:通过USB挂载外设到文件系统,然后获取USB权限,把USB设备映射为存储设备,可以在GitHub上搜索libaums框架或者Usb Mass Storage for Android框架,前者是收藏最多的USB存储框架,后者是比较简单好用的框架。两者的实现原理相同。
    2. 方法二:有的USB设备不能完成上面两个框架所必须的初始化,比如我在初始化时遇到了claim interface failed的报错,这个报错是由native方法:native_claim_interface(int interfaceID, boolean force)抛出。从网上是找不到解决方案的,估计是设备不兼容。这时候直接打开文件管理,看USB设备是不是能在文件管理中直接看到,如果可以,我们可以认为它其实已经挂载了,所以libaums框架或者Usb Mass Storage for Android框架可能并不适用。我们直接把它当成SD卡的文件进行读取,实际上也支持删除文件。

    下面介绍方法一和方法二的使用

    如果你尝试了方法一行不通,可以转为使用方法二。两者都需要注册一个广播监听USB的插入和拔出,方法一还需要监听USB权限的获取结果:

    public class UsbReceiver extends BroadcastReceiver {private static final String TAG = "UsbReceiver";public static final String ACTION_USB_PERMISSION = "ACTION_USB_PERMISSION";@Overridepublic void onReceive(Context context, Intent intent) {String action = intent.getAction();switch (action) {case UsbManager.ACTION_USB_DEVICE_ATTACHED:Log.d(TAG, "onReceive: USB_DEVICE_ATTACHED");break;case UsbManager.ACTION_USB_DEVICE_DETACHED:Log.d(TAG, "onReceive: USB_DEVICE_DETACHED");break;case ACTION_USB_PERMISSION://方法二不需要Log.d(TAG, "onReceive: ACTION_USB_PERMISSION");break;}}
    }
    

    注册这个广播:

    private void initUsbReceiver() {mUsbReceiver = new UsbReceiver();//注册广播,监听USB插入和拔出IntentFilter intentFilter = new IntentFilter();intentFilter.addAction(UsbManager.ACTION_USB_DEVICE_ATTACHED);intentFilter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);intentFilter.addAction(UsbReceiver.ACTION_USB_PERMISSION);registerReceiver(mUsbReceiver, intentFilter);
    }
    ​@Overrideprotected void onDestroy() {super.onDestroy();unregisterReceiver(mUsbReceiver);}
    

    这是动态注册广播的方式,不需要在manifest文件再注册了。

    方法一详细使用:

1.申请存取权限:
  1. 1.github搜索并集成上述两种框架的一种

  2. 在manifest中加入:

    <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" />

USB_PERMISSION就是后续需要在代码中动态请求的USB权限,我们知道现在安卓设备基本都需要动态请求权限了。

android:required="true"的意思是,如果安卓设备不支持USB权限,APP直接不能安装在这个设备上。

public void checkPermission() {List<String> ps = new ArrayList<>();if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {ps.add(Manifest.permission.READ_EXTERNAL_STORAGE);}if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {ps.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);}if (ps.size() > 0) {String[] pers = new String[ps.size()];for (int i = 0; i < ps.size(); i++) {pers[i] = ps.get(i);}ActivityCompat.requestPermissions(this, pers, REQUEST_PERMISSION_CODE);}
}
​
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {super.onRequestPermissionsResult(requestCode, permissions, grantResults);if (requestCode == REQUEST_PERMISSION_CODE) {for (int i = 0; i < permissions.length; i++) {Log.i("MainActivity", "申请的权限为:" + permissions[i] + ",申请结果:" + grantResults[i]);}} else {if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {} else {Toast.makeText(this, "需要读写权限来保存并上传数据!", Toast.LENGTH_SHORT).show();}}
}
2.USB读取和使用
//三个全局变量
private UsbDevice mDevice;
private UsbDeviceConnection mConnection;
private PendingIntent mPermissionIntent;
​
//初始化
private void init() {mUsbManager = (UsbManager) getContext().getSystemService(Context.USB_SERVICE);mPermissionIntent = PendingIntent.getBroadcast(getContext(), 0, new Intent(UsbReceiver.ACTION_USB_PERMISSION), PendingIntent.FLAG_IMMUTABLE);
}
​
//请求权限和读取,使用
private void test() {try {HashMap<String, UsbDevice> list = mUsbManager.getDeviceList();Iterator<UsbDevice> iterator = list.values().iterator();while (iterator.hasNext()) {mDevice = iterator.next();// 申请USB权限,权限申请成功后,会收到上面注册的广播,然后需再调用test()方法if (!mUsbManager.hasPermission(mDevice)) {mUsbManager.requestPermission(mDevice, mPermissionIntent);return;}}//Usb Mass Storage for Android框架举例mConnection = mUsbManager.openDevice(mDevice);VirtualFileSystem fileSystem = new VirtualFileSystem(mDevice, mConnection);if (fileSystem.mount(0)) { //这里如果报错,可能要考虑更换方法二Toast.makeText(getContext(), "挂载成功", Toast.LENGTH_LONG).show();}for (VFSFile file : fileSystem.listFiles()) {Log.d(TAG, "test: file : " + file);//在这里对文件进行操作}
​} catch (Exception e) {Log.d(TAG, "test: " + e.getMessage());}
}
​

方法二使用详解:

如果方法一行不通,看插上USB设备后,能否在文件管理中看到,如果可以的话,直接把USB设备当着SD卡,用方法二进行操作:

1.申请存取权限:

在manifest中加入:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /><uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

动态申请权限:

public void checkPermission() {List<String> ps = new ArrayList<>();if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {ps.add(Manifest.permission.READ_EXTERNAL_STORAGE);}if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {ps.add(Manifest.permission.WRITE_EXTERNAL_STORAGE);}if (ps.size() > 0) {String[] pers = new String[ps.size()];for (int i = 0; i < ps.size(); i++) {pers[i] = ps.get(i);}ActivityCompat.requestPermissions(this, pers, REQUEST_PERMISSION_CODE);}
}
​
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {super.onRequestPermissionsResult(requestCode, permissions, grantResults);if (requestCode == REQUEST_PERMISSION_CODE) {for (int i = 0; i < permissions.length; i++) {Log.i("MainActivity", "申请的权限为:" + permissions[i] + ",申请结果:" + grantResults[i]);}} else {if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {} else {Toast.makeText(this, "需要读写权限来保存并上传数据!", Toast.LENGTH_SHORT).show();}}
}
2.开始文件操作

安卓的文件操作要求越来越严格,我建议不要使用添加

android:preserveLegacyExternalStorage="true"
android:requestLegacyExternalStorage="true"

或者修改targetsdk = 29等骚操作来规避安卓文件安全要求,这是种迟早过时的办法,有的手机也已经没办法规避了。

建议使用安卓官方推荐的SAF框架进行文件存取:

SAF包含一个文档提供程序(Document provider) ,这个文档提供程序包含了USB:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-twecfElt-1660053575217)(https://upload-images.jianshu.io/upload_images/27762813-90cabc4bffd1c9bc.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)]

让我们能够跳转到文档选择界面,选择自己要操作的文档,然后返回它的URI,并通过ContentResolver对其进行操作。

选择文件:
    private static final int ACTIVITY_CHOOSE_FILE = 689;public void onBrowse() {Intent chooseFile;chooseFile = new Intent(Intent.ACTION_OPEN_DOCUMENT);//chooseFile = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE); //选择文件夹chooseFile.addCategory(Intent.CATEGORY_OPENABLE);//设置自己所需文件的MIME类型chooseFile.setType("application/octet-stream"); startActivityForResult(chooseFile, ACTIVITY_CHOOSE_FILE);}
操作文件
    @RequiresApi(api = Build.VERSION_CODES.Q)public void onActivityResult(int requestCode, int resultCode, Intent data) {if (resultCode != RESULT_OK) return;String path = "";if (requestCode == ACTIVITY_CHOOSE_FILE) {Uri uri = data.getData();String fileName = uri.getPath().split(":")[1];File file = uriToFileApiQ(uri, fileName, getContext());if (file.exists()) {Log.d(TAG, "onActivityResult: file exists");}System.out.print("Path  = " + file);}}​//URI转File@RequiresApi(api = Build.VERSION_CODES.Q)public static File uriToFileApiQ(Uri uri, String fileName, Context context) {File file = null;if (uri == null) return file;//android10以上转换if (uri.getScheme().equals(ContentResolver.SCHEME_FILE)) {file = new File(uri.getPath());} else if (uri.getScheme().equals(ContentResolver.SCHEME_CONTENT)) {//把文件复制到沙盒目录,我们对沙盒目录的文件有权限进行任何操作,也可以用DocumentFile.fromSingleUri来获取一个DocumentFile对象来做操作,但这个对象不是File的子类!ContentResolver contentResolver = context.getContentResolver();try {InputStream is = contentResolver.openInputStream(uri);File cache = new File(context.getCacheDir().getAbsolutePath(), fileName);FileOutputStream fos = new FileOutputStream(cache);FileUtils.copy(is, fos);file = cache;fos.close();is.close();} catch (IOException e) {e.printStackTrace();}DocumentFile.fromSingleUri(context, uri).delete();//可以删除文件}return file;}

安卓通过USB存储文件技术的两种方法-USB框架和SAF框架相关推荐

  1. android查ip地址,安卓手机查看IP地址的两种方法,  二、进入手机状态

    安卓手机查看IP地址的两种方法, 二.进入手机状态 无论是电脑链接宽带上网还是手机链接wifi上网,当我们在链接网络后,会自动分配一个独立的IP地址,对于电脑端的上网IP地址我们可通过ipconfig ...

  2. android ip查看工具,安卓手机查看IP地址的两种方法

    无论是电脑链接宽带上网还是手机链接wifi上网,当我们在链接网络后,会自动分配一个独立的IP地址,对于电脑端的上网IP地址我们可通过ipconfig/all命令来查看,那么如果在手机端的话应该如何查看 ...

  3. 开发板通过usb连linux,虚拟机Linux系统和开发板通过USB转串口连接的两种方法

    通过USB转串口连接虚拟机中的Linux系统和开发板,开始时候总是出现错误,显示什么串口重叠,最后求助高手帮我通过其他连接方式解决了问题,今天在网上查找资料,终于明白了开始错误的原因:开发板提供的驱动 ...

  4. JAVA获取安卓系统下usb_Android 获取 usb 权限的两种方法

    前言: 最近工作上遇到几个USB模块在android平台上适配使用的情况,所以要用到USB权限获取问题 ##USB权限获取有以下2种方式: 一.直接在AndroidManifest.xml文件中进行如 ...

  5. 两种禁止USB autosuspend的方法

    在有些情况下,由于低功耗情况下,USB硬件的bug或者提高USB resume的速度,我们需要禁止USB的 autosuspend,下面是两种方法. 1: 在bootloader的bootargs里加 ...

  6. 禁用usb的两种方法

    禁用usb的两种方法: https://www.cnblogs.com/iyangyuan/archive/2012/07/16/2801825.html 都不完美,一个不生效,一个要打开配置文件来控 ...

  7. 【vmware虚拟机上网教程】虚拟机如何上网?两种方法简单易学(安卓Android x86虚拟机演示)

    [vmware虚拟机上网教程]虚拟机如何上网?两种方法简单易学(安卓Android x86虚拟机演示) 启动安卓虚拟机,点击打开虚拟机,然后找到虚拟机的文件. 虚拟机列表中有一个开启此虚拟机的按钮.点 ...

  8. httos双向认证配置_APP爬虫双向认证抓包的两种方法

    APP抓包相对繁琐,越来越多的 APP 在 https 请求和响应时,为了防止中间人攻击(或中间人抓包),会做证书认证,让抓包工具抓不到请求.证书认证分单向认证和双向认证,双向认证是相较于单向认证而言 ...

  9. 华为n3计算机在哪里,在华为nova3i中连接电脑的两种方法介绍

    大家知道怎么在华为nova3i中连接电脑吗?不知道没有关系,小编今天介绍在华为nova3i中连接电脑的两种方法,希望可以帮助到你哦. 在华为nova3i中连接电脑的两种方法介绍 方法一: 1.下载并且 ...

最新文章

  1. 如何从JavaScript中的给定数字中形成最小的数字
  2. JSP标签和JSTL标签注意点
  3. matlab 03d,randomforest-matlab 一个 实现 的源代码,里面有使用说明和范例 Data Mining 数据挖掘 242万源代码下载- www.pudn.com...
  4. 同比增长19.1%,软银第一季度净利2542亿日元
  5. 图像处理(8) : 模板匹配
  6. React 实现 PDF 文件在线预览 - 手把手教你写 React PDF 预览功能
  7. 汇川小型PLC-MODBUS(485)通讯模式
  8. 互联网网站的反爬虫策略浅析
  9. [K.O.]安装iTunes提示此Windows Installer软件包有一个问题…!
  10. CAE软件技术现状调研
  11. HTML+CSS知识点总结(一)
  12. C#模拟union接收
  13. Python文件操作错误:OSError: [Errno 22] Invalid argument(关于Windows下文件名中的敏感字符)
  14. QML之gradient
  15. 从虚拟化前端Bug学习分析Kernel Dump
  16. 安卓手机版微信聊天加密软件 悬浮窗版本
  17. ProxySQL 配置详解及读写分离(+GTID)等功能说明 (完整篇)
  18. Cubase Pro 10.0.50 PC 完整版音乐制作宿主软件下载
  19. 同步辐射X射线断层扫描成像在各行业的应用
  20. 这篇数据库设计规范建议,我必须分享给你

热门文章

  1. C++ memset()函数用法
  2. python --图像转素描图
  3. 10. hr 综合面试题汇总
  4. 解决电脑自带office删除之后不能下载或者找到正版office的问题
  5. 【学习笔记】数理统计习题十
  6. 蚂蚁借呗超过20次就会影响贷款吗?
  7. 一周新书榜:西瓜书伴侣、Python编程快速上手第2版上榜
  8. 手机熔丝版本简单介绍
  9. 关于APP上传到苹果商店的详细步骤
  10. Linux性能监控的常用工具/命令