2013-05-16 201 views
3

在我的Android應用程序中,我將外部存儲的所有路徑填充到數組中。java.lang.StackOverflowError遞歸目錄

少數設備正在報告StackOverflowError

我讀過many linked posts關於這個問題的原因,但我不知道如何處理它或防止它在我使用的代碼內發生。我也不理解Android可以處理的'遞歸限制'。

以下代碼適用於from this source

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或個別設備硬件的位置或限制?

在此先感謝您的幫助。

回答

1

計數遞歸很簡單:只需添加一個int參數的getFileListingNoSort方法,並增加在每次調用值:

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和爲什麼最好的價值它是無限循環的。有問題的文件系統可能具有創建週期的符號鏈接。

+0

謝謝安德烈亞斯。是的,問題仍然是這個限制應該設置爲什麼?另外,我看過帖子中超過了'最大整數級別',應該使用長整型。不知道這個限制,我不知道這是否也需要考慮?可能不太可能是由於計數是存儲路徑。感謝您指出符號鏈接 - 我不認爲這一點。我會調查。 – 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如果你想要的目錄),將目錄添加到要搜索的目錄列表,並繼續,直到目錄列表爲空。

遞歸是壞的,如果你不能提前知道你需要遞減多少,或者你有多遠。我不認爲文件系統迭代是遞歸的適當位置,但這是我個人的看法。

+0

謝謝本,但我有點困惑 - '維護目錄列表'< - 我怎麼能做到這一點,而不遞歸創建一個!? – brandall

+0

當在遍歷filesDirs的過程中遇到目錄時,將其添加到dirsToSearch;否則,您檢查它是否符合您的條件,如果符合,則將其添加到resultsArray中。只要有已添加但未列出的目錄,do ... while循環將繼續,列出目錄的第一步是將其從dirsToSearch中移除。 –

+0

我需要一點時間來消化你的建議實現。感謝您回覆澄清。 – brandall