我正在尝试使用存储在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中存储的资产数量成正比?我知道资产是压缩的,但我只是获取资产的名称,我认为这些名称会存储在某个表格中。
答案 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
上的提交