Overview
Remote Image Retrieval is a commonly used function for client-server applications. image resources consume a large amount of traffic, it will cause a user to crash, and the mobile phone traffic will be used up without knowing it. If the user finds that your application has consumed the mobile phone traffic, you can imagine what fate your application will face.
Another problem is the loading speed. If the image loading speed in the application is slow, the user will wait for the crash.
So how can we get and manage image resources?
* Asynchronous download
* Local cache
Asynchronous download:
As we all know, in Android applications, if the UI thread does not respond within 5 seconds, it will throw a non-response exception. for remote access to large resources, this exception will easily be thrown out, so how can we avoid this problem. There are two ways to do this in Android:
Start a new thread to obtain resources. After the completion, send messages through the handler mechanism and process messages in the UI thread to obtain images in the asynchronous thread, then, the process of updating the UI thread through handler message.
Use asynctask provided in Android.
The specific method is not described here. You can check the API, or Google or Baidu. The local cache is used here.
Local cache:
For image resources, you cannot allow applications to download images remotely every time they obtain them. This wastes resources, however, you cannot place all image resources in the memory (although loading will be faster), because image resources usually occupy a large amount of memory space, which may easily lead to oom. If the downloaded image is saved to the sdcard, will it be directly obtained from the sdcard next time? This is also a practice. I have read it and many applications adopt this method. Using LRU and other algorithms can ensure that the space occupied by sdcard is only a small part, this not only ensures image loading, reduces traffic, but also makes the sdcard space only occupy a small part. Another approach is to store resources directly in the memory and set the expiration time and LRU rules.
Save sdcard:
To open up a certain amount of space on the sdcard, you must first determine whether the remaining space on the sdcard is sufficient. If there is enough space, you can open up some space, such as 10 m.
When you need to obtain an image, you must first find it from the directory on the sdcard. If you find the image, use the image and update the last time the image was used. If not found, download through URL
Go to the server to download the image. If the download is successful, place it on the sdcard and use it. If the download fails, a Retry Mechanism should be available. For example, three times.
After the download is successful and saved to the sdcard, You need to determine whether the 10 MB space is used up. If the space is not used up, save it. If the space is insufficient, delete some resources that have not been used recently according to the LRU rule.
Key code:
Save the image to the SD card:
Private void savebmptosd (Bitmap BM, stringurl) {If (Bm = NULL) {log. W (TAG, "trying to savenull bitmap"); return;} // determine the space if (free_sd_space_needed_to_cache> freespaceonsd () on the sdcard {log. W (TAG, "Low Free Space onsd, do not cache"); return;} string filename = converturltofilename (URL); string dir = getdirectory (filename ); file file = new file (DIR + "/" + filename); try {file. createnewfile (); outputstream outstream = newfileoutputstream (File); BM. compress (bitmap. compressformat. JPEG, 100, outstream); outstream. flush (); outstream. close (); log. I (TAG, "image saved tosd");} catch (filenotfoundexception e) {log. W (TAG, "filenotfoundexception");} catch (ioexception e) {log. W (TAG, "ioexception ");}}
Calculate the space on the sdcard:
/*** Calculate the remaining space on the sdcard * @ return */private int freespaceonsd () {statfs stat = newstatfs (environment. getexternalstoragedirectory (). getpath (); double sdfreemb = (double) stat. getavailableblocks () * (double) stat. getblocksize ()/MB; Return (INT) sdfreemb ;}
Last modification time of the file:
/*** Last modification time of the file * @ Param dir * @ Param filename */private void updatefiletime (string Dir, string filename) {file = new file (Dir, filename); long newmodifiedtime = system. currenttimemillis (); file. setlastmodified (newmodifiedtime );}
Local Cache Optimization:
/*** Calculate the file size in the storage directory, when the total file size is greater than the specified cache_size or the remaining sdcard space is less than the free_sd_space_needed_to_cache limit *, delete the file not used recently * @ Param dirpath * @ Param filename */private void removecache (string dirpath) {file dir = new file (dirpath); file [] files = dir. listfiles (); If (Files = NULL) {return;} int dirsize = 0; For (INT I = 0; I <files. length; I ++) {If (files [I]. getname (). contains (wholesale_conv) {dirsize + = files [I]. length () ;}} if (dirsize> cache_size * MB | free_sd_space_needed_to_cache> freespaceonsd () {int removefactor = (INT) (0.4 * files. length) + 1); arrays. sort (files, newfilelastmodifsort (); log. I (TAG, "Clear some expiredcache Files"); For (INT I = 0; I <removefactor; I ++) {If (files [I]. getname (). contains (wholesale_conv) {files [I]. delete () ;}}}/*** Delete expired files * @ Param dirpath * @ Param filename */private void removeexpiredcache (stringdirpath, string filename) {file = new file (dirpath, filename); If (system. currenttimemillis ()-file. lastmodified ()> mtimediff) {log. I (TAG, "Clear some expiredcache Files"); file. delete ();}}
Sort files by Time:
/*** Todo sorts Objects Based on the last modification time **/classfilelastmodifsort implements comparator <File> {public int compare (File arg0, file arg1) {If (arg0.lastmodified ()> arg1.lastmodified () {return 1;} else if (arg0.lastmodified () = arg1.lastmodified () {return 0;} else {return-1 ;}}}
Memory storage:
If the data is saved in the memory, you can only save a certain amount, but cannot keep it in it. You need to set the data expiration time, LRU, and other algorithms. Here, you can put commonly used data in one cache (a), but not frequently in another cache (B ). To obtain data, first obtain data from A. If a does not exist, then retrieve data from B. Data in B is mainly from LRU in A. the memory reclaim here is mainly for Memory B, so that data in a can be effectively hit.
First define a cache:
Private Final hashmap <string, bitmap> mhardbitmapcache = new linkedhashmap <string, bitmap> (hard_cache_capacity/2, 0.75f, true) {@ override protected booleanremoveeldestentry (linkedhashmap. entry <string, bitmap> eldest) {If (SIZE ()> hard_cache_capacity) {// when the size of map is greater than 30, put the key that is not commonly used recently into msoftbitmapcache, this ensures the efficiency of mhardbitmapcache. put (eldest. getkey (), newsoftreference <bitmap> (eldest. getvalue (); Return true;} else return false ;}};
And then set it to B cache:
/*** When the key of the mhardbitmapcache is greater than 30, the unused key is put into the cache according to the LRU algorithm. * Bitmap uses softreference. When the memory space is insufficient, bitmap in the cache will be reclaimed */private final staticconcurrenthashmap <string, softreference <bitmap> msoftbitmapcache = new concurrenthashmap <string, softreference <bitmap> (hard_cache_capacity/2 );
Obtain data from the cache:
/*** Obtain the image from the cache */private bitmap getbitmapfromcache (stringurl) {// obtain synchronized (mhardbitmapcache) {final Bitmap bitmap = mhardbitmapcache from the mhardbitmapcache first. get (URL); If (Bitmap! = NULL) {// if found, move the element to the very beginning of linkedhashmap to ensure that mhardbitmapcache is finally deleted in the LRU algorithm. remove (URL); mhardbitmapcache. put (URL, bitmap); Return bitmap ;}// if mhardbitmapcache cannot be found, find softreference <bitmap> bitmapreference = msoftbitmapcache in msoftbitmapcache. get (URL); If (bitmapreference! = NULL) {final Bitmap bitmap = bitmapreference. Get (); If (Bitmap! = NULL) {return bitmap;} else {msoftbitmapcache. Remove (URL) ;}} return NULL ;}
If the cache does not exist, you can only download it from the server:
/*** Asynchronous download image */class imagedownloadertask extendsasynctask <string, void, bitmap> {Private Static final int io_buffer_size = 4*1024; private string URL; private finalweakreference <imageview> imageviewreference; Public imagedownloadertask (imageviewimageview) {imageviewreference = newweakreference <imageview> (imageview);} @ override protected bitmapdoinbackground (string... params) {final androidhtt Pclient client = androidhttpclient. newinstance ("android"); url = Params [0]; Final httpget getrequest = newhttpget (URL); try {httpresponse response client.exe cute (getrequest); Final int statuscode = response. getstatusline (). getstatuscode (); If (statuscode! = Httpstatus. SC _ OK) {log. W (TAG, "An error occurred while downloading images from" + URL + !, Error Code: "+ statuscode); return NULL;} final httpentity entity = response. getentity (); If (entity! = NULL) {inputstream = NULL; outputstream = NULL; try {inputstream = entity. getcontent (); finalbytearrayoutputstream datastream = new bytearrayoutputstream (); outputstream = newbufferedoutputstream (datastream, io_buffer_size); copy (inputstream, outputstream); outputstream. flush (); Final byte [] DATA = datastream. tobytearray (); Final Bitmap bitmap = bitmapfactory. decodebytearray (Data, 0, Data. Length); Return bitmap;} finally {If (inputstream! = NULL) {inputstream. Close ();} If (outputstream! = NULL) {outputstream. close ();} entity. consumecontent () ;}} catch (ioexception e) {getrequest. abort (); log. W (TAG, "I/O errorwhile retrieving bitmap from" + URL, e);} catch (illegalstateexception e) {getrequest. abort (); log. W (TAG, "incorrect URL:" + URL);} catch (exception e) {getrequest. abort (); log. W (TAG, "error whileretrieving bitmap from" + URL, e);} finally {If (client! = NULL) {client. Close () ;}return null ;}
There are also some applications that use the thread pool and Message Queue MQ during download, which is more efficient for image download. If you are interested, you can check it out.
Summary:
For relatively large resources such as remote images, the local cache must be obtained in asynchronous threads.