性能:Android JCIFS 文件读取速度慢
Posted
技术标签:
【中文标题】性能:Android JCIFS 文件读取速度慢【英文标题】:Performance: Android JCIFS file reading is slow 【发布时间】:2017-08-23 21:45:55 【问题描述】:首先,我看到了一个现有问题 (JCIFS: file retrieval is too slow to be usable),但它是针对 Java 而不是 android,建议的答案都不起作用。
我在 Android Studio 2.3 中为 Android SDK 25 (7.1.1) 创建了一个默认项目,将库与 compile 'jcifs:jcifs:1.3.17'
链接起来,并输入了以下简单的测试代码。结果在代码下方。
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
jcifs.Config.setProperty("jcifs.util.loglevel", "3");
//jcifs.Config.setProperty("jcifs.smb.client.dfs.disabled", "false");
//jcifs.Config.setProperty("jcifs.resolveOrder", "DNS");
try
NtlmPasswordAuthentication auth = new NtlmPasswordAuthentication("", ID, PASSWORD);
final SmbFile smb = new SmbFile("smb://192.168.XX.XX/Share/FileName", auth);
Thread t = new Thread(new Runnable()
@Override
public void run()
Log.d(TAG, "Test Start");
for(int i = 1000; i<10000; i+=1000)
measure(i);
Log.d(TAG, "Test End");
private void measure(int bufferSize)
Log.d(TAG, "=====Buffer: " + bufferSize + "============");
try
byte[] buffer = new byte[bufferSize];
int read = 0;
InputStream str = smb.getInputStream();
long start = System.nanoTime();
while(read < 1000000)
read += str.read(buffer);
long end = System.nanoTime();
str.close();
float time = (float) ((end - start) / 1000000000d);
float speed = (float) read / 1048576 / time;
Log.d(TAG, "Time:" + time + ", size =" + read);
Log.d(TAG, "Speed = " + speed + "MB/s");
catch(IOException exc)
exc.printStackTrace();
);
t.start();
catch(Exception exc)
Log.d(TAG, exc.toString());
结果
Test Start
=====Buffer: 1000============
Time:2.210785, size =1000000
Speed = 0.43137363MB/s
=====Buffer: 2000============
Time:1.4158936, size =1000000
Speed = 0.6735495MB/s
=====Buffer: 3000============
Time:1.0556641, size =1002000
Speed = 0.9051948MB/s
=====Buffer: 4000============
Time:0.7543335, size =1000000
Speed = 1.2642609MB/s
=====Buffer: 5000============
Time:3.6557617, size =1000000
Speed = 0.26086885MB/s
=====Buffer: 6000============
Time:3.292389, size =1002000
Speed = 0.2902396MB/s
=====Buffer: 7000============
Time:2.9179688, size =1001000
Speed = 0.32715496MB/s
=====Buffer: 8000============
Time:2.462616, size =1000000
Speed = 0.38726068MB/s
=====Buffer: 9000============
Time:3.9379272, size =1008000
Speed = 0.24411413MB/s
Test End
读取速度约为 0.2MB/s ~ 1.2MB/s。设备连接150Mbps Wi-Fi,理论上可以达到10MB/s以上。 SMB 服务器也不慢。当我将文件复制到笔记本电脑时,读取速度约为 30MB/s。
为什么这么慢?我应该检查什么?如果缓冲区大小为 4000,为什么读取速度大约高 5 倍(1.2MB/s)?
顺便说一句,我已经测试过使用其他商业应用程序复制相同的文件。 File Commander、Asus File Manager 显示同样低速,ES File Explorer 显示约 2MB/s,Solid Explorer 显示约 5MB/s。因为我很确定他们都使用 JCIFS(尽管它的版本可能略有不同),所以必须有一种方法可以像 Solid Explorer 那样实现至少 5MB/s。
【问题讨论】:
【参考方案1】:在Windows电脑上使用WireShark(网络分析工具)后发现,无论我设置哪个缓冲区大小,read SMB命令总是给Windows电脑4286字节。 SmbFileInputStream.java
似乎正在使用服务器的最大缓冲区大小。
但是当我看到来自 Solid Explorer 的数据包时,它是 32768 字节。所以,我反编译了 Solid Explorer 的 APK(它当然被混淆了),并在其中看到了 SmbFileInputStream.java
文件(该文件属于 JCIFS)。似乎 Solid Explorer 的开发人员修改了该文件,并设置了更大的readSize
。所以,我尝试了类似的事情。然后我在上面的相同代码中实现了 5MB/s。
由于 JCIFS 附带 LGPL,Solid Explorer 使用修改后的 JCIFS 而不公开源代码的事实违反了 JCIFS 的许可。但是,哦,好吧,似乎很多 Android 应用程序开发人员都忽略了他们使用的库的许可证。他们甚至没有正确地归功于他们使用的开源库。
【讨论】:
也许他们使用了我回答中提到的补丁?【参考方案2】:您是否尝试过:jcifs.Config.setProperty("jcifs.smb.client.dfs.disabled", "true");
在我的情况下(虽然是 Java),它有助于慢速连接。默认为false
对我来说唯一的问题是我不确定该属性是否会“破坏”其他工作正常的东西..
【讨论】:
是的,如果您仔细观察,我已经在我的原始代码示例中尝试过。正如我所写的那样,问题在于消息较小,这会带来更多的往返次数和开销。【参考方案3】:有一个大缓冲区读取的补丁:
https://github.com/kohsuke/jcifs/issues/11 https://github.com/kohsuke/jcifs/tree/master/patches https://jcifs.samba.org/src/patches/LargeReadWrite.patch:从自述文件内部:
这个补丁增加了两个 SMB,据说可以改善读写 性能相当。不幸的是,目前还不清楚 所有实现都正确支持命令。请注意,在 除了这个补丁,还需要添加一个“& 0xFFFF” SmbTransport.java:doRecv:~437 显示为:
int size = Encdec.dec_uint16be(BUF, 2) & 0xFFFF;
尽管在 1.2.7 中已进行了此更改。
不确定这是否适用于 Android,但解决方案可能类似。
【讨论】:
以上是关于性能:Android JCIFS 文件读取速度慢的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 jCIFS 在 Android 中使用默认查看器打开文件