为什么AssetManger.list()这么慢?

时间:2012-09-28 09:03:20

标签: android android-sdcard

我正在尝试使用存储在SD卡上的文件混合填充ListView并将其作为资产存储在APK中。使用TraceView,我可以看到AssetManager.list()的效果与File.listFiles()相比较差,即使我使用的是SD卡的文件名过滤器。

这是一个简单的方法,它返回SD卡上文件夹中的所有png文件:

// The folder on SDcard may contain files other than png, so filter them out
private File[] getMatchingFiles(File path) {
File[] flFiles = path.listFiles(new FilenameFilter() {
    public boolean accept(File dir, String name) {
    name = name.toLowerCase();
    return name.endsWith(".png");
    }
});  
return flFiles;
}

我在这里调用该方法,检索16个文件大约需要12毫秒:

final String state = Environment.getExternalStorageState();           
if (Environment.MEDIA_MOUNTED.equals(state)||Environment.MEDIA_SHARED.equals(state)) {
    File path = Environment.getExternalStoragePublicDirectory(getResources().getString(R.string.path_dir));
if (path.exists()){
    File[] files = getMatchingFiles(path); 
        ... 

am.list方法需要49ms来检索大约6个文件的名称!

// Get all filenames from specific Asset Folder and store them in String array
AssetManager am = getAssets();
String path = getResources().getString(R.string.path_dir);
String[] fileNames = am.list(path);  
...

任何人都可以解释为什么表现如此糟糕?性能是否与APK中存储的资产数量成正比?我知道资产是压缩的,但我只是获取资产的名称,我认为这些名称会存储在某个表格中。

4 个答案:

答案 0 :(得分:4)

Coverdriven的评论“存储在某个地方的桌子上”激发了我解决自己的问题,这个问题我已经推迟了一段时间。

这不能回答OP,但确实提供了一种不同的方法,它处理CommonsWare解决方案所不具备的子文件夹,除非你进行递归(当然这是另一种可能的解决方案)。它专门针对在子文件夹中拥有大量资产的应用程序。

我添加了一个ANT预构建目标来运行此命令(我在Windows上)

dir assets /b /s /A-d > res\raw\assetfiles

这将创建所有文件的递归(/ s),准系统(/ b)列表,不包括资产文件夹中的目录条目(/ A-d)。

然后我创建了这个类来静态地将assetsfiles的内容加载到一个hashmap中,其关键是文件名,值是完整路径

public class AssetFiles {

// create a hashmap of all files referenced in res/raw/assetfiles

/*map of all the contents of assets located in the subfolder with the name specified in FILES_ROOT
the key is the filename without path, the value is the full path relative to FILES_ROOT
includes the root, e.g. harmonics_data/subfolder/file.extension - this can be passed
directly to AssetManager.open()*/
public static HashMap<String, String> assetFiles = new HashMap<String, String>();
public static final String FILES_ROOT = "harmonics_data";

static {

    String line;
    String filename;
    String path;

    try {

        BufferedReader reader = new BufferedReader(new InputStreamReader(TidesPlannerApplication.getContext().getResources().openRawResource(R.raw.assetfiles)));

        while ((line = reader.readLine()) != null) {
            // NB backlash (note the escape) is specific to Windows
            filename = line.substring(line.lastIndexOf("\\")+1);
            path = line.substring(line.lastIndexOf(FILES_ROOT)).replaceAll("\\\\","/");;
            assetFiles.put(filename, path);
        }

    } catch (IOException e) {
        e.printStackTrace();
    }

}

public static boolean exists(String filename){
    return assetFiles.containsKey(filename);
}

public static String getFilename(String filename){
    if (exists(filename)){
        return assetFiles.get(filename);
    } else {
        return "";
    }

}

}

要使用它,我只需调用AssetFiles.getFilename(filename),它返回我可以传递给AssetManager.open()的完整路径。快得多!

NB。我还没有完成这个课程,但它还没有硬化,所以你需要添加适当的异常捕获和操作。它也非常特定于我的应用程序,因为我的所有资源都位于子文件夹中,而子文件夹又位于assets文件夹的子文件夹中(请参阅FILES_ROOT),但很容易适应您的情况。

还要注意需要替换反斜杠,因为Windows使用正斜杠生成资产文件列表。您可以在OSX和* nix平台上消除此问题。

答案 1 :(得分:2)

  

任何人都可以解释为什么表现如此糟糕?

显然,读取ZIP存档(资产所在的APK)的内容比读取文件系统上目录的内容要慢。在摘要中,这并不特别令人惊讶,因为我怀疑所有主要操作系统都适用。

读入list()数据一次,然后将其保存在其他地方以便更快地访问(例如,数据库),特别是以针对将来查找优化的形式(例如,简单的数据库查询可以为您提供什么你想要,而不是必须加载并“再次”递归搜索“。

答案 2 :(得分:1)

如果资产中有深层目录,则可以首先检测项目是文件还是目录,然后在其上调用.list()(实际上加速了遍历树的步骤)。这是我为此发现的解决方案:

try {
    AssetFileDescriptor desc = getAssets().openFd(path);  // Always throws exception: for directories and for files
    desc.close();  // Never executes
} catch (Exception e) {
    exception_message = e.toString();
}

if (exception_message.endsWith(path)) {  // Exception for directory and for file has different message
    // Directory
} else {
    // File
}

答案 3 :(得分:1)

你可以接近APK包,因为它是一个ZIP文件,并使用Java的内置ZipFile读取所有条目。它将为您提供所有文件名及其完整路径。也许找到你有哪些目录应该不难。

到目前为止,这是我测试过的最快的方法。

归功于@ obastemur在jxcore-android-basics sample project

上的提交