Android-UIL image cache framework source code analysis

Source: Internet
Author: User

Android-UIL image cache framework source code analysis

Android-Universal-Image-Loader is an open-source Image cache framework on github. It provides Image MemoryCache and DiskCache functions, and supports loading images from network, local, and contentProvider.

 

Acceptable URIs examples
"Http://site.com/image.png" // from Web "file: // mnt/sdcard/image.png" // from SD card "file: /// mnt/sdcard/video.mp4 "// from SD card (video thumbnail)" content: // media/external/images/media/13 "// from content provider" content: // media/external/video/media/13 "// from content provider (video thumbnail)" assets: // image.png "// from assets" drawable: // "+ R. drawable. img // from drawables (the non-9patch image S) // is usually not used.

 

NOTE: Usedrawable://Only if you really need it! Alwaysconsider the native wayto load drawables-ImageView.setImageResource(...)Instead of usingImageLoader.

 

Next I will analyze the process of this open-source project from the source code perspective:

First, write a simple example:

 

 ImageLoader imageLoader = ImageLoader.getInstance();        ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(this).build();        imageLoader.init(config);        imageLoader.displayImage("http://pic32.nipic.com/20130829/12906030_124355855000_2.png", image);
The first line needs to instantiate ImageLoader first. It adopts the singleton mode for instantiation.

 

Then you need to initialize the configuration information for imageLoader, that is, the ImageLoaderConfiguration class. If it is not initialized, an exception is reported.

Next, let's take a look at the variables that can be initialized in this class:

 

Final Resources resources; // used to load the final int maxImageWidthForMemoryCache file of the app's Chinese source file; // The maximum image width in the memory cache is the screen width by default; // The disk cache width is unlimited by default. final int maxImageHeightForDiskCache; // same as final BitmapProcessor processorForDiskCache; // bitmap processor disk cache processor final Executor taskExecutor; // task Executor final Executor taskexecutorforcachedi; // cache image task executor final boolean customExecutor; // custom task executor final boolean customExecutorForCachedImages; // custom cache image task executor final int threadPoolSize; // The default thread pool size is 3 final int threadPriority; // The thread priority final QueueProcessingType tasksProcessingType; // The queue type can be FIFO (first-in-first-out) LIFO (later-in-first-out) final MemoryCache memoryCache; // memory cache final DiskCache; // disk cache final ImageDownloader downloader; // final ImageDecoder decoder; // final DisplayImageOptions defadisplaydisplayimageoptions; // final ImageDownloader networkDeniedDownloader; // final ImageDownloader slowNetworkDownloader; // picture download Tool
In this configuration class, you can initialize the above content. Below are some default initialization items.

 

 

File cacheDir = StorageUtils.getCacheDirectory(context);  ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(context)          .memoryCacheExtraOptions(480, 800) // default = device screen dimensions          .diskCacheExtraOptions(480, 800, CompressFormat.JPEG, 75, null)          .taskExecutor(...)          .taskExecutorForCachedImages(...)          .threadPoolSize(3) // default          .threadPriority(Thread.NORM_PRIORITY - 1) // default          .tasksProcessingOrder(QueueProcessingType.FIFO) // default          .denyCacheImageMultipleSizesInMemory()          .memoryCache(new LruMemoryCache(2 * 1024 * 1024))          .memoryCacheSize(2 * 1024 * 1024)          .memoryCacheSizePercentage(13) // default          .diskCache(new UnlimitedDiscCache(cacheDir)) // default          .diskCacheSize(50 * 1024 * 1024)          .diskCacheFileCount(100)          .diskCacheFileNameGenerator(new HashCodeFileNameGenerator()) // default          .imageDownloader(new BaseImageDownloader(context)) // default          .imageDecoder(new BaseImageDecoder()) // default          .defaultDisplayImageOptions(DisplayImageOptions.createSimple()) // default          .writeDebugLogs()          .build();  

 


You can select the disk and memory cache policies as needed.

 

Next we will continue to look:

 

public synchronized void init(ImageLoaderConfiguration configuration) {if (configuration == null) {throw new IllegalArgumentException(ERROR_INIT_CONFIG_WITH_NULL);}if (this.configuration == null) {L.d(LOG_INIT_CONFIG);engine = new ImageLoaderEngine(configuration);this.configuration = configuration;} else {L.w(WARNING_RE_INIT_CONFIG);}}
The init method is used to pass in the configuration information and initialize the ImageLoaderEngine Class Based on the configuration information (mainly to initialize TaskExecutor)

 

The displayImage method is followed.

Next let's look at the displayImage method.

The most important overload of this method parameter is:

Public void displayImage (String uri, ImageAware imageAware, DisplayImageOptions options,
ImageSize targetSize, ImageLoadingListener listener, ImageLoadingProgressListener progressListener)

Parameters include the options for displaying images in the image uri control, image size, image loading monitoring, and image loading progress bar monitoring. You can also set more options in options.

 

The following describes the source code of the displayImage method (because it is too long to be viewed step by step ):

 

checkConfiguration();  if (imageAware == null) {throw new IllegalArgumentException(ERROR_WRONG_ARGUMENTS);}if (listener == null) {listener = defaultListener;}if (options == null) {options = configuration.defaultDisplayImageOptions;}

The first line is very simple to check whether there is a configuration file

 

Private void checkConfiguration (){
If (configuration = null ){
Throw new IllegalStateException (ERROR_NOT_INIT );
}
}

The following lines are similar. If the variable is null,

 

if (TextUtils.isEmpty(uri)) {engine.cancelDisplayTaskFor(imageAware);listener.onLoadingStarted(uri, imageAware.getWrappedView());if (options.shouldShowImageForEmptyUri()) {imageAware.setImageDrawable(options.getImageForEmptyUri(configuration.resources));} else {imageAware.setImageDrawable(null);}listener.onLoadingComplete(uri, imageAware.getWrappedView(), null);return;}if (targetSize == null) {targetSize = ImageSizeUtils.defineTargetSizeForView(imageAware, configuration.getMaxImageSize());}

Continue. The first line determines whether the uri is null. If it is null, cancel the task in the engine directly (I don't think I have added the task in the engine yet, why do we need to remove it, but it must be true to remove it first .)

 

After the listener start is called, if the uri is empty and an image needs to be set to be empty, you can directly set the image to be set when the image is empty. If this is not set, simply do not display the image.

Then call the complete callback and return the result. This is the case where the uri is null and there is no need to perform too many operations or cache.

If the image size is set to null, set the size of the image to be displayed based on the size set by the control.

Public static ImageSize defineTargetSizeForView (ImageAware imageAware, ImageSize maxImageSize ){
Int width = imageAware. getWidth ();
If (width <= 0) width = maxImageSize. getWidth ();


Int height = imageAware. getHeight ();
If (height <= 0) height = maxImageSize. getHeight ();


Return new ImageSize (width, height );
}

 

String memoryCacheKey = MemoryCacheUtils.generateKey(uri, targetSize);engine.prepareDisplayTaskFor(imageAware, memoryCacheKey);listener.onLoadingStarted(uri, imageAware.getWrappedView());
Then a key is generated based on the uri and target size, and the task is put into the engine collection.

 

Callback started Method

 

Bitmap bmp = configuration. memoryCache. get (memoryCacheKey); // obtain if (bmp! = Null &&! Bmp. isRecycled () {// if it exists and is not recycled. d (LOG_LOAD_IMAGE_FROM_MEMORY_CACHE, memoryCacheKey); if (options. shouldPostProcess () {// If postProcess execution is set, this parameter is not set by default. You can process the image in advance. ImageLoadingInfo imageLoadingInfo = new ImageLoadingInfo (uri, imageAware, targetSize, memoryCacheKey, options, listener, progressListener, engine. getLockForUri (uri); ProcessAndDisplayImageTask displayTask = new ProcessAndDisplayImageTask (engine, bmp, imageLoadingInfo, defineHandler (options); if (options. isSyncLoading () {displayTask. run ();} else {engine. submit (displayTask) ;}} else {options. getDisplayer (). display (bmp, imageAware, LoadedFrom. MEMORY_CACHE); listener. onLoadingComplete (uri, imageAware. getWrappedView (), bmp );}}

Next is the important part.

 

First, the first line retrieves bitmap from the memory cache based on the key

The second line is used to determine whether or not the memory is recycled or not. If there is a switch but it is not recycled, it is relatively simple.

Process the image and then display it.
The main purpose of the preceding task code is to encapsulate some processing operations on the image before the image is displayed and then display the image.

When the last five lines of the else statement do not need to process the image before the image is displayed, you can directly use displaywe to display the image and call back the complete function.

Here, this displayer can be set to fadeIn (transparency) and Circle displayer (rounded corner ).

Public void display (Bitmap bitmap, ImageAware imageAware, LoadedFrom loadedFrom ){
ImageAware. setImageBitmap (bitmap );
}

The general displayer display is very simple. See the above Code.

 

 

Else {// if the memory cache does not exist or if (options. shouldShowImageOnLoading () {imageAware. setImageDrawable (options. getImageOnLoading (configuration. resources);} else if (options. isResetViewBeforeLoading () {imageAware. setImageDrawable (null);} ImageLoadingInfo imageLoadingInfo = new ImageLoadingInfo (uri, imageAware, targetSize, memoryCacheKey, options, listener, progressListener, engine. getLockForUri (uri); LoadAndDisplayImageTask displayTask = new LoadAndDisplayImageTask (engine, imageLoadingInfo, defineHandler (options); if (options. isSyncLoading () {// indicates that displayTask is executed directly in synchronization. run ();} else {// if it is not synchronized, it is handed over to the thread pool object execution engine with Executor, which contains the thread pool engine. submit (displayTask );}}
If the source code is not in the memory cache, it will be troublesome. The general operation step is to first load the image from the original image, get the image, put it into the hard disk and memory, and then display it.

 

The second line is set if the image needs to be displayed during loading. Otherwise, no image is set.

Set the information ImageLoadingInfo and task LoadAndDisplayImageTask when loading.

Based on whether or not to execute the task synchronously

Next, let's take a look at the run method of displayTask.

 

if (waitIfPaused()) return;if (delayIfNeed()) return;ReentrantLock loadFromUriLock = imageLoadingInfo.loadFromUriLock;L.d(LOG_START_DISPLAY_IMAGE_TASK, memoryCacheKey);if (loadFromUriLock.isLocked()) {L.d(LOG_WAITING_FOR_IMAGE_LOADED, memoryCacheKey);}loadFromUriLock.lock();
The first two rows are

 

If true is returned for waitIfPaused () and delayIfNeed (), it is returned directly from the run () method without executing the following logic,

The two methods are mainly used to determine whether a task is interrupted or delayed.

Continue to check that the fourth row obtains a lock and then locks it to prevent repeated loading.

Assume that an item in a ListView is being acquired, and the item is rolled out of the page. If no lock is applied after the item is rolled in, this item will be loaded again. If the image is rolled frequently in a short period of time, the image will be requested multiple times on the network, therefore, a ReentrantLock object is matched according to the image Url, so that requests with the same Url will wait in the last line. After the image is loaded, the ReentrantLock will be released, requests with the same Url will continue to execute the following code.

 

Bitmap bmp; try {checkTaskNotActual (); // check whether the task is still in bmp = configuration. memoryCache. get (memoryCacheKey); // obtain bmp if (bmp = null | bmp. isRecycled () {// if the memory cache does not contain bmp = tryLoadBitmap (); // check whether there are any images in the hard disk if they are loaded from the hard disk if they are not read from the network and cached to the hard disk if (bmp = null) return; // listener callback already was firedcheckTaskNotActual (); checkTaskInterrupted (); if (options. shouldPreProcess () {// whether the image needs to be processed before it is displayed L. d (LOG_PREPROCESS_IMAGE, memoryCacheKey); bmp = options. getPreProcessor (). process (bmp); if (bmp = null) {L. e (ERROR_PRE_PROCESSOR_NULL, memoryCacheKey) ;}} if (bmp! = Null & options. isCacheInMemory () {// cache the loaded image to the memory. d (LOG_CACHE_IMAGE_IN_MEMORY, memoryCacheKey); configuration. memoryCache. put (memoryCacheKey, bmp) ;}} else {// In the memory cache, from is set as the memory cache loadedFrom = LoadedFrom. MEMORY_CACHE; L. d (LOG_GET_IMAGE_FROM_MEMORY_CACHE_AFTER_WAITING, memoryCacheKey);} // process the loaded image. if (bmp! = Null & options. shouldPostProcess () {L. d (LOG_POSTPROCESS_IMAGE, memoryCacheKey); bmp = options. getPostProcessor (). process (bmp); if (bmp = null) {L. e (authorization, memoryCacheKey) ;}} checkTaskNotActual (); checkTaskInterrupted () ;}catch (TaskCancelledException e) {fireCancelEvent (); return ;} finally {loadFromUriLock. unlock (); // release the lock} // The following two rows are the tasks for displaying images. Loading bitmap is now loaded and cached to the memory and disk. You only need to display it. // oncancle and the oncomplete method calls the progress bar to call the onstart method to call back the DisplayBitmapTask displayBitmapTask = new DisplayBitmapTask (bmp, imageLoadingInfo, engine, loadedFrom); runTask (displayBitmapTask, syncLoading, handler, engine );

The above code is the cache part. First, read from the memory according to the key. If the memory is not or has been recycled, execute the tryLoadBitmap Method for a long time.

 

Read data from the disk first. If the data is not loaded from the network

Let's take a look at this method.

 

Private Bitmap tryLoadBitmap () throws TaskCancelledException {Bitmap bitmap = null; try {File imageFile = configuration. diskCache. get (uri); // read from disk if (imageFile! = Null & imageFile. exists () & imageFile. length ()> 0) {// if L. d (LOG_LOAD_IMAGE_FROM_DISK_CACHE, memoryCacheKey); loadedFrom = LoadedFrom. DISC_CACHE; checkTaskNotActual (); // check whether the task actually exists bitmap = decodeImage (Scheme. FILE. wrap (imageFile. getAbsolutePath (); // parse bitmap} if (bitmap = null | bitmap. getWidth () <= 0 | bitmap. getHeight () <= 0) {// if the hard disk does not exist, download it from the network and cache it to the hard disk. d (LOG_LOAD_IMAGE_FROM_NETWORK, MemoryCacheKey); loadedFrom = LoadedFrom. NETWORK; String imageUriForDecoding = uri; if (options. isCacheOnDisk () & tryCacheImageOnDisk () {// The tryCahcheImageDisk method downloads from the network and caches it to the hard disk imageFile = configuration. diskCache. get (uri); if (imageFile! = Null) {imageUriForDecoding = Scheme. FILE. wrap (imageFile. getAbsolutePath (); // change the path to the appropriate format} checkTaskNotActual (); bitmap = decodeImage (imageUriForDecoding); // decode the image if (bitmap = null | bitmap. getWidth () <= 0 | bitmap. getHeight () <= 0) {fireFailEvent (FailType. DECODING_ERROR, null); // if a failure occurs, set the failure image and call back the failed function }}catch (IllegalStateException e) {fireFailEvent (FailType. NETWORK_DENIED, null);} catch (TaskCancelledException e) {throw e;} catch (IOException e) {L. e (e); fireFailEvent (FailType. IO_ERROR, e);} catch (OutOfMemoryError e) {L. e (e); fireFailEvent (FailType. OUT_OF_MEMORY, e);} catch (Throwable e) {L. e (e); fireFailEvent (FailType. UNKNOWN, e);} return bitmap ;}
TryCacheImageOnDisk is used to retrieve images from the network and cache them to the disk when they are not retrieved from the disk.

 

 

/** @ ReturnTrue-If image was downloaded successfully;False-Otherwise */private boolean tryCacheImageOnDisk () throws TaskCancelledException {L. d (LOG_CACHE_IMAGE_ON_DISK, memoryCacheKey); boolean loaded; try {loaded = downloadImage (); // This method downloads the image from the network if (loaded) {int width = configuration. maxImageWidthForDiskCache; int height = configuration. maxImageHeightForDiskCache; if (width> 0 | height> 0) {L. d (LOG_RESIZE_CACHED_IMAGE_FILE, memoryCacheKey); resizeAndSaveImage (width, height); // TODO: process boolean result }}catch (IOException e) {L. e (e); loaded = false;} return loaded ;}
Private boolean downloadImage () throws IOException {InputStream is = getDownloader (). getStream (uri, options. getExtraForDownloader (); // download if (is = null) {L. e (ERROR_NO_IMAGE_STREAM, memoryCacheKey); return false;} else {try {return configuration. diskCache. save (uri, is, this); // cache to disk} finally {IoUtils. closeSilently (is );}}}
In this way, the image cache and display are completed.

 

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.