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盘,解决写入不完整的问题的主要内容,如果未能解决你的问题,请参考以下文章

macbook如何解决优盘无法写入的问题

U盘删除文件时提示“文件或目录损坏且无法读取”的解决方法

img文件写入U盘后,怎么恢复原来的文件?

Mac不能复制拷贝写入文件到移动硬盘,U盘怎么办

u盘启动出现grub应该怎么解决

解决linux下U盘变成只读模式