Filedownloader: Why not use okio instead of RandomAccessFile in FileDownloader?

Created on 3 Sep 2016  ·  15Comments  ·  Source: lingochamp/FileDownloader

FileDownload 有用到okhttp,那么为什么不将java io替换成okio?
okio应该比java io来得更有效率吧?

enhancement need-info

Most helpful comment

这个问题,我验证了一些,网络IO和文件IO利用率非常高,okio确实效率非常高效,支持断点续传的文件seek需要做一些额外的工作。

All 15 comments

带了okhttp,就间接引用了okio,确实是可以考虑将RAF直接改用okio。我回头测试下性能以及是否符合需求,然后做这方面打算。谢谢你提醒。

这个问题,我验证了一些,网络IO和文件IO利用率非常高,okio确实效率非常高效,支持断点续传的文件seek需要做一些额外的工作。

@10045125 👍 好的,也欢迎PR。

但是,我还没有找到如何支持断点续传的写入文件的方式。。。

@10045125 系统IO的资源是固定的,这块效率的策略与稳定性取决因素很多,从单纯的一两个纬度看不出优势,如果okio,在我们FileDownloader使用场景下,刚好得到一个很好的成绩,就可以考虑,具体的在预言了以后我会认真看看。

网络IO和文件IO和断点续传的代码,我这边已经测试是OK的,晚些时间我pull你看下。

@10045125 ok,建议在 demo project中的 PerformanceTestActivity#onClickWriteTest 参照其中的其他两个也加上对应的测试。

根据feat(print-fetching-consume)... 添加的日志来看,在MI5,6M/s 的下载速度下,loop额外耗时(除出write以外loop,强制sync与消息分发的耗时)均值为0.5ms并且前80%都是小于0.54ms。

因此我以loop额外耗时在PerformanceTestActivity中模拟测试使用okio,写完6MB大小的文件,相比与现在使用的RandomAccessFile快了 0.05s~0.5s左右,与FileOutputStream相比相差无几,有时候FieOutputStream甚至更快些。


According to the added log in feat(print-fetching-consume)... ,in Xiaomi5 and 6M/s downloading speed,the extra time consuming in fetching data loop(except the time consuming in write、 force sync and message transmit): average is 0.5ms, and the value of extra time consuming greater than 80 percent is less than 0.54ms.

So I mock testing okio inPerformanceTestActivity with the extra time consuming equal to 0.5ms,completed written 6MB length file uses okio.shink is faster than RandomAccessFile about 0.05s~0.5s,and the speed of okio.shink is almost near to FileOutputStream done,sometimes FieOutputStream is more faster..


The extra time consuming with Xiaomi5 and 6M/s downloading speed:

screen shot 2016-09-26 at 1 49 44 pm

MI5-6M:s-downloading-consume.zip

There are several reasons we can't replace RandomAccessFile with okio easily:

  1. okio.shink doesn't support seek which used for seeking to last correct breakpoint stored in the database when resuming downloading from the breakpoint.
  2. okio.shink doesn't support setLength which used for pre-allocate length for the file when to start downloading.

In the forthcoming 1.2.0 release, you can use the okio write file instead of the default OutputStream in FileDownloader by referring to the configuration here.

为啥不这样做呢?

accessFile = getRandomAccessFile(isSucceedContinue, total);
            fd = accessFile.getFD();
            sink = Okio.buffer(Okio.sink(new FileOutputStream(fd)));
            Buffer buffer = sink.buffer();
            long len;
            source = response.body().source();
            while ((len = source.read(buffer, BUFFER_SIZE)) != -1) {
                sink.emit();
                soFar += len;
                // callback on progressing
                onProgress(soFar, total, fd);

                // Step 6, check pause
                if (checkState()) {
                    // callback on paused
                    onPause();
                    return true;
                }
            }

@10045125

不这样做的原因:

  1. FileDownloadOkio仅仅是使用okio的基本场景的实现,因为我们没有说一定要堆满多少的cache再write一次。
  2. 在我看来,你这个相比与FileDownloadOkio来说并没有什么差别,也是每次都读取4096的数据到buffer,然后再从buffer中取出所有数据,然后写入FileOutputStream。(你可以参照下源码对比下)
  3. 当然由于已经组件化,你可以实现任意的策略,哪怕是你定义一个buffer,在write的时候都仅仅只是写入buffer,在sync的时候才进行真正的写入也是work的。

目前采用组件化,可以提供你动态选用 Okio、BufferedOutputStream、RandomAccessFile 以及 可以自己实现,主要是以下原因 :

  1. 整体测试结果Okio的速度未必比BufferedOutputStream快。
  2. 部分output stream,不支持seek,因此需要配合 file.non-pre-allocation 保证FileDownloader符合预期的运作。

Okio本身就有一个OutputStream的接口,所以可以用BufferedOutputStream来进一步包装FileOutputStream,这样下载速度和写入效率都会比较高效,而且支持seek,我认为不需要提供多种文件写入方式,以下是我的部分代码
`accessFile = getRandomAccessFile(isSucceedContinue, total);
fd = accessFile.getFD();
sink = Okio.buffer(Okio.sink(new BufferedOutputStream(new FileOutputStream(fd))));
Buffer buffer = sink.buffer();
long len;
source = response.body().source();
while ((len = source.read(buffer, BUFFER_SIZE)) != -1) {
sink.emit();
soFar += len;
// callback on progressing
onProgress(soFar, total, fd);

            // Step 6, check pause
            if (checkState()) {
                // callback on paused
                onPause();
                return true;
            }
        }`

我认为Okio的BufferedSource能够充分的利用榨取网络速度,在配合BufferedOutputStream的写入效率,这两点能够很好的结合。希望作者考虑下,以上的方式是我几种方案中表现非常不错的。

@10045125 其实有一个速度最快的,无论用哪个OutputStream,你可以试试:实现 FileDownloadOkio接口,在write时只写入memory,在sync时保证固化到本地。

只是不同的策略而已。稳定性与速度的取舍,而对于同一台设备而言,资源都是有限而固定的。

Was this page helpful?
0 / 5 - 0 ratings