java.lang.StackOverflowError 递归目录

3
在我的Android应用程序中,我正在将外部存储的所有路径填充到一个数组中。
少数设备报告了StackOverflowError
我已经阅读了许多相关帖子,了解了这个问题的原因,但我不知道如何处理它或在我使用的代码中防止它发生。我也不理解Android可以处理的“递归限制”。
下面的代码是从这个来源改编的。
private final Locale loc = SupportedLanguages.isSupported();
private final String CACHE = "cache";
private final String TEMP = "temp";

@Override
protected Boolean doInBackground(Void... params) {

        final File fileList = Environment.getExternalStorageDirectory();

        final String absolutePath = Environment.getExternalStorageDirectory().getAbsolutePath();

        final File[] dirList = fileList.listFiles();

        final List<File> listDirs = Arrays.asList(dirList);

        if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {

            final ArrayList<String> dirPath = new ArrayList<String>();
            final ArrayList<String> dirName = new ArrayList<String>();
            String fileName = "";

            for (final File startingDirectory : listDirs) {
                if (!startingDirectory.isFile() && startingDirectory.canRead() && !startingDirectory.isHidden()) {

                    final List<File> files = getFileListing(startingDirectory);

                    if (files != null) {

                        for (final File file : files) {

                            fileName = file.getPath().replaceAll(absolutePath, "").toLowerCase(loc).replaceAll("\\/", " ")
                                    .trim();
                            fileName = fileName.replaceAll(" +", " ");

                            dirName.add(fileName);
                            dirPath.add(file.toString());
                        }
                    }
                }
            }

        } 


    return true;
}

private List<File> getFileListing(File aStartingDir) {
    List<File> result = getFileListingNoSort(aStartingDir);

    if (result != null && !result.isEmpty()) {
        Collections.sort(result);
    }
    return result;
}

private List<File> getFileListingNoSort(File aStartingDir) {
    List<File> resultArray = new ArrayList<File>();
    File[] filesAndDirs = aStartingDir.listFiles();

    if (filesAndDirs != null && filesAndDirs.length > 0) {

        List<File> filesDirs = Arrays.asList(filesAndDirs);

        for (File file : filesDirs) {
            if (!file.isFile() && file.canRead() && !file.isHidden() && !file.getName().toLowerCase(loc).startsWith(CACHE)
                    && !file.getName().toLowerCase(loc).startsWith(TEMP)) {

                resultArray.add(file);
                List<File> deeperList = getFileListingNoSort(file);
                resultArray.addAll(deeperList);
            }
        }
    }

    return resultArray;
}

崩溃日志:

> Caused by: java.lang.StackOverflowError at
> java.lang.AbstractStringBuilder.append0(AbstractStringBuilder.java:145)
> at java.lang.StringBuilder.append(StringBuilder.java:216) at
> java.io.File.join(File.java:215) at java.io.File.<init>(File.java:157)
> at java.io.File.<init>(File.java:124) at
> java.io.File.filenamesToFiles(File.java:852) at
> java.io.File.listFiles(File.java:791) at
> com.mypackage.name.ll.a(Unknown Source) at
> com.mypackage.name.ll.a(Unknown Source) at
> com.mypackage.name.ll.a(Unknown Source) at
> com.mypackage.name.ll.a(Unknown Source) at
> com.mypackage.name.ll.a(Unknown Source) at
> com.mypackage.name.ll.a(Unknown Source) at
> com.mypackage.name.ll.a(Unknown Source) at
> com.mypackage.name.ll.a(Unknown Source) at
> com.mypackage.name.ll.a(Unknown Source)

等等……

Proguard映射:

com.mypackage.name.GenerateSubDirectoryList -> com.mypackage.name.ll:
java.util.List getFileListingNoSort(java.io.File) -> a

我需要在某处对递归进行计数并应用限制。但我不知道是在哪里,也不知道适用于Android还是单独的设备硬件?

提前感谢您的帮助。

2个回答

1
计算递归很容易:只需在getFileListingNoSort方法中添加一个int参数,并在每次调用时增加该值即可。请勿删除html标签。
private List<File> getFileListingNoSort(File aStartingDir, int level) {
    List<File> resultArray = new ArrayList<File>();
    File[] filesAndDirs = aStartingDir.listFiles();

    if (level < MAX_LEVEL && filesAndDirs != null && filesAndDirs.length > 0) {

        List<File> filesDirs = Arrays.asList(filesAndDirs);

        for (File file : filesDirs) {
            if (!file.isFile() && file.canRead() && !file.isHidden() && !file.getName().toLowerCase(loc).startsWith(CACHE)
                    && !file.getName().toLowerCase(loc).startsWith(TEMP)) {

                resultArray.add(file);
                List<File> deeperList = getFileListingNoSort(file, ++level);
                resultArray.addAll(deeperList);
            }
        }
    }

    return resultArray;
}

但问题仍然是:MAX_LEVEL的最佳值是多少,为什么会无限循环。涉及的文件系统可能有创建循环的符号链接。

谢谢Andreas。是的,问题仍然是应该将这个限制设置为多少?我看到过一些帖子,其中“最大整数级别”被超过,需要使用long。但是,如果不知道限制,我不知道是否也需要考虑这一点?也许不太可能,因为计数是存储路径。感谢您指出符号链接-我没有考虑过。我会进行调查。 - brandall

1

Android可以运行在许多硬件上,其中许多可能根本没有太多的堆栈;因此,不要通过递归子目录来搜索,而是进行广度优先搜索,如下所示:

private List<File> getFileListingNoSort(File aStartingDir) 
{
    // assuming aStartingDir is a valid input
    List<File> dirsToSearch = new ArrayList<File>();
    dirsToSearch.add(aStartingDir);
    List<File> resultArray = new ArrayList<File>();
    do{
        File thisDir = dirsToSearch.remove(0);      
        List<File> filesDirs = Arrays.asList(thisDir.listFiles());

        for (File file : filesDirs) 
        {
            if (file.isDirectory())
            {
                dirsToSearch.add(file);
            }
             else if( file.canRead() && 
                      !file.isHidden() &&     
                      !file.getName().toLowerCase(loc).startsWith(CACHE) &&
                      !file.getName().toLowerCase(loc).startsWith(TEMP))
            {
                resultArray.add(file);              
            }
        }
    } while(false == dirsToSearch.isEmpty());
    return resultArray;
}
但是这个想法是,维护一个目录列表,从你关心的目录开始,从该列表中删除第一个目录,将该目录中的文件添加到结果中(如果您也想要目录,请修改代码将目录添加到resultArray中),将目录添加到要搜索的目录列表中,并继续直到目录列表为空。
如果您无法事先知道需要递归多少次或者能够递归多远,则递归是不好的。我认为,在文件系统迭代中使用递归是不合适的,但这是我的个人意见。

谢谢Ben,但我有点困惑 - “维护目录列表” <- 首先,我如何在不递归创建目录的情况下做到这一点!? - brandall
当遍历filesDirs时,如果遇到一个目录,则将它添加到dirsToSearch中;否则,您检查它是否符合条件,如果是,则将其添加到resultsArray中。do...while循环将继续,只要有您已添加但未列出的目录,并且列出目录的第一步是将其从dirsToSearch中删除。 - Ben Brammer
我需要一点时间来消化你提出的实现建议。感谢你回来澄清。 - brandall

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接