Teaches you how to write image loading and loading policies for the Android ImageLoader framework,
In writing the initial configuration and Request Scheduling of the Android ImageLoader framework, we have already talked about the design and implementation of ImageLoader's request configuration and scheduling. Today, we will take a closer look at the image loading process and loading strategies (including loading in order and loading in reverse order). Here I will share some of my design decisions, you are also welcome to give me suggestions.
Implementation of image loading Loader and LoaderManager
In the previous article, we talked about Loader and LoaderManager in the initial configuration and Request Scheduling of the Android ImageLoader framework. ImageLoader constantly retrieves requests from the queue and parses the request to the image uri schema. From the schema format, you can know where the image is stored. For example, the schema of the network image object is http or https, the schema of the image stored on the SD card is file, and schemae corresponds to Loader. According to the schema, we obtain the corresponding Loader from LoaderManager to load the image. This design ensures the scalability of SimpleImageLoader to load image types, which is why the package loader is added. You only need to construct the image uri according to the uri format, implement your own Loader class, and then inject the Loader object into LoaderManager. The run function in RequestDispatcher is as follows:
@ Override public void run () {try {while (! This. isInterrupted () {final BitmapRequest request = mRequestQueue. take (); if (request. isCancel) {continue;} final String schema = parseSchema (request. imageUri); // obtain loader imageLoader = LoaderManager according to schema. getInstance (). getLoader (schema); imageLoader. loadImage (request) ;}} catch (InterruptedException e) {Log. I ("", "### request the distributor to exit ");}}
Loader only defines one interface and only uses one method to load images.
public interface Loader { public void loadImage(BitmapRequest result);}
Abstract is to be extensible. By defining this interface, we can inject our own image loading implementation class. For example, load from resources and assets. Whether loading images from the network or locally, we can perform the following steps to attach images:
We can find that these logics are generic no matter where the image is loaded, so I abstracted an AbsLoader class. It abstracts these processes and only submits the changed parts to sub-classes for processing. This is equivalent to AbsLoader encapsulating a logical framework (you can think about the design pattern used). The Code is as follows:
/*** @ Author mrsimple */public abstract class AbsLoader implements Loader {/*** image cache */private static BitmapCache mCache = SimpleImageLoader. getInstance (). getConfig (). bitmapCache; @ Override public final void loadImage (BitmapRequest request) {// 1. Obtain Bitmap resultBitmap = mCache from the cache. get (request); Log. e ("", "### cache:" + resultBitmap + ", uri =" + request. imageUri); if (resultBitmap = null) {showLoading (request); // 2. if no cache exists, call onLoaderImage to load the image resultBitmap = onLoadImage (request ); // 3. cache image cacheBitmap (request, resultBitmap);} else {request. justCacheInMem = true;} // 4. Deliver the result to the UI thread deliveryToUIThread (request, resultBitmap);}/** the hook method for loading the image, leave it to the subclass for processing * @ param request * @ return */protected abstract Bitmap onLoadImage (BitmapRequest request); // Code omitted}
The Code logic implements a template function as described above. The changed part is onLoadImage. Here the subclass implements the true method of loading images. For example, attach images to a network.
/*** @ Author mrsimple */public class UrlLoader extends AbsLoader {@ Override public Bitmap onLoadImage (BitmapRequest request) {final String imageUrl = request. imageUri; FileOutputStream fos = null; InputStream is = null; try {URL url = new URL (imageUrl); final HttpURLConnection conn = (HttpURLConnection) url. openConnection (); is = new BufferedInputStream (conn. getInputStream (); is. mark (is. available (); final InputStream inputStream = is; BitmapDecoder bitmapDecoder = new BitmapDecoder () {@ Override public Bitmap decodeBitmapWithOption (Options options) {Bitmap bitmap = BitmapFactory. decodeStream (inputStream, null, options); // if (options. inJustDecodeBounds) {try {inputStream. reset ();} catch (IOException e) {e. printStackTrace () ;}} else {// disable stream conn. disconnect () ;}return bitmap ;}; return bitmapDecoder. decodeBitmap (request. getImageViewWidth (), request. getImageViewHeight ();} catch (Exception e) {} finally {IOUtil. closeQuietly (is); IOUtil. closeQuietly (fos);} return null ;}}
When initializing ImageLoader, several loaders are injected into LoaderManager by default. Then, when loading images, ImageLoader obtains the corresponding Loader Based on the Image schema to complete the loading function.
/** * */ private LoaderManager() { register(HTTP, new UrlLoader()); register(HTTPS, new UrlLoader()); register(FILE, new LocalLoader()); }
Load Policy
The loading policy is the rule by which ImageLoader loads your request after your image loading request is submitted. The default value is the SerialPolicy Policy (FIFO). Whoever is in front of the queue is who is preferentially executed. But things are often not that simple. When we scroll the ListView, we want the images added to the Request queue to be loaded first, so they are on the mobile phone screen at this time, so we added a ReversePolicy. Attention. We cannot be specific or abstract this possibility! So I defined the LoadPolicy interface, which is used to compare two requests to define the sorting principle.
public interface LoadPolicy { public int compare(BitmapRequest request1, BitmapRequest request2);}
Because our request queue uses the priority queue PriorityBlockingQueue, our BitmapRequest implements the Comparable interface. In the BitmapRequest function, compareTo is delegated to the compare of the LoadPolicy object.
@Override public int compareTo(BitmapRequest another) { return mLoadPolicy.compare(this, another); }
Let's take a look at the default load policy, that is, loading in order. Requests first added to the queue are first executed.
/*** Sequential loading policy ** @ author mrsimple */public class SerialPolicy implements LoadPolicy {@ Override public int compare (BitmapRequest request1, BitmapRequest request2) {// execute return request1.serialNum-request2.serialNum;} according to the sequence of the serial numbers added to the queue ;}}
For reverse loading:
/*** Reverse loading policy, that is, loading from the last request to the queue ** @ author mrsimple */public class ReversePolicy implements LoadPolicy {@ Override public int compare (BitmapRequest request1, bitmapRequest request2) {// note that Bitmap requests must first execute the latest request to join the queue. The ImageLoader policy return request2.serialNum-request1.serialNum ;}}
Oh, think this is not a rule model! The original mode is everywhere. When you get used to it, you will find that the mode has been applied to your code in an invisible way. As shown above, the policy is implemented in a simple way. You only need to specify the policy When configuring ImageLoader. You can also implement the policy class as needed and inject it to ImageLoader. This ensures flexibility and scalability.
Summary
The Loader and LoaderManager ensure the scalability of the image source to be loaded, that is, images can be stored on the network, in the SD card, and in the res folder, and so on, so that a Loader can be loaded from a specific location, register a schema for the Loader. When loading an image, obtain the schema Based on the image path, get the Loader through the schema, and load the image through the Loader.
The image loading policy is customized through the LoadPolicy abstraction. You can implement the loading policy on your own. This ensures the flexibility. Of course, the later image cache also requires the same flexibility. As mentioned in my six principles of object orientation in public technology, the several principles of object orientation are eventually converted into several simple keywords: abstraction, single responsibility, and minimization. After understanding these ideas, I think your code quality should be improved.
ImageLoader library, image cache must be indispensable. The Caching Design of images is still an old saying. I will explain it later ~
Github address
SimpleImageLoader.