ImageManager2 class has functions such as asynchronous download of pictures from the network, reading local pictures from SD, memory cache, hard disk cache, and image usage animation gradual appearance. It has been applied to applications containing a large number of pictures for more than a year, and no Oom appears.
Android programs often overflow memory, and there are many solutions online, such as soft references, manual call to recycle, etc. But after our practice, we found that these solutions have not achieved good results. Our applications still have a lot of Ooms, especially our applications contain a large number of pictures. After Android 3.0, soft references have basically expired, because virtual machines will be recycled as soon as they encounter soft references, so they cannot bring any performance improvement.
My solution here is HandlerThread (asynchronous loading) + LruCache (memory cache) + DiskLruCache (hard disk cache).
As a programmer, I won’t say much. I just share my code with you, so it’s more convenient to communicate with you using the code.
package ; import ; import ; import ; import ; import ; import ; import ; import ; import ; import ; import ; import ; import ; import ; import ; import ; import ; import ; import ; import ; import ; import ; import .; import ; import ; /** * Image loading class * * @author Moon Bird */ public class ImageManager2 { private static ImageManager2 imageManager; public LruCache<String, Bitmap> mMemoryCache; private static final int DISK_CACHE_SIZE = 1024 * 1024 * 20; // 10MB private static final String DISK_CACHE_SUBDIR = "thumbnails"; public DiskLruCache mDiskCache; private static MyApplication myapp; /** Image loading queue, later in first out */ private Stack<ImageRef> mImageQueue = new Stack<ImageRef>(); /** Image request queue, first in, first out, is used to store sent requests. */ private Queue<ImageRef> mRequestQueue = new LinkedList<ImageRef>(); /** Image loading thread message processor */ private Handler mImageLoaderHandler; /** Is the image loading thread ready */ private boolean mImageLoaderIdle = true; /** Request pictures */ private static final int MSG_REQUEST = 1; /** Image loading is completed */ private static final int MSG_REPLY = 2; /** abort the image loading thread */ private static final int MSG_STOP = 3; /** If the image is loaded from the network, a progressive animation is applied, and if read from the cache, no animation is applied */ private boolean isFromNet = true; /** * Get singleton, only used in UI threads. * * @param context * @return */ public static ImageManager2 from(Context context) { // If not in the ui thread, an exception is thrown if (() != ()) { throw new RuntimeException("Cannot instantiate outside UI thread."); } if (myapp == null) { myapp = (MyApplication) (); } if (imageManager == null) { imageManager = new ImageManager2(myapp); } return imageManager; } /** * Private constructor, guarantee singleton pattern * * @param context */ private ImageManager2(Context context) { int memClass = ((ActivityManager) context .getSystemService(Context.ACTIVITY_SERVICE)).getMemoryClass(); memClass = memClass > 32 ? 32 : memClass; // Use 1/8 of available memory as image cache final int cacheSize = 1024 * 1024 * memClass / 8; mMemoryCache = new LruCache<String, Bitmap>(cacheSize) { protected int sizeOf(String key, Bitmap bitmap) { return () * (); } }; File cacheDir = DiskLruCache .getDiskCacheDir(context, DISK_CACHE_SUBDIR); mDiskCache = (context, cacheDir, DISK_CACHE_SIZE); } /** * Store picture information */ class ImageRef { /** Image Corresponding ImageView Control */ ImageView imageView; /** Image URL address */ String url; /** Image cache path */ String filePath; /** Default graph resource ID */ int resId; int width = 0; int height = 0; /** * Constructor * * @param imageView * @param url * @param resId * @param filePath */ ImageRef(ImageView imageView, String url, String filePath, int resId) { = imageView; = url; = filePath; = resId; } ImageRef(ImageView imageView, String url, String filePath, int resId, int width, int height) { = imageView; = url; = filePath; = resId; = width; = height; } } /** * Show pictures * * @param imageView * @param url * @param resId */ public void displayImage(ImageView imageView, String url, int resId) { if (imageView == null) { return; } if (() != null && ().toString().equals(url)) { return; } if (resId >= 0) { if (() == null) { (resId); } (null); } if (url == null || ("")) { return; } // Add url tag (url); // Read map cache Bitmap bitmap = (url); if (bitmap != null) { setImageBitmap(imageView, bitmap, false); return; } // Generate file name String filePath = urlToFilePath(url); if (filePath == null) { return; } queueImage(new ImageRef(imageView, url, filePath, resId)); } /** * Show thumbnails of pictures with fixed sizes, which are generally used to display pictures in lists, which can greatly reduce memory usage * * @param imageView control to load the image * @param url Loading address * @param resId Default image * @param width Specify width * @param height Specify height */ public void displayImage(ImageView imageView, String url, int resId, int width, int height) { if (imageView == null) { return; } if (resId >= 0) { if (() == null) { (resId); } (null); } if (url == null || ("")) { return; } // Add url tag (url); // Read map cache Bitmap bitmap = (url + width + height); if (bitmap != null) { setImageBitmap(imageView, bitmap, false); return; } // Generate file name String filePath = urlToFilePath(url); if (filePath == null) { return; } queueImage(new ImageRef(imageView, url, filePath, resId, width, height)); } /** * Join the team, enter and exit first * * @param imageRef */ public void queueImage(ImageRef imageRef) { // Delete an existing ImageView Iterator<ImageRef> iterator = (); while (()) { if (().imageView == ) { (); } } // Add a request (imageRef); sendRequest(); } /** * Send a request */ private void sendRequest() { // Turn on the image loading thread if (mImageLoaderHandler == null) { HandlerThread imageLoader = new HandlerThread("image_loader"); (); mImageLoaderHandler = new ImageLoaderHandler( ()); } // Send a request if (mImageLoaderIdle && () > 0) { ImageRef imageRef = (); Message message = (MSG_REQUEST, imageRef); (message); mImageLoaderIdle = false; (imageRef); } } /** * Image loading thread */ class ImageLoaderHandler extends Handler { public ImageLoaderHandler(Looper looper) { super(looper); } public void handleMessage(Message msg) { if (msg == null) return; switch () { case MSG_REQUEST: // Received a request Bitmap bitmap = null; Bitmap tBitmap = null; if ( != null && instanceof ImageRef) { ImageRef imageRef = (ImageRef) ; String url = ; if (url == null) return; // If the local URL reads the SD album picture, it will be read directly without going through DiskCache if (().contains("dcim")) { tBitmap = null; opt = new (); = 1; = true; (url, opt); int bitmapSize = * * 4; = bitmapSize / (1000 * 2000); = false; tBitmap = (url, opt); if ( != 0 && != 0) { bitmap = (tBitmap, , , ThumbnailUtils.OPTIONS_RECYCLE_INPUT); isFromNet = true; } else { bitmap = tBitmap; tBitmap = null; } } else bitmap = (url); if (bitmap != null) { // ("Read from disk cache"); // Write to map cache if ( != 0 && != 0) { if ((url + + ) == null) (url + + , bitmap); } else { if ((url) == null) (url, bitmap); } } else { try { byte[] data = loadByteArrayFromNetwork(url); if (data != null) { opt = new (); = 1; = true; (data, 0, , opt); int bitmapSize = * * 4;// pixels*3 if it's RGB and pixels*4 // if it's ARGB if (bitmapSize > 1000 * 1200) = 2; = false; tBitmap = (data, 0, , opt); if ( != 0 && != 0) { bitmap = ThumbnailUtils .extractThumbnail( tBitmap, , , ThumbnailUtils.OPTIONS_RECYCLE_INPUT); } else { bitmap = tBitmap; tBitmap = null; } if (bitmap != null && url != null) { // Write to the SD card if ( != 0 && != 0) { (url + + , bitmap); (url + + , bitmap); } else { (url, bitmap); (url, bitmap); } isFromNet = true; } } } catch (OutOfMemoryError e) { } } } if (mImageManagerHandler != null) { Message message = ( MSG_REPLY, bitmap); (message); } break; case MSG_STOP: // Received a termination instruction ().quit(); break; } } } /** UI thread message processor */ private Handler mImageManagerHandler = new Handler() { @Override public void handleMessage(Message msg) { if (msg != null) { switch () { case MSG_REPLY: // Received a reply do { ImageRef imageRef = (); if (imageRef == null) break; if ( == null || () == null || == null) break; if (!( instanceof Bitmap) || == null) { break; } Bitmap bitmap = (Bitmap) ; // Non-same ImageView if (!().equals((String) .getTag())) { break; } setImageBitmap(, bitmap, isFromNet); isFromNet = false; } while (false); break; } } // Set idle flag mImageLoaderIdle = true; // If the service is not closed, the next request is sent. if (mImageLoaderHandler != null) { sendRequest(); } } }; /** * Add image to display a progressive animation * */ private void setImageBitmap(ImageView imageView, Bitmap bitmap, boolean isTran) { if (isTran) { final TransitionDrawable td = new TransitionDrawable( new Drawable[] { new ColorDrawable(), new BitmapDrawable(bitmap) }); (true); (td); (300); } else { (bitmap); } } /** * Get an image byte array from the network * * @param url * @return */ private byte[] loadByteArrayFromNetwork(String url) { try { HttpGet method = new HttpGet(url); HttpResponse response = ().execute(method); HttpEntity entity = (); return (entity); } catch (Exception e) { return null; } } /** * Generate the full path name of the cache file according to the url * * @param url * @return */ public String urlToFilePath(String url) { // Extension location int index = ('.'); if (index == -1) { return null; } StringBuilder filePath = new StringBuilder(); // Image access path (().toString()).append('/'); // Image file name (MD5.Md5(url)).append((index)); return (); } /** * ListView will not have residual requests after Activity#onStop. */ public void stop() { // Clear the request queue (); } }
Here are solutions for asynchronous loading, memory cache and hard disk cache, and I hope it will be helpful to everyone's learning.