¿Por qué AssetManger.list () es tan lento?

Estoy tratando de poblar un ListView con una mezcla de archivos almacenados en la tarjeta SD y almacenados como activos en el APK. Usando TraceView , puedo ver que el rendimiento de AssetManager.list() es pobre en comparación con File.listFiles() , aunque estoy usando un filtro de nombre de archivo para la tarjeta SD.

Aquí está un método simple que devuelve todos los archivos png de una carpeta en la SDcard:

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

Invoco este método aquí y se tarda unos 12 ms para recuperar 16 archivos:

 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); ... 

Mientras que el método am.list toma 49ms para recuperar sólo los nombres de unos 6 archivos!

 // 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); ... 

¿Puede alguien explicar por qué el rendimiento sería tan pobre? ¿El rendimiento es proporcional al número de activos almacenados en el APK? Soy consciente de que los activos están comprimidos, pero sólo busco los nombres de los activos, que pensé que se almacenarían en una tabla en algún lugar.

El comentario de Coverdriven "almacenado en una mesa en algún lugar" me inspiró a resolver mi propio problema que he estado posponiendo por un tiempo.

Esto no responde al OP, pero ofrece un enfoque diferente y maneja las subcarpetas que la solución de CommonsWare no hace a menos que vayas recursivo (que por supuesto es otra posible solución). Está específicamente dirigido a aplicaciones que tienen un gran número de activos en subcarpetas.

He añadido una ANT pre-build objetivo para ejecutar este comando (estoy en Windows)

 dir assets /b /s /Ad > res\raw\assetfiles 

Esto crea una lista recursiva (/ s), barebones (/ b) de todos los archivos, excluyendo las entradas de directorio (/ Ad) en mi carpeta de activos.

A continuación, creé esta clase para cargar de forma estática el contenido de assetsfiles en un hashmap, cuya clave es el nombre de archivo y el valor de la ruta completa

 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, eg 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 ""; } } 

}

Para usarlo, simplemente llamo AssetFiles.getFilename (filename) que devuelve la ruta completa que puedo pasar a AssetManager.open (). Mucho mucho más rápido!

NÓTESE BIEN. No he terminado esta clase y no está endurecido todavía así que usted necesitará agregar las capturas y las acciones apropiadas de la excepción. También es muy específico para mi aplicación en que todos mis activos están en subcarpetas que a su vez se encuentran en una subcarpeta de la carpeta de activos (ver FILES_ROOT) pero fácil de adaptar a su situación.

Tenga en cuenta también la necesidad de reemplazar las barras inversas, ya que Windows genera la lista assetfiles, con barras diagonales. Usted podría eliminar esto en plataformas OSX y * nix.

¿Puede alguien explicar por qué el rendimiento sería tan pobre?

Al leer el contenido de un archivo ZIP (el APK donde se encuentran los recursos) es más lento que leer el contenido de un directorio en el sistema de archivos. En abstracto, esto no es especialmente sorprendente, ya que sospecho que esto sería cierto para todos los principales sistemas operativos.

Lea en esa list() datos una vez, luego guárdelo en otro lugar para un acceso más rápido (por ejemplo, base de datos), en particular en un formulario que está optimizado para búsquedas futuras (por ejemplo, donde una simple consulta de base de datos podría dar lo que desea. Tener que cargar y "recursivamente buscarlo" de nuevo).

Puede acercarse al paquete APK como si fuera un archivo ZIP y leer todas las entradas usando el ZipFile builtin de Java. Le dará todos los nombres de archivo con sus rutas de acceso completas. Tal vez no debería ser difícil encontrar qué directorios tiene.

Hasta ahora este es el enfoque más rápido que he probado.

Crédito va a @ obastemur cometer en jxcore-android-basics proyecto de muestra

Si tiene un árbol profundo de directorios en los activos, puede detectar en primer lugar si un elemento es un archivo o directorio y, a continuación, llame a .list () en él (realmente acelera el caminar a través del árbol). Esta es mi solución que he descubierto para esto:

 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 } 
  • Android - ¿Cómo copiar una carpeta del sistema de archivos a la tarjeta SD usando DDMS?
  • Android: error de transferencia: sistema de archivos de sólo lectura
  • Lista todas las imágenes de la tarjeta SD.
  • Cómo reproducir archivos mp3 de la tarjeta SD interna y externa en android?
  • Almacenamiento de archivos en el almacenamiento externo en Nexus 7 y recuperación desde un PC
  • Android: quitar una imagen de la tarjeta sd
  • Copia de seguridad de Android y restauración de la base de datos desde y hacia la tarjeta SD
  • Compruebe si el directorio existe en la tarjeta sdcard de android
  • Problemas de lectura inputStream desde un Uri y envío a través de un outputStream android
  • Android: Detectar cuando la tarjeta SD montada como unidad de disco a un equipo
  • ¿Cómo obtener el Uri de una imagen almacenada en el SDCARD?
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.