android - 性能:Android JCIFS 文件读取速度慢

标签 android performance optimization jcifs

首先,我看到了一个现有问题 ( 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。

最佳答案

在Windows电脑上使用WireShark(网络分析工具)后,我发现无论我设置什么缓冲区大小,读取SMB命令总是给Windows电脑4286字节。似乎 SmbFileInputStream.java 正在使用服务器的最大缓冲区大小。

但是当我看到来自 Soild Explorer 的数据包时,它是 32768 字节。因此,我反编译了 Solid Explorer 的 APK(它当然是经过混淆处理的),并在其中看到了 SmbFileInputStream.java 文件(该文件属于 JCIFS)。似乎 Solid Explorer 的开发人员修改了该文件,并设置了更大的 readSize。所以,我尝试了类似的事情。然后我用上面的相同代码实现了 5MB/s。

由于 JCIFS 附带 LGPL,Solid Explorer 在不公开源代码的情况下使用修改后的 JCIFS 违反了 JCIFS 的许可。但是,哦,好吧,似乎很多 Android 应用程序开发人员都忽略了他们使用的库的许可。他们甚至没有正确地归功于他们使用的开源库。

关于android - 性能:Android JCIFS 文件读取速度慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43109872/

相关文章:

android - getView 未在 baseAdapter 类上调用

php - Symfony 和 CakePHP 是否太慢而无法使用?

javascript - 实例化多个 Mootools 类的性能开销

c - 将字符串转换为整数的最小方法(反之亦然)

android - 未处理的异常 : MissingPluginException(No implementation found for method getBatteryLevel on channel *) with flutter1. 12

android - AlarmManager - 任务队列(PendingIntent 队列)

php - base64_encode 和序列化 php 函数参数

python - 内部列表中的第一项尽可能高效

android - 如何从 Jenkins 创建 aab 而不是 apk

Python:加速从列表中删除每个第 n 个元素