安卓通过USB存储文件技术的两种方法-USB框架和SAF框架
安卓设备通过USB与外设通信有两种形式(无需ROOT):
与设备之间相互发送命令:用串口通信比较多,建议在github搜索felHR85,使用串口通信的前提是,外设支持串口通信且有串口通信的协议,外设不是单纯的存储设备
只读取设备的文件,有时需要删除文件:
- 方法一:通过USB挂载外设到文件系统,然后获取USB权限,把USB设备映射为存储设备,可以在GitHub上搜索libaums框架或者Usb Mass Storage for Android框架,前者是收藏最多的USB存储框架,后者是比较简单好用的框架。两者的实现原理相同。
- 方法二:有的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.github搜索并集成上述两种框架的一种
在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框架相关推荐
- android查ip地址,安卓手机查看IP地址的两种方法, 二、进入手机状态
安卓手机查看IP地址的两种方法, 二.进入手机状态 无论是电脑链接宽带上网还是手机链接wifi上网,当我们在链接网络后,会自动分配一个独立的IP地址,对于电脑端的上网IP地址我们可通过ipconfig ...
- android ip查看工具,安卓手机查看IP地址的两种方法
无论是电脑链接宽带上网还是手机链接wifi上网,当我们在链接网络后,会自动分配一个独立的IP地址,对于电脑端的上网IP地址我们可通过ipconfig/all命令来查看,那么如果在手机端的话应该如何查看 ...
- 开发板通过usb连linux,虚拟机Linux系统和开发板通过USB转串口连接的两种方法
通过USB转串口连接虚拟机中的Linux系统和开发板,开始时候总是出现错误,显示什么串口重叠,最后求助高手帮我通过其他连接方式解决了问题,今天在网上查找资料,终于明白了开始错误的原因:开发板提供的驱动 ...
- JAVA获取安卓系统下usb_Android 获取 usb 权限的两种方法
前言: 最近工作上遇到几个USB模块在android平台上适配使用的情况,所以要用到USB权限获取问题 ##USB权限获取有以下2种方式: 一.直接在AndroidManifest.xml文件中进行如 ...
- 两种禁止USB autosuspend的方法
在有些情况下,由于低功耗情况下,USB硬件的bug或者提高USB resume的速度,我们需要禁止USB的 autosuspend,下面是两种方法. 1: 在bootloader的bootargs里加 ...
- 禁用usb的两种方法
禁用usb的两种方法: https://www.cnblogs.com/iyangyuan/archive/2012/07/16/2801825.html 都不完美,一个不生效,一个要打开配置文件来控 ...
- 【vmware虚拟机上网教程】虚拟机如何上网?两种方法简单易学(安卓Android x86虚拟机演示)
[vmware虚拟机上网教程]虚拟机如何上网?两种方法简单易学(安卓Android x86虚拟机演示) 启动安卓虚拟机,点击打开虚拟机,然后找到虚拟机的文件. 虚拟机列表中有一个开启此虚拟机的按钮.点 ...
- httos双向认证配置_APP爬虫双向认证抓包的两种方法
APP抓包相对繁琐,越来越多的 APP 在 https 请求和响应时,为了防止中间人攻击(或中间人抓包),会做证书认证,让抓包工具抓不到请求.证书认证分单向认证和双向认证,双向认证是相较于单向认证而言 ...
- 华为n3计算机在哪里,在华为nova3i中连接电脑的两种方法介绍
大家知道怎么在华为nova3i中连接电脑吗?不知道没有关系,小编今天介绍在华为nova3i中连接电脑的两种方法,希望可以帮助到你哦. 在华为nova3i中连接电脑的两种方法介绍 方法一: 1.下载并且 ...
最新文章
- 如何从JavaScript中的给定数字中形成最小的数字
- JSP标签和JSTL标签注意点
- matlab 03d,randomforest-matlab 一个 实现 的源代码,里面有使用说明和范例 Data Mining 数据挖掘 242万源代码下载- www.pudn.com...
- 同比增长19.1%,软银第一季度净利2542亿日元
- 图像处理(8) : 模板匹配
- React 实现 PDF 文件在线预览 - 手把手教你写 React PDF 预览功能
- 汇川小型PLC-MODBUS(485)通讯模式
- 互联网网站的反爬虫策略浅析
- [K.O.]安装iTunes提示此Windows Installer软件包有一个问题…!
- CAE软件技术现状调研
- HTML+CSS知识点总结(一)
- C#模拟union接收
- Python文件操作错误:OSError: [Errno 22] Invalid argument(关于Windows下文件名中的敏感字符)
- QML之gradient
- 从虚拟化前端Bug学习分析Kernel Dump
- 安卓手机版微信聊天加密软件 悬浮窗版本
- ProxySQL 配置详解及读写分离(+GTID)等功能说明 (完整篇)
- Cubase Pro 10.0.50 PC 完整版音乐制作宿主软件下载
- 同步辐射X射线断层扫描成像在各行业的应用
- 这篇数据库设计规范建议,我必须分享给你