Android Handler Asynchronous message processing mechanism to create a powerful picture loading class

Source: Internet
Author: User
Tags message queue one more line

Reprint please indicate source: http://blog.csdn.net/lmj623565791/article/details/38476887, this article from "Zhang Hongyang's Blog"

A group has been created recently. Easy to communicate, Group No.:55032675

The previous blog describes the android asynchronous message processing mechanism. Assuming you don't understand, you can see: the Android Asynchronous message processing mechanism gives you a deep understanding of Looper, Handler, and message relationships.

At the end of the blog post, it was suggested that the asynchronous message processing mechanism was not just updating the UI in Mainactivity. Be able to use other places. Recently also has been thinking about this issue, fortunately, to come up with a practical case. The asynchronous message processing mechanism is used in a large number of images loaded in the tool class, in fact, but also specifically hope to write a large number of images loaded articles, finally have a chance ~ First:

1. Overview

Loading of a large number of images. For example, the GridView implementation of mobile phone registration function. The LRUCache is usually used. The thread pool. Task queue, etc., so what can asynchronous message processing be used for?

1. For UI thread update ImageView when bitmap loading is complete

2, in the image loading class initialization, we will maintain a loop instance in a sub-thread, of course, there is messagequeue,looper in the child thread will always be in that loop waiting for the arrival of the message, when a message arrives. From the task queue according to the way the queue is dispatched (FIFO,LIFO, etc.), remove a task into the thread pool for processing.

A simple process: when a picture needs to be loaded, first load the picture to increase the task queue, and then use the loop thread (sub-thread) in the hander send a message that the task arrives, loop () (sub-thread) will then take out a task, to load the picture, when the picture is loaded, A message is sent using the UI thread's handler to update the UI interface.

Said so much. It is expected that the clouds will be in the fog, the following look at the actual examples.

2, the realization of the function of the picture library

The program first scans all the directories in the phone that include pictures. Finally select the most pictures of the directory, using the GridView display in the picture

1. layout file

<relativelayout xmlns:android= "http://schemas.android.com/apk/res/android"    xmlns:tools= "http// Schemas.android.com/tools "    android:layout_width=" match_parent "    android:layout_height=" Match_parent " >    <gridview        android:id= "@+id/id_gridview"        android:layout_width= "Match_parent"        android: layout_height= "Match_parent"        android:cachecolorhint= "@android: color/transparent"        android:columnwidth= " 90dip "        android:gravity=" center "        android:horizontalspacing=" 20dip "        android:listselector=" @android : Color/transparent "        android:numcolumns=" Auto_fit "        android:stretchmode=" ColumnWidth "        android: verticalspacing= "20dip" >    </GridView></RelativeLayout>

The layout file is fairly simple, just a gridview.

2, Mainactivity

Package Com.example.zhy_handler_imageloader;import Java.io.file;import Java.io.filenamefilter;import Java.util.arrays;import Java.util.hashset;import Java.util.list;import Android.app.activity;import Android.app.progressdialog;import Android.content.contentresolver;import Android.database.cursor;import Android.net.uri;import Android.os.bundle;import Android.os.environment;import Android.os.Handler;import Android.provider.mediastore;import Android.widget.gridview;import Android.widget.imageview;import Android.widget.listadapter;import Android.widget.toast;public class Mainactivity extends Activity{private ProgressDialog mprogressdialog;private ImageView mimageview;/** * Store the number of pictures in the directory */private int mpicssize;/** * The directory with the highest number of pictures */PR Ivate File mimgdir;/** * All pictures */private list<string> mimgs;private GridView mgirdview;private listadapter mAdapter ;/** * Temporary helper class to prevent multiple scans of the same directory */private hashset<string> mdirpaths = new hashset<string> ();p rivate Handler Mhandler = new Handler () {PublIC void Handlemessage (Android.os.Message msg) {Mprogressdialog.dismiss (); Mimgs = Arrays.aslist (Mimgdir.list (new FilenameFilter () {@Overridepublic Boolean accept (File dir, String filename) {if (Filename.endswith (". jpg")) return true; return false;})); * * can see the path of the directory and the path of the picture are saved separately. Greatly reduces the consumption of memory. */madapter = new Myadapter (Getapplicationcontext (), Mimgs,mimgdir.getabsolutepath ()); Mgirdview.setadapter (MAdapter );};}; @Overrideprotected void OnCreate (Bundle savedinstancestate) {super.oncreate (savedinstancestate); Setcontentview ( R.layout.activity_main); Mgirdview = (GridView) Findviewbyid (R.id.id_gridview); GetImages ();} /** * Using ContentProvider to scan the picture in the phone, this method in the sub-thread run in the picture scan, and finally get the most jpg that directory */private void GetImages () {if (! Environment.getexternalstoragestate (). Equals (environment.media_mounted)) {Toast.maketext (this, "No external storage", Toast.length_short). Show (); return;} Show run progress bar Mprogressdialog = Progressdialog.show (this, null, "Loading ..."); New Thread (New Runnable () {@Overridepublic void Run () {Uri Mimageuri = Mediastore.imageS.media.external_content_uri; Contentresolver mcontentresolver = MainActivity.this.getContentResolver ();//Only the images of JPEG and PNG are queried cursor mcursor = Mcontentresolver.query (Mimageuri, null,mediastore.images.media.mime_type + "=? or "+ MediaStore.Images.Media.MIME_TYPE +" =? ", new string[] {" Image/jpeg "," Image/png "},mediastore.images.media.date_ MODIFIED); while (Mcursor.movetonext ()) {//Gets the path of the picture, string path = Mcursor.getstring (Mcursor.getcolumnindex ( MediaStore.Images.Media.DATA));//Gets the parent path name of the picture file Parentfile = new file (path). Getparentfile (); String Dirpath = Parentfile.getabsolutepath (); Using a hashset to prevent multiple scans of the same directory (without this inference, the picture is still quite scary ~ ~) if (Mdirpaths.contains (Dirpath)) {continue;} Else{mdirpaths.add (Dirpath);} int picsize = parentfile.list (new FilenameFilter () {@Overridepublic Boolean accept (File dir, String filename) {if ( Filename.endswith (". jpg")) return True;return false;}}). Length;if (Picsize > Mpicssize) {mpicssize = Picsize;mimgdir = Parentfile;}} Mcursor.close ();//scan complete. The auxiliary hashset will also be able to free up memory Mdirpaths = null; Notify handler to scan the picture mhandler.sendemptymessage (0x110);}}). Start ();}}

Mainactivity is also relatively simple, using ContentProvider assist. Find the most pictures of the directory, directly handler to hide ProgressDialog, and then initialize the data, adapter, etc.;

But a little bit of attention:

1, when scanning the picture. A temporary hashset is used to save scanned directories, which effectively avoids repeated scans.

For example, I have a directory in my phone that has more than 3,000 images below. Assuming that you do not infer that the directory will be scanned more than 3,000 times, the processor time and memory consumption is very considerable.

2, in the adapter. When saving list<string>, consider simply saving the name of the picture, and the path is passed in as a separate variable. Under normal circumstances, the path of the picture is much longer than the image name, with 3000 images added, and a path length of 30. The average length of the image is 10, then the length of the List<string> Save path is: (30+10) *3000 = 120000; Storage alone requires only: 30+10*3000 = 30030. The more pictures. The more objective the saved memory is;

In short, as much as possible to reduce memory consumption, these are very easy to do ~

3, the GridView adapter

Package Com.example.zhy_handler_imageloader;import Java.util.list;import Android.content.context;import Android.view.layoutinflater;import Android.view.view;import Android.view.viewgroup;import Android.widget.baseadapter;import Android.widget.imageview;import Com.zhy.utils.imageloader;public class MyAdapter Extends Baseadapter{private Context mcontext;private list<string> mdata;private String mdirpath;private Layoutinflater minflater;private imageloader mimageloader;public myadapter (context context, list<string> MData, String dirpath) {this.mcontext = Context;this.mdata = Mdata;this.mdirpath = Dirpath;minflater = Layoutinflater.from ( Mcontext); mimageloader = Imageloader.getinstance ();} @Overridepublic int GetCount () {return mdata.size ();} @Overridepublic Object getItem (int position) {return mdata.get (position);} @Overridepublic long Getitemid (int position) {return position;} @Overridepublic view GetView (int position, view Convertview, final ViewGroup parent) {Viewholder holder = null; if (Convertview = = null) {holder = new Viewholder (); Convertview = Minflater.inflate (R.layout.grid_item, Parent,false); Holder.mimageview = (ImageView) Convertview.findviewbyid (r.id.id_item_image); Convertview.settag (holder);} Else{holder = (Viewholder) Convertview.gettag ();} Holder.mImageView.setImageResource (r.drawable.friends_sends_pictures_no);// Use Imageloader to load picture mimageloader.loadimage (Mdirpath + "/" + mdata.get (position), Holder.mimageview); return Convertview ;} Private Final class Viewholder{imageview Mimageview;}}

There is no difference in how you can see the traditional adapter, even in GetView, where there are no common callbacks (findviewbytag~ is used to prevent image misalignment). Just one more line of code:

Mimageloader.loadimage (Mdirpath + "/" + mdata.get (position), holder.mimageview); it is not used or quite cool. All the details that need to be dealt with are encapsulated.

4, Imageloader

Now it's the critical moment. We encapsulate the Imageloader class. Of course, our asynchronous message processing mechanism is in the midst of today.

First, a lazy load of a single case

/** * Single example gets the instance object *  * @return */public static Imageloader getinstance () {if (minstance = null) {synchronized (imageloader . Class) {if (minstance = = null) {minstance = new Imageloader (1, Type.lifo);}}} return minstance;}

Nothing to say, directly call the private construction method, can see. Default passed in 1 (number of threads in thread pool), and LIFO (how the queue works)

Private Imageloader (int threadcount, type type) {init (threadcount, type);} private void init (int threadcount, type type) {//Loop Threadmpoolthread = new Thread () {@Overridepublic void run () {try{//please Ask for a semaphore msemaphore.acquire ();} catch (Interruptedexception e) {}looper.prepare (); mpoolthreadhander = new Handler () {@Overridepublic void handlemessage (Message msg) {Mthreadpool.execute (Gettask ()); Try{mpoolsemaphore.acquire ();} catch (Interruptedexception e) {}}};// Release a semaphore msemaphore.release (); Looper.loop ();}}; Mpoolthread.start ();//Gets the application maximum available memory int maxmemory = (int) runtime.getruntime (). MaxMemory (); int cacheSize = MAXMEMORY/8; Mlrucache = new lrucache<string, bitmap> (cacheSize) {@Overrideprotected int sizeOf (String key, Bitmap value) { return Value.getrowbytes () * Value.getheight ();};}; Mthreadpool = Executors.newfixedthreadpool (threadcount); mpoolsemaphore = new Semaphore (threadcount); mTasks = new Linkedlist<runnable> (); mtype = type = = null? Type.LIFO:type;}

Then we call our Init method inside the private construct, and at the beginning of this method we create the Mpoolthread, which we run Looper.prepare, initialize Mpoolthreadhander,looper.loop. Let's say I read the previous blog post. It must be known that a message queue is maintained in this child thread, and the child thread enters a loop of infinite read messages. The messages sent by Mpoolthreadhander This handler are sent directly to the message queue in this thread.

Then look at the method of Handlemessage in Mpoolthreadhander, call the Gettask method directly to take out a task, and then put it into the thread pool to run. Assuming that you are more careful, you may find some code for the operation of other semaphores inside. Suppose you don't know what a semaphore is, you can refer to: Java concurrency topic: Semaphore for mutual exclusion and connection pooling.

Briefly say the Msemaphore (signal number is 1) function. Because Mpoolthreadhander is actually a child thread initialized, I called Msemaphore.acquire before initializing to request a semaphore. Then after the initialization is done, I release this semaphore, why do I do this? Since the main thread may be used immediately to Mpoolthreadhander, Mpoolthreadhander is initialized on the child thread. Although very fast, but I can not be guaranteed. The main thread was initialized at the end of use, in order to avoid null pointer exceptions. So I call this when I need to use the main thread:

/** * Add a task *  * @param runnable */private synchronized void AddTask (runnable runnable) {try{//request semaphore. Prevent Mpoolthreadhander to nullif (Mpoolthreadhander = = null) Msemaphore.acquire ();} catch (Interruptedexception e) {}mtasks.add (runnable); Mpoolthreadhander.sendemptymessage (0x110);}

Assuming that Mpoolthreadhander is not initialized, it will acquire a semaphore, in fact, to wait for Mpoolthreadhander initialization to complete. If you are interested in this, you can stare at the code about Msemaphore. This error is then discovered when initializing Mpoolthreadhander uses Thread.Sleep to pause for 1 seconds.

At the end of initialization, Mimageloader.loadimage (Mdirpath + "/" + mdata.get (position), Holder.mimageview) is called in GetView; So let's go see the LoadImage method.

/** * Load Image * * @param path * @param imageView */public void LoadImage (Final String path, final ImageView ImageView) {//SE T Tagimageview.settag (path);//UI thread if (Mhandler = = null) {Mhandler = new Handler () {@Overridepublic void Handlemessage ( Message msg) {Imgbeanholder holder = (imgbeanholder) Msg.obj;imageview ImageView = Holder.imageview; Bitmap BM = Holder.bitmap; String path = holder.path;if (Imageview.gettag (). toString (). Equals (path) {Imageview.setimagebitmap (BM);}};} Bitmap BM = Getbitmapfromlrucache (path), if (BM! = null) {Imgbeanholder holder = new Imgbeanholder (); holder.bitmap = Bm;hold Er.imageview = Imageview;holder.path = path; Message message = Message.obtain (); message.obj = holder;mhandler.sendmessage (message);} Else{addtask (New Runnable () {@Overridepublic void run () {ImageSize ImageSize = getimageviewwidth (imageView); int Reqwidth = Imagesize.width;int Reqheight = imagesize.height; Bitmap BM = Decodesampledbitmapfromresource (path, reqwidth,reqheight); Addbitmaptolrucache (path, BM); ImgBEanholder holder = new Imgbeanholder (); holder.bitmap = Getbitmapfromlrucache (path); holder.imageview = ImageView; Holder.path = path; Message message = Message.obtain (); message.obj = holder;//log.e ("TAG", "mhandler.sendmessage (message);"); Mhandler.sendmessage (message); Mpoolsemaphore.release ();}});}}

This code is longer and, of course, the core code.

Line 10-29: First set the path to the incoming ImageView. The Mhandler is initialized with a bitmap for setting ImageView, note at this time on the UI thread. This is the message from the Mhandler. is called in the UI thread.

Can see in the handlemessage, we remove ImageView from the message. Bitmap,path and then compare the path to the ImageView tag to prevent the image from being misaligned. finally set bitmap;

31 lines: Let's go from the LRUCache first to find out if this picture has been cached

32-40: If found, use Mhandler to send the message directly. Here, a imgbeanholder is used to encapsulate the imageview,bitmap. The path of the three objects. Then update the run Handlemessage code to update the UI

43-66 rows: Assuming there is no cache, create a Runnable object as a task to run the AddTask method to increase the task queue

49 Line: Getimageviewwidth the size of the appropriate picture according to the ImageView. For subsequent compressed images, the code is pasted in the following order

54 Line: The image will be compressed according to the required width and height of the calculation.

The code is pasted in the following order

56 Line: Put the compressed picture in the cache

58-64 lines to create the message. Use Mhandler to send, update UI

/** * Based on ImageView to obtain appropriate compression width and height * * @param imageView * @return */private ImageSize getimageviewwidth (ImageView imageView) {Im Agesize imageSize = new ImageSize (); final Displaymetrics displaymetrics = Imageview.getcontext (). Getresources (). Getdisplaymetrics (); final layoutparams params = Imageview.getlayoutparams (); int width = Params.width = = Layoutparams.wrap_content? 0:imageview.getwidth (); Get actual image Widthif (width <= 0) width = params.width; Get Layout width parameterif (width <= 0) width = getimageviewfieldvalue (ImageView, "mmaxwidth"); check//maxwidth//parameterif (width <= 0) width = displaymetrics.widthpixels;int height = Params.height = = LayoutPa Rams. Wrap_content?

0:imageview.getheight (); Get actual image heightif (height <= 0) height = params.height; Get Layout height parameterif (height <= 0) height = getimageviewfieldvalue (imageView, "mmaxheight"); check//maxheight//parameterif (height <= 0) height = displaymetrics.heightpixels;imagesize.width = width; Imagesize.height = Height;return imageSize;}

/** * Based on calculated insamplesize, get compressed picture *  * @param pathName * @param reqwidth * @param reqheight * @return */private Bitmap de Codesampledbitmapfromresource (String pathname,int reqwidth, int reqheight) {//First resolution sets Injustdecodebounds to True, To get the picture size final bitmapfactory.options Options = new Bitmapfactory.options (); options.injustdecodebounds = true; Bitmapfactory.decodefile (pathName, options);//Call the method defined above to calculate the Insamplesize value options.insamplesize = Calculateinsamplesize (options, reqwidth,reqheight);//Use the obtained Insamplesize value to parse the picture again options.injustdecodebounds = false; Bitmap Bitmap = Bitmapfactory.decodefile (pathName, options); return Bitmap;}
Next look at the AddTask code:

/** * Add a task *  * @param runnable */private synchronized void AddTask (runnable runnable) {try{//request semaphore to prevent Mpoolthreadhan Der is nullif (Mpoolthreadhander = = null) Msemaphore.acquire ();} catch (Interruptedexception e) {}mtasks.add (runnable); Mpoolthreadhander.sendemptymessage (0x110);}

Can see, simply put the task into the task queue, and then use Mpoolthreadhander send a message to the background loop, loop in the background will take out the message run: Mthreadpool.execute (Gettask ());

Execute runs the Run method in the runnable that was analyzed above.

Note: The above code will also see Mpoolsemaphore this semaphore figure. This is useful, because after calling AddTask, a task is taken out of the task queue and put into the thread pool, because the thread pool actually maintains a queue, and the "pull a task from the task queue" action is instantaneous. Directly increases the queue maintained by the thread pool; This causes the user to set the scheduling queue as LIFO. However, because the "remove a task from the task queue" This action will be instantaneous, the queue is always maintained in the state of the empty queue, so that users feel that the LIFO has no effect; so I set a semaphore according to the number of worker threads that the user set thread pool, so that when the task is finished running, Only from the task queue to get the task, so that the LIFO has a very good effect, interested in the ability to stare at all the Mpoolsemaphore code, the test is clear.

The basic introduction to this code is complete. The details are still very much, the following will be attached source code, interested in the study under the Code, no interest. Be able to run the code below. Let's say that you feel good fluency. The experience is good, can be used as a tool class directly, the use of getview inside a line of code.


I've posted about 3000 pictures of my mobile phone directory. The loading speed is quite smooth:


Real machine record, a little drop frame. Look at the middle I'm crazy dragging the scroll bar, but the picture is basically still displayed in an instant.

Let's say that the FIFO hypothesis is set to this mode. If you do not do the processing in the control, the user pulls the slow effect is still good. However, the user's phone is assumed to have a thousands of, instantly pull to the end. The last screen image may require a cup of tea. You can do the processing in the control. Either. When dragging, do not load the picture, stop in to reload. Or, when the phone is lifted and gives a very large acceleration, the screen is still very fast when the slide stops loading and loads the picture when it stops.

LIFO This mode may be a lot better user experience, no matter how many blocks the user pulls, finally stopped the screen image will be instantly displayed ~

Last break. The advantage of using the asynchronous message processing mechanism as a sub-thread behind is that it can actually be implemented directly with a child thread, but the child thread may need while (true) in run to query the task queue for tasks in every 200 milliseconds or less. There is no thread.sleep. And then go to query. This assumes that for a long time without adding a task, the thread continues to query.

and the asynchronous message mechanism. It's only going to run when the message is sent, and it's certainly more accurate; When no task arrives for a long time, it won't be queried, it will always be plugged in; another point, this mechanism Android internal implementation. It's more of a thread than we are. Stability, high efficiency bar ~


Source code click to download







Android Handler Asynchronous message processing mechanism to create a powerful picture loading class

Related Article

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.