¿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:
- Android 2.2 - ¿Cómo puedo detectar si estoy instalado en la tarjeta SD o no?
- ¿Cómo eliminar un archivo de la tarjeta SD?
- Android obtiene imágenes de sdcard
- Escritura Bitmap al almacenamiento externo (sdcard)
- ¿Cómo puedo leer el archivo json de la tarjeta SD
// 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.
- Impedir que otras aplicaciones accedan a la imagen de mi aplicación almacenada en la tarjeta sd
- Problema que empuja archivo grande a Emulator / SDcard con Eclipse DDMS
- Cambiar android: installLocation de preferExternal a internalOnly
- Todavía el permiso denegado después de agregar "WRITE_EXTERNAL_STORAGE" en AndroidManifest.xml
- Android SAF (Storage Access FrameWork): Obtenga un archivo Uri de TreeUri
- ¿Por qué no puedo usar el atributo installLocation en el archivo Manifest?
- Exportar archivo de base de datos sqlite en XML y luego en la hoja de cálculo de Excel
- Reinicie el servicio para la aplicación en la tarjeta SD cuando se vuelve a cargar la tarjeta SD
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 }
- Activación de la conexión wifi mediante programación
- Codificación y decodificación de bytes con ZXing