安卓设备内部文件操作可以使用Android文件操作帮助类FileUtils
注意:
安卓设备若是外接OTG,向外部存储进行文件操作时有时会出现异常。排除权限原因,文件在向OTG进行文件复制粘贴或是删除时,当函数返回成功时,文件有时并未实际写入磁盘,现象就是:向外部设备复制一个文件,当提示文件复制成功时立即拔出OTG,再插上OTG发现刚复制成功的文件损坏,排除Android对OTG热插拔的支持问题。
热插拔OTG,导致刚复制的文件损坏的原因是:文件复制是有缓冲机制的,函数返回复制成功时,文件并未实际落入磁盘,感觉flush()/sync()并未起作用。
解决方法:

  1. 方法1.拔掉OTG,要求安全卸载外部存储设备,执行StorageManager . unmount();
  2. 方法2.使用NDK方式编译so,采用Linux open write 文件写入选择绕过缓冲区 直接写入。此种方法缺点:速度会慢很多,如果有大文件的复制操作(超过500M),等待时间会难以忍受。
src = open(srcPath, O_RDONLY | O_EXCL);
    dst= open(dstPath, O_RDWR | O_CREAT | O_SYNC | O_DIRECT);
   //源文件设置只读模式,若设置读写模式会导致:正在写的文件无法复制。
flag用于指定文件的打开/创建模式,这3个常量定义在fcntl.h中,这3个参数是必选的,而且只能选择一个:
 O_RDONLY      只读模式
 O_WRONLY      只写模式
 O_RDWR        读写模式
 下面的常量是可选的:
 O_DIRECT:     无缓冲的输入、输出。
 O_APPEND      每次写操作都写入文件的末尾。
 O_CREAT       如果指定文件不存在,则创建这个文件。如果存在则直接打开文件。如果创建新文件,而mode参数没有指定,则创建的文件权限不定。
 O_EXCL        如果文件不存在,则返回错误。如果同时指定了O_CREAT,而文件已经存在,则会出错。 用此测试一个文件是否存在,如果不存在,则创建此文件。
 O_TRUNC      如果文件存在,并且以只写/读写方式打开,则清空文件全部内容。
 O_NOCTTY     如果路径名指向终端设备,不要把这个设备用作控制终端。
 O_NONBLOCK   如果路径名指向 FIFO/块文件/字符文件,则把文件的打开和后继 I/O设置为非阻塞模式(nonblocking mode)。
 下面三个标志也是可选的,他们是Single UNIX Specification中同步输入和输出选项的一部分:
 O_DSYNC      等待物理 I/O 结束后再 write。在不影响读取新写入的数据的前提下,不等待文件属性更新。 
 O_RSYNC      read 等待所有写入同一区域的写操作完成后再进行。
 O_SYNC       等待物理 I/O 结束后再 write,包括更新文件属性的 I/O,以同步IO方式打开文件。


使用Android代码进行文件复制需要注意的几点:

  1. 文件复制结束后需要关闭文件流
  2. 文件复制结束后需要flush()
  3. 文件复制结束后需要out.getFD().sync();

以下是FileUtils中部分代码。

/**
     * Copy data from a source stream to destFile.
     * Return true if succeed, return false if failed.
     */
    public static boolean copyToFile(InputStream inputStream, File destFile) {
        try {
            if (destFile.exists()) {
                destFile.delete();
            }
            FileOutputStream out = new FileOutputStream(destFile);
            try {
                byte[] buffer = new byte[4096];
                int bytesRead;
                while ((bytesRead = inputStream.read(buffer)) >= 0) {
                    out.write(buffer, 0, bytesRead);
                }
            } finally {
                out.flush();
                try {
                    out.getFD().sync();
                } catch (IOException e) {
                }
                out.close();
            }
            return true;
        } catch (IOException e) {
            return false;
        }
    }