Android L 拷贝文件到U盘异常问题

前言:在高通8916平台,通过自带的文件管理器拷贝文件到U盘,显示完成后拔除U盘,在电脑上发现文件有问题,或是为0b或是打不开或是大小异常。如果正常通过设置中的移除U盘就没有这个问题,故问题定位在热插拔异常。

首先我来解释下为何会出现这种问题,基于linux, write操作和read操作有一点是不同的:
read操作需要从磁盘上读取到用户需的有效数据才算结束。
而write则只需要把数据写入内存cache就返回了,所以当copy完成后,数据还位于缓存中,还没有写入磁盘。
这样做的目的是为了提高write效率,先写入缓存,后台write back再会将数据写入磁盘(与之相对的是称之为Direct IO的跳过缓存的写操作,但是会write速率比较慢)
这种机制是为了performance是不会改变的,所以这就是为何不提倡热插拔磁盘。

问题找到了,那该如何解决呢?
实时刷新、写入io流。
高通自带文件管理器相关代码:

FileHelper.java
/**
     * Method that copies a file
     *
     * @param src The source file
     * @param dst The destination file
     * @param bufferSize The buffer size for the operation
     * @return boolean If the operation complete successfully
     */
    public static boolean bufferedCopy(final File src, final File dst,
        int bufferSize, Program program)
            throws ExecutionException, CancelledOperationException {
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;
        FileInputStream fis = null;
        FileOutputStream fos = null;
        FileDescriptor fd = null;
        try {
            fis = new FileInputStream(src);
            fos = new FileOutputStream(dst);
            fd = fos.getFD();
            bis = new BufferedInputStream(fis, bufferSize);
            bos = new BufferedOutputStream(fos, bufferSize);
            int read = 0;
            byte[] data = new byte[bufferSize];
            while ((read = bis.read(data, 0, bufferSize)) != -1) {
                // Short circuit if we've been cancelled. Show's over :(
                if (program.isCancelled()) {
                    throw new CancelledOperationException();
                }
                bos.write(data, 0, read);
            }
            bos.flush(); // flush data from the stream into the buffer
            fd.sync();  // confirms data to be written to the disk
            return true;

        } catch (Throwable e) {
            Log.e(TAG,
                    String.format(TAG, "Failed to copy from %s to %d", src, dst), e); //$NON-NLS-1$

            // Check if this error is an out of space exception and throw that specifically.
            // ENOSPC -> Error No Space
            if (e.getCause() instanceof ErrnoException
                        && ((ErrnoException)e.getCause()).errno == OsConstants.ENOSPC) {
                throw new ExecutionException(R.string.msgs_no_disk_space);
            } if (e instanceof CancelledOperationException) {
                // If the user cancelled this operation, let it through.
                throw (CancelledOperationException)e;
            }

            return false;
        } finally {
            try {
                if (bis != null) {
                    bis.close();
                }
            } catch (Throwable e) {/**NON BLOCK**/}
            try {
                if (bos != null) {
                    bos.close();
                }
            } catch (Throwable e) {/**NON BLOCK**/}
            try {
                if (fis != null) {
                    fis.close();
                }
            } catch (Throwable e) {/**NON BLOCK**/}
            try {
                if (fos != null) {
                    fos.close();
                }
            } catch (Throwable e) {/**NON BLOCK**/}
        }
    }

fd.sync();同步写入即可。
在所有要write的数据写完后,执行sync命令,相当于将缓存内容强制刷入磁盘。
副作用:
首先sync是系统提供的,没有优化办法。
这样做从原来系统在后台sync(写操作会迅速返回), 变成了前台去强制sync(sync完才返回)。
好处是,sync完返回,可以保证数据安全性,不让用户在sync完之前拔掉U盘。
坏处是,用户可能需要多等一段时间。
这是一个策略问题,需要权衡。

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值