Android实现操作U盘,解决写入不完整的问题
Posted Wastrel_xyz
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android实现操作U盘,解决写入不完整的问题相关的知识,希望对你有一定的参考价值。
因为项目需要,APP要把数据导出到U盘中,下文将介绍下android5.0以上怎么操作U盘。重点就是写入文件之后,必须调用内核文件同步函数,否则可能存在写入不完全的问题.这里不详叙DocumentFile相关的操作,网上已经有很多了.
获取外部存储
private List<StorageVolume> getVolume()
StorageManager manager = (StorageManager) getSystemService(Context.STORAGE_SERVICE);
if (manager != null)
// 过滤掉不符合需求的设备,USB设备肯定是可移动的,这里注意存储卡也会出来 最好是用对话框来提示用户要操作那个存储设备
return ListUtils.filiter(Utils.getVolume(manager, mActivity), it -> Utils.compatStorageRemoveable(it));
return null;
DocumentFile授权
private void requestUri(String uuid)
//记住授权的ID,授权之前建议用界面引导用户如何授权,授权界面是Android自带的,程序不可控!
currentUUID = uuid;
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE);
startActivityForResult(intent, DOCUMENT_TREE_REQUEST);
授权之后保存权限
@TargetApi(Build.VERSION_CODES.KITKAT)
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data)
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK)
if (requestCode == DOCUMENT_TREE_REQUEST)
Uri treeUri;
//获取授权之后的Uri
treeUri = data.getData();
if (treeUri != null)
String value = treeUri.toString();
String uuid = value.substring(value.lastIndexOf("/") + 1).replace("%3A", "");
//判断用户选的设备是否是我们想要的设备!
if (uuid.equals(currentUUID))
//将URL存起来 后续访问时可以直接用
PreferencesUtils.put(ACTION_OPEN_DOCUMENT_TREE_URL + uuid, value);
final int takeFlags = data.getFlags()
& (Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
//固化授权信息,下次该设备插入次APP就无需授权
getContentResolver().takePersistableUriPermission(treeUri, takeFlags);
else
appendMessage(new Message("您选择的不是移动存储设备的根目录或不是该移动存储设备!", CallBack.ERROR));
读取文件
DocumentFile file = Utils.findPath(DocumentFile.fromTreeUri(mActivity, Uri.parse(uri)), "xj_data/" + fileName);
//拿到流了就不需要我多说什么了吧
InputStream inputStream = getContext().getContentResolver().openInputStream(file.getUri());
写入文件
//与读取文件差不多,先通过DocumentFile的相关方法找到文件,然后打开输出流,把流写进去就行
OutputStream outputStream =context.getContentResolver().openOutputStream(dataFile.getUri());
...
//流写完了必须调用内核方法强制sync到设备上,不然拔快了,文件还没有同步到外部存储设备上
((ParcelFileDescriptor.AutoCloseOutputStream) outputStream).getFD().sync();
操作工具类代码
public class Utils
public static DocumentFile findPath(DocumentFile root, String path)
String[] split = path.split(File.separator);
if (!root.exists())
return null;
else
for (String s : split)
if (TextUtils.isEmpty(s))
continue;
DocumentFile file = root.findFile(s);
if (file == null || !file.exists())
return null;
else
root = file;
return root;
/**
* 获取存储设备的UUID
*/
public static String compatStorageUUID(StorageVolume volume)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
return volume.getUuid();
else
try
Class<?> storageVolumeClazz = Class.forName("android.os.storage.StorageVolume");
Method uuid = storageVolumeClazz.getDeclaredMethod("getUuid");
uuid.setAccessible(true);
return (String) uuid.invoke(volume);
catch (Exception e)
return null;
/**
* 获取存储设备是否可移动
*/
public static boolean compatStorageRemoveable(StorageVolume volume)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
return volume.isRemovable();
else
try
Class<?> storageVolumeClazz = Class.forName("android.os.storage.StorageVolume");
Method uuid = storageVolumeClazz.getDeclaredMethod("isRemovable");
uuid.setAccessible(true);
return (boolean) uuid.invoke(volume);
catch (Exception e)
return false;
/**
* 获取存储设备的描述
*/
public static String compatStorageDesc(StorageVolume volume, Activity activity)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
return volume.getDescription(activity);
else
try
Class<?> storageVolumeClazz = Class.forName("android.os.storage.StorageVolume");
Method desc = storageVolumeClazz.getMethod("getDescription", Context.class);
desc.setAccessible(true);
return (String) desc.invoke(volume, activity);
catch (Exception ignored)
return null;
/**
* 获取所有存储设备
*/
public static List<StorageVolume> getVolume(StorageManager manager, Context context)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
return manager.getStorageVolumes();
else
try
Method getVolume = StorageManager.class.getDeclaredMethod("getVolumeList", int.class, int.class);
StorageVolume[] invoke = (StorageVolume[]) getVolume.invoke(manager, getUserId(context), 0);
return Arrays.asList(invoke);
catch (Exception ignored)
return null;
private static int getUserId(Context context)
try
Method userId = Context.class.getDeclaredMethod("getUserId");
return (int) userId.invoke(context);
catch (Exception ignored)
return android.os.Process.myUid() / 100000;
以上是关于Android实现操作U盘,解决写入不完整的问题的主要内容,如果未能解决你的问题,请参考以下文章