Cómo utilizar y gestionar mapas de bits de forma eficaz
I. ¿Proceso de carga de imágenes?
Primero, hablemos sobre el proceso de carga de imágenes. El proceso de procesamiento del módulo en el proyecto es el siguiente:
1. En el hilo principal de la interfaz de usuario, obtenga la imagen del. Memoria caché, regresa después de encontrarla. Si no lo encuentra, continúe con el siguiente paso;
2. En el hilo de trabajo, obtenga la imagen del caché del disco, regrese y actualice la memoria caché después de encontrarla. Si no se encuentra, vaya al siguiente paso;
3. En el hilo de trabajo, obtenga la imagen del caché del disco, regrese y actualice la memoria caché después de encontrarla.
3. En el hilo de trabajo, obtenga la imagen de la red, regrese y actualice la memoria caché y el caché del disco después de encontrarla. De lo contrario, se mostrará el mensaje predeterminado.
En segundo lugar, la clase de memoria caché (PanoMemCache)
Aquí usamos la clase LruCache proporcionada por Android, que guarda una referencia sólida para limitar la cantidad de contenido cada vez que se accede a un elemento. , El artículo se moverá al principio de la cola. Cuando se agrega un nuevo elemento cuando el caché está lleno, los elementos al final de la cola serán desalojados.
[java] ¿Ver impresión de copia pura?
clase pública PanoMemoryCache { ?
// ¿Capacidad inicial de LinkedHashMap?
privada estática final int INITIAL_CAPACITY = 16;
// ¿factor de carga de LinkedHashMap?
private static final int LOAD_FACTOR = 0.75f;
// ¿modo de clasificación de LinkedHashMap?
privado estático final booleano ACCESS_ORDER = true;
// ¿Caché de referencia suave?
privado estático LinkedHashMap
// ¿Caché de referencia dura?
LruCache estático privado
?
public PanoMemoryCache() { ?
// Obtener un single ¿La cantidad máxima de memoria disponible para el proceso?
// Método 1: ¿Usar el servicio ActivityManager (en M)?
/*int memClass = ((ActivityManager) context. getSystemService(Context .ACTIVITY_SERVICE)).getMemoryClass();*/ ?
// Método 2: ¿Usar clases de tiempo de ejecución (en bytes)?
final int memClass = (int) Runtime.getRuntime().maxMemory(); ?
// ¿Establecer en 1/4 de la memoria disponible (en bytes)?
final int cacheSize = memClass / 4 ?
mLruCache = new LruCache
@Override ?
protected int sizeOf(String key, Bitmap value) { ?
if(value ! = null) { ?
// Calcula los bytes ocupados por el número de mapa de bits almacenado ?
return value.getRowBytes() * value.getHeight(); ?
} else { ?
Devuelve 0 ?
}?
} ??
?
@Override ?
entrada nula protegidaRemoved(booleano desalojado, clave de cadena, valor antiguo de mapa de bits , Bitmap newValue) { ?
if(oldValue ! = null) { ?
// Cuando la memoria caché de referencia física esté llena, se utilizará el algoritmo LRU para transferir la más reciente imágenes no utilizadas Se movieron a la caché de referencia suave.
mSoftCache.put(clave, nueva SoftReference
}?
} ??
}; ?
?
/*?
* El primer parámetro: capacidad inicial (predeterminado 16) ?
* El primero Dos parámetros: ¿factor de carga (por defecto 0,75)?
* El tercer parámetro: Modo de clasificación (verdadero: ordenado por visitas; falso: ordenado por orden de inserción).
*/ ?
mSoftCache = new LinkedHashMap
serialVersionUID final largo estático privado = 7237325113220820312L; ?
@Override ?
booleano protegido removeEldestEntry(Entrada
if(size() > SOFT_CACHE_SIZE) { ?
Devuelve verdadero ?
}?
Devuelve falso ?
} ??
}; ?
}?
?
/**?
?* ¿Obtener mapa de bits del caché? * @param url?* @return bitmap?
?* @return bitmap?
mapa de bits público getBitmapFromMem(String url) { ?
mapa de bits mapa de bits = null ?
// ¿Obtenerlo primero del caché de referencia física?
sincronizado (mLruCache) { ?
mapa de bits = mLruCache.get(url); ?
if(mapa de bits ! = nulo) { ?
// Una vez que se encuentra el mapa de bits, muévalo a la parte superior de LinkedHashMap, asegurándose de que se elimine al final en el algoritmo LRU. ?
mLruCache.remove(url); ?
mLruCache.put(url, mapa de bits ?
Devolver mapa de bits; > }?
} ??
// ¿Volver a obtener del caché de referencia suave?
sincronizado (mSoftCache) { ?
SoftReference
if(bitmapReference ! = null) { ?
bitmap = bitmapReference.get( ); ?
if(bitmap ! = null) { ?
// Una vez encontrado el mapa de bits, muévalo al caché de referencia física. y elimínelo del caché de referencia suave.
?
mLruCache.put(url, mapa de bits); ?
mSoftCache.remove(url ?
Devolver mapa de bits; > } else { ?
mSoftCache.remove(url); ?
}?
} ??
} ??
Devolver nulo; ?
} ??
?
/**?
?* Establecer el bit ¿Agregar gráfico a la memoria caché?
?* @param url?* @param bitmap?
?* @param url?
?
public void addBitmapToCache( URL de cadena, mapa de bits mapa de bits) { ?
if(mapa de bits! = nulo) { ?
sincronizado (mLruCache) { ?
mLruCache.put(url, mapa de bits);
} ??
} ??
} ??
?
/* *?
?* ?¿Borrar caché de referencia suave?
?
?
public void clearCache() { ?
mSoftCache.clear() ?
> mSoftCache = null; ?
}?
} ??
Además, dado que la plataforma 4.0 ajustó posteriormente la estrategia de reciclaje de los objetos de referencia de la clase SoftReference, entonces El caché de referencia suave en esta clase en realidad tiene poco impacto y se puede eliminar. Las plataformas anteriores a 2.3 recomiendan conservarlo.
3. ¿Clase de caché de disco (PanoDiskCache)?
5. ¿Usar decodeByteArray() o decodeStream()?
Dicho esto, algunos niños pueden preguntarme por qué uso BitmapFactory.decodeByteArray(data, 0, data.length, opts) para crear Bitmap en lugar de usar BitmapFactory.decodeStream(is, null, opts). ¿No requeriría esto escribir un método estático adicional readInputStream()?
Sí, decodeStream() es de hecho el método preferido en este caso de uso, pero en algunos casos da como resultado que el recurso de imagen no esté disponible de inmediato o que la imagen se almacene en caché en secreto y espere un poco más. enviárnoslo dentro del plazo establecido. Pero el retraso es fatal y no podemos darnos el lujo de esperar. Por lo tanto, aquí usamos decodeByteArray() para obtener directamente desde la matriz de bytes, que está cerca del IO subyacente, no está restringido por la plataforma y tiene menos riesgo de uso.
6. ¿Cuáles son los métodos para obtener imágenes después de introducir el mecanismo de almacenamiento en caché?
[java]¿Ver impresión de copia pura?
/**?
?* ¿Cargar mapa de bits?
?* @param url ?
?* @return ?
?*/ ?
mapa de bits privado loadBitmap(String url) { ?
// Desde Obtenido de la memoria caché, ¿se recomienda ejecutarlo en el hilo principal de la interfaz de usuario?
Bitmap bitmap = memCache.getBitmapFromMem(url ?
if(bitmap == null); { ?
// Obtener del caché del archivo, ¿se recomienda hacerlo en el hilo de trabajo?
bitmap = diskCache.getBitmapFromDisk(url); ?
if(bitmap == null) { ?
// Obtenerlo de Internet, sin recomendación requerido, Tierra Todo el mundo lo sabe~_~ ?
bitmap = PanoUtils.downloadBitmap(this, url ?
if(bitmap ! = null) { ?
diskCache .addBitmapToCache(mapa de bits, URL); ?
memCache.addBitmapToCache(url, mapa de bits);
}?
} else { ? p>
memCache.addBitmapToCache(url, mapa de bits); ?
} ??
} ??
Devolver mapa de bits;
} ??
VII. ¿Grupo de subprocesos de trabajo?
Para obtener más información sobre cómo cambiar entre varios subprocesos e invalidar el método loadBitmap() en el subproceso de la interfaz de usuario, consulte esta otra publicación del blog: Rompiendo la interfaz de usuario en aplicaciones de Android 4. usando el modo severo La "dictadura" del hilo principal.
La forma recomendada de manejar los subprocesos de trabajo es utilizar un grupo de subprocesos personalizado, cuyo código principal es el siguiente:?
[java] ¿ver copia simple imprimir?
// ¿Capacidad inicial del grupo de subprocesos?
private static final int POOL_SIZE = 4;
private ExecutorService executorService ?
@Override ?
public void onCreate(Bundle saveInstanceState) { ?
super.onCreate(savedInstanceState);
// ¿Obtienes el número de CPU que utilizan actualmente el dispositivo?
int cpuNums = Runtime.getRuntime().availableProcessors(); ?
// ¿Número de grupos de subprocesos abiertos por adelantado?
executorService = Executors.newFixedThreadPool(cpuNums * POOL_SIZE);
...Ejecutores = newFixedThreadPool(cpuNums * POOL_SIZE);
executorService.submit( new Runnable() { ?
// Esto realizará un trabajo que requiere mucho tiempo y no implica trabajo en la interfaz de usuario. Si esto sucede, se pasará directamente al hilo principal de la interfaz de usuario.
pano.setImage(loadBitmap(url)); ?
}); ?
...
} ??
Sabemos que la construcción de subprocesos también requiere muchos recursos. Asegúrese de administrarlos y mantenerlos de manera efectiva. No lo haga a la ligera. Si no se ocupa del trabajo del subproceso de una imagen, es posible que no pueda hacer nada. Cuando el escenario de uso se convierte en ListView y GridView, el trabajo del grupo de subprocesos se vuelve particularmente importante. ¿Android no proporciona AsyncTask? ¿Por qué no? De hecho, la capa inferior de AsyncTask también admite grupos de subprocesos. La cantidad de subprocesos asignados por ella es 128 de forma predeterminada, que es mucho mayor que nuestro servicio ejecutor personalizado.