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)

等々......

プロガード マッピング:

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

どこかで、再帰を数えて制限を適用する必要があります。しかし、Android または個々のデバイスのハードウェアに適用される制限がどこにあるのかわかりません。

よろしくお願いします。

4

2 に答える 2

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;
}

emptor の警告: このコードを実行したり、コンパイルできるかどうかを確認したりしませんでした。

しかし、アイデアは、関心のあるディレクトリから始まるディレクトリのリストを維持し、そのリストから最初のディレクトリを削除し、そのディレクトリのファイルを結果に追加することです(必要な場合は、ディレクトリをresultArrayに追加するようにコードを変更します)ディレクトリも必要)、ディレクトリをディレクトリのリストに追加して検索し、ディレクトリのリストが空になるまで続けます。

どこまで再帰する必要があるか、またはどこまで再帰できるかを事前に知ることができない場合、再帰は良くありません。ファイルシステムの繰り返しが再帰に適した場所だとは思いませんが、それは私自身の意見です。

于 2013-05-16T13:51:47.860 に答える