1. Introduction
Currently, images are inevitably used in android applications. Some images can be changed and need to be pulled from the network at each startup, there are many such scenarios in advertising spaces and pure image applications (such as Baidu meipai.
Now there is a problem: If you pull images from the network every time you start, it will inevitably consume a lot of traffic. In the current situation, for non-wifi users, the traffic is still very expensive. For a very traffic-consuming application, the number of users must be affected. Of course, I think that an application like Baidu meide must also have its internal image Cache Policy. In short, image caching is very important and necessary.
2. Image Cache principles
It is not difficult to implement image caching. A corresponding cache policy is required. Here I use the memory-file-network layer-3 cache mechanism, in which the memory cache includes the strong reference cache and soft reference cache (SoftReference). In fact, the network is not a cache, you can also drag it to the cache hierarchy. When pulling images from the network based on the url, first find the image from the memory. If the image does not exist in the memory, find the image from the cache file. If the image does not exist in the cache file, then pull the image from the network through an http request. In key-value pairs, the cached key of the image is the hash value of the image url, and the value is bitmap. Therefore, according to this logic, as long as a url has been downloaded, its image will be cached.
For SoftReference of objects in Java, if an object has soft references and the memory space is sufficient, the zookeeper recycler will not recycle it. If the memory space is insufficient, the memory of these objects will be recycled. The object can be used by programs as long as the garbage collector does not recycle it. Soft references can be used to implement memory-sensitive high-speed cache. Using Soft references can prevent memory leakage and enhance program robustness.
In terms of code, an ImageManager is used to manage and cache images. The function interface is public void loadBitmap (String url, Handler handler). The url is the image address to be downloaded, handler is the callback after the image is successfully downloaded. It processes the message in handler, and the message contains the image information and bitmap object. The ImageMemoryCache, ImageFileCache, and LruCache used in ImageManager will be described in subsequent articles.
3. Code ImageManager. java Copy codeThe Code is as follows :/*
* Image management
* Gets an image asynchronously and calls the loadImage () function to determine whether the image is loaded from the cache or the network.
* To synchronously retrieve images, call the getBitmap () function to determine whether to load images from the cache or from the network.
* Get the image from the local device only and call getBitmapFromNative ()
* Only attach images from the network and call getBitmapFromHttp ()
*
*/
Public class ImageManager implements IManager
{
Private final static String TAG = "ImageManager ";
Private ImageMemoryCache imageMemoryCache; // memory cache
Private ImageFileCache imageFileCache; // File Cache
// List of images being downloaded
Public static HashMap <String, Handler> ongoingTaskMap = new HashMap <String, Handler> ();
// List of images awaiting download
Public static HashMap <String, Handler> waitingTaskMap = new HashMap <String, Handler> ();
// Number of threads for simultaneous image download
Final static int MAX_DOWNLOAD_IMAGE_THREAD = 4;
Private final Handler downloadStatusHandler = new Handler (){
Public void handleMessage (Message msg)
{
StartDownloadNext ();
}
};
Public ImageManager ()
{
ImageMemoryCache = new ImageMemoryCache ();
ImageFileCache = new ImageFileCache ();
}
/**
* Image retrieval and multi-thread Portal
*/
Public void loadBitmap (String url, Handler handler)
{
// Get it from the memory cache and load it directly
Bitmap bitmap = getBitmapFromNative (url );
If (bitmap! = Null)
{
Logger. d (TAG, "loadBitmap: loaded from native ");
Message msg = Message. obtain ();
Bundle bundle = new Bundle ();
Bundle. putString ("url", url );
Msg. obj = bitmap;
Msg. setData (bundle );
Handler. sendMessage (msg );
}
Else
{
Logger. d (TAG, "loadBitmap: will load by network ");
Downloadbmp onnewthread (url, handler );
}
}
/**
* Download images from a new thread
*/
Private void downloadbmp onnewthread (final String url, final Handler handler)
{
Logger. d (TAG, "ongoingTaskMap 'size =" + ongoingTaskMap. size ());
If (ongoingTaskMap. size ()> = MAX_DOWNLOAD_IMAGE_THREAD)
{
Synchronized (waitingTaskMap)
{
WaitingTaskMap. put (url, handler );
}
}
Else
{
Synchronized (ongoingTaskMap)
{
OngoingTaskMap. put (url, handler );
}
New Thread ()
{
Public void run ()
{
Bitmap bmp = getBitmapFromHttp (url );
// No matter whether the download is successful or not, it is removed from the download queue, and then determined by the business logic whether or not to re-Download
// HttpClientRequest is used to download the image, and the reconnection mechanism is already in place.
Synchronized (ongoingTaskMap)
{
OngoingTaskMap. remove (url );
}
If (downloadStatusHandler! = Null)
{
DownloadStatusHandler. sendEmptyMessage (0 );
}
Message msg = Message. obtain ();
Msg. obj = bmp;
Bundle bundle = new Bundle ();
Bundle. putString ("url", url );
Msg. setData (bundle );
If (handler! = Null)
{
Handler. sendMessage (msg );
}
}
}. Start ();
}
}
/**
* Load a single bitmap from memory, cached files, and network in sequence, regardless of the thread
*/
Public Bitmap getBitmap (String url)
{
// Obtain the image from the memory cache
Bitmap bitmap = imageMemoryCache. getBitmapFromMemory (url );
If (bitmap = null)
{
// Get it in the File Cache
Bitmap = imageFileCache. getImageFromFile (url );
If (bitmap! = Null)
{
// Add to memory cache
ImageMemoryCache. addBitmapToMemory (url, bitmap );
}
Else
{
// Obtain from Network
Bitmap = getBitmapFromHttp (url );
}
}
Return bitmap;
}
/**
* Obtain bitmap from memory or cache files
*/
Public Bitmap getBitmapFromNative (String url)
{
Bitmap bitmap = null;
Bitmap = imageMemoryCache. getBitmapFromMemory (url );
If (bitmap = null)
{
Bitmap = imageFileCache. getImageFromFile (url );
If (bitmap! = Null)
{
// Add to memory cache
ImageMemoryCache. addBitmapToMemory (url, bitmap );
}
}
Return bitmap;
}
/**
* Downloading images over the network is irrelevant to the thread
*/
Public Bitmap getBitmapFromHttp (String url)
{
Bitmap bmp = null;
Try
{
Byte [] tmpPicByte = getImageBytes (url );
If (tmpPicByte! = Null)
{
Bmp = BitmapFactory. decodeByteArray (tmpPicByte, 0,
TmpPicByte. length );
}
TmpPicByte = null;
}
Catch (Exception e)
{
E. printStackTrace ();
}
If (bmp! = Null)
{
// Add to File Cache
ImageFileCache. saveBitmapToFile (bmp, url );
// Add to memory cache
ImageMemoryCache. addBitmapToMemory (url, bmp );
}
Return bmp;
}
/**
* Download link image resources
*
* @ Param url
*
* @ Return Image
*/
Public byte [] getImageBytes (String url)
{
Byte [] pic = null;
If (url! = Null &&! "". Equals (url ))
{
Requester request = RequesterFactory. getRequester (
Requester. REQUEST_REMOTE, RequesterFactory. IMPL_HC );
// Execute the request
MyResponse myResponse = null;
MyRequest mMyRequest;
MMyRequest = new MyRequest ();
MMyRequest. setUrl (url );
MMyRequest. addHeader (HttpHeader. REQ. ACCEPT_ENCODING, "identity ");
InputStream is = null;
ByteArrayOutputStream baos = null;
Try {
MyResponse = request.exe cute (mMyRequest );
Is = myResponse. getInputStream (). getImpl ();
Baos = new ByteArrayOutputStream ();
Byte [] B = new byte [512];
Int len = 0;
While (len = is. read (B ))! =-1)
{
Baos. write (B, 0, len );
Baos. flush ();
}
Pic = baos. toByteArray ();
Logger. d (TAG, "icon bytes. length =" + pic. length );
}
Catch (Exception e3)
{
E3.printStackTrace ();
Try
{
Logger. e (TAG,
"Download shortcut icon faild and responsecode ="
+ MyResponse. getStatusCode ());
}
Catch (Exception e4)
{
E4.printStackTrace ();
}
}
Finally
{
Try
{
If (is! = Null)
{
Is. close ();
Is = null;
}
}
Catch (Exception e2)
{
E2.printStackTrace ();
}
Try
{
If (baos! = Null)
{
Baos. close ();
Baos = null;
}
}
Catch (Exception e2)
{
E2.printStackTrace ();
}
Try
{
Request. close ();
}
Catch (Exception e1)
{
E1.printStackTrace ();
}
}
}
Return pic;
}
/**
* Retrieve the first task waiting for the queue and start downloading.
*/
Private void startDownloadNext ()
{
Synchronized (waitingTaskMap)
{
Logger. d (TAG, "begin start next ");
Iterator iter = waitingTaskMap. entrySet (). iterator ();
While (iter. hasNext ())
{
Map. Entry entry = (Map. Entry) iter. next ();
Logger. d (TAG, "WaitingTaskMap isn't null, url =" + (String) entry. getKey ());
If (entry! = Null)
{
WaitingTaskMap. remove (entry. getKey ());
Downloadbmp onnewthread (String) entry. getKey (), (Handler) entry. getValue ());
}
Break;
}
}
}
Public String startDownloadNext_ForUnitTest ()
{
String urlString = null;
Synchronized (waitingTaskMap)
{
Logger. d (TAG, "begin start next ");
Iterator iter = waitingTaskMap. entrySet (). iterator ();
While (iter. hasNext ())
{
Map. Entry entry = (Map. Entry) iter. next ();
UrlString = (String) entry. getKey ();
WaitingTaskMap. remove (entry. getKey ());
Break;
}
}
Return urlString;
}
/**
* Rounded corner of the image
* @ Param bitmap: Incoming bitmap
* @ Param pixels: the degree of the rounded corner. The larger the value, the larger the rounded corner.
* @ Return bitmap: bitmap with rounded corners
*/
Public static Bitmap toRoundCorner (Bitmap bitmap, int pixels)
{
If (bitmap = null)
Return null;
Bitmap output = Bitmap. createBitmap (bitmap. getWidth (), bitmap. getHeight (), Config. ARGB_8888 );
Canvas canvas = new Canvas (output );
Final int color = 0xff0000242;
Final Paint paint = new Paint ();
Final Rect rect = new Rect (0, 0, bitmap. getWidth (), bitmap. getHeight ());
Final RectF rectF = new RectF (rect );
Final float roundPx = pixels;
Paint. setAntiAlias (true );
Canvas. drawARGB (0, 0, 0, 0 );
Paint. setColor (color );
Canvas. drawRoundRect (rectF, roundPx, roundPx, paint );
Paint. setXfermode (new porterduxfermode (Mode. SRC_IN ));
Canvas. drawBitmap (bitmap, rect, rect, paint );
Return output;
}
Public byte managerId ()
{
Return IMAGE_ID;
}
}