Read Baidu Map point aggregation source (bottom)---renderer class analysis

Source: Internet
Author: User

The previous article analyzed Clustermananger's overall structure and the core algorithm to read the Baidu Map point aggregation source (above), this article is followed by an article.

In this article, we'll learn how to do a lot of things in the UI thread without causing the interface to stutter.

The last time we talked about the cluster () method in the Clustermanager class, call the Clustertask background threading core algorithm, since there are doinbackground () background task functions, there will be OnPostExecute () function to handle the results returned by the background thread, this one analyzes how the returned results are handled.

So let's start with the results of the return!

private class Clustertask extends asynctask< Float, Void, set<? Extends Cluster<t>>> {@Override protected set<? extends Cluster<t>> Doinbackground (            Float ... zoom) {Malgorithmlock.readlock (). Lock ();            try {return malgorithm.getclusters (zoom[0]);            } finally {Malgorithmlock.readlock (). Unlock ();            }} @Override protected void OnPostExecute (SET<? extends cluster<t>> clusters) {        mrenderer.onclusterschanged (clusters); }} 

above is clustertask source, background task processing algorithm, and then return the data to the main thread, return is a set<? extends cluster<t>> type of object, is a collection that contains several cluster objects, and the cluster object is a collection that contains a number of myitem (implements Clusteritem).
Also, the myitem in each cluster are determined to be aggregated into one point.
So how do you deal with this cluster collection?
We see is mrenderer to deal with, is the source of Defaultclusterrenderer class, of course, we can also inherit this class to achieve our own renderer class, this needs to elaborate it.
Let's start by analyzing what the Defaultclusterrenderer class has done.
Everything starts with the onclusterschanged (clusters) method, which is implemented from the Clusterrenderer interface.
@Override public    void onclusterschanged (set<? extends cluster<t>> clusters) {        Mviewmodifier.queue (clusters);    }
This method is very simple, there is only one line of code, so we have to go inside tracking.
At this point we will have two questions: what is Mviewmodifier? What does the queue () function do?
Viewmodifier is actually a class that inherits from the handler, with only two functions inside: Handlemessage () and queue ()
Let's look at the queue () function first:
public void queue (set<. extends cluster<t>> clusters) {            synchronized (this) {                //Overwrite any Pendin G cluster tasks-we don ' t care about intermediate states.                Mnextclusters = new Rendertask (clusters);            }            Sendemptymessage (Run_task);        }

Create a Rendertask object inside the function, which is an implementation class for the Runnable interface, which is a thread that is not started.
Then send a message to the Handlemessage function. As you can probably guess, this thread will definitely start in Handlemessage.
The following is the code in Handlemessage:
@Override public void Handlemessage (Message msg) {if (Msg.what = = task_finished) {//Thread execution complete                    mviewmodificationinprogress = false;//Mark Thread has completed if (mnextclusters! = null) {//start thread again if a new task has not been executed                    Run the task that is queued up.                Sendemptymessage (Run_task);            } return;            } removemessages (Run_task);                if (mviewmodificationinprogress) {//busy-wait for the callback.            Return                } if (mnextclusters = = null) {//do.            Return            } Rendertask Rendertask;                Synchronized (this) {rendertask = mnextclusters;                Mnextclusters = null;                 mviewmodificationinprogress = true;//tag thread is running} rendertask.setcallback (new Runnable () {//setting callback when thread completes            @Override    public void Run () {sendemptymessage (task_finished);//When the thread completes, send a message to itself}); Rendertask.setprojection (Mmap.getprojection ());//Non-action Rendertask.setmapzoom (Mmap.getmapstatus (). Zoom )///Set the latest map level new thread (Rendertask). Start ();//Start thread}

It's not complicated, in a nutshell: Start a thread, the thread is rendertask.
Program execution here, we actually did only one thing--started a thread called Rendertask.
So, what exactly is therendertask sacred?
As you can see from the code above, there are some settings for Rendertask before starting the thread, so before you analyze its specific functionality, let's see what these settings do.
The first is the Setprojection () method, which has no effect in the current source code.
Then the Setmapzoom () method, take a look at the source code
public void Setmapzoom (float zoom) {            this.mmapzoom = zoom;            This.msphericalmercatorprojection =                    New Sphericalmercatorprojection (Math.pow (2, math.min (zoom, mZoom)));        }
As you can see, this method saves the zoom value of the current map and then creates a Sphericalmercatorprojection object, which is also used in the previous core algorithm to achieve the conversion between position (latitude and longitude) and point (two-dimensional coordinate points).
To calculate the distance between points and points, we need to convert the position to point, and in order to draw marker on the map, we need to convert the point to position.
This conversion of the formula I would not much analysis, pure math problem. It is worth mentioning that the parameters passed in when creating the Sphericalmercatorprojection object are actually the Worldwidth calculated from the zoom calculation (the formula: Worldwidth = zoom^2). As for Mzoom, it is the last zoom value saved. 
In fact, the size of the zoom value and the final result is not particularly large, the only effect is to convert position and point precision. I do not know if you can understand, can not understand the words to see a few times it.
Then, Rendertask is the most important run () method.
The code logic is not particularly complex, or the code + comments in the way to analyze it!
public void Run () {if (Clusters.equals (DefaultClusterRenderer.this.mClusters)) {Mcallback.run (            );//Judge if the new clusters equals the last saved clusters, return directly to return; } final Markermodifier markermodifier = new Markermodifier ();//This class handles display and animation final float zoom = Mmapzoo m;//Latest Zoom Value Final boolean zoomingin = zoom > Mzoom;//mzoom to last saved zoom value final float Zoomdelta = Z Oom-mzoom;//zoom change magnitude, beyond a certain level will not perform the animation of the final set<markerwithposition> markerstoremove = mmarkers;//need to delete the point.            Please consider what points need to be deleted? Final Latlngbounds visiblebounds = Mmap.getmapstatus (). bound;//the visible range of the map on the phone screen//1. Add points//Find out all the original C on the screen            Luster Center Point, some animations need to use these points when adding points list<point> existingclustersonscreen = null; if (DefaultClusterRenderer.this.mClusters! = null && should_animate) {Existingclustersonscreen = n                EW arraylist<point> (); for (cluster<t&Gt  C:defaultclusterrenderer.this.mclusters) {//iteration last saved clusters if (Shouldrenderascluster (c) && Visiblebounds.contains (C.getposition ())) {//Only the cluster that have been aggregated can add a point to points = Msphericalmercator Projection.topoint (C.getposition ());//position converted to point existingclustersonscreen.add;//Save screen already Aggregated Cluster}}}//Create the new markers and animate them to th            EIR new positions. Final set<markerwithposition> newmarkers = collections.newsetfrommap (New concurrenthashmap<m Arkerwithposition, boolean> ());//Save the points that need to be displayed in the new clusters, and turn to the Markerwithposition type for (cluster<t> c:cluste                RS) {//Iterate new clusters Boolean onscreen = Visiblebounds.contains (C.getposition ());//whether in-screen if (zoomingin && onscreen && should_animate) {//Map magnification + this cluster in-screen + can be animated (SDK version >11) Point point = Msphericalmercatorprojection.topoint (C.getposition ());//position turns to point Point closest = Findclosestcluster (Existingclustersonscreen, points);//Find the spot on the original screen closest to this cluster (c                        Losest! = null) {//exists, the implementation animation LATLNG Animateto = msphericalmercatorprojection.tolatlng (closest);                    Markermodifier.add (True, new Createmarkertask (C, Newmarkers, Animateto));                    } else {//does not exist, it is added directly without generating animation Markermodifier.add (True, new Createmarkertask (c, newmarkers, null)); }} else {//Add point directly, do not generate animation Markermodifier.add (onscreen, new Createmarkertask                (c, newmarkers, null));            }}//2. The task that waits to add a point completes markermodifier.waituntilfree ();            The points in the newmarkers are removed from the Markerstoremove, and the points in the Markerstoremove are Markerstoremove.removeall (newmarkers) that need to be removed from the map;         3. Removing points   Find the cluster center point that is now displayed on the screen and use these points for the animation list<point> Newclustersonscreen = null when removing points;                if (should_animate) {newclustersonscreen = new arraylist<point> (); for (cluster<t> c:clusters) {if (Shouldrenderascluster (c) && Visiblebounds.contains (c.                        GetPosition ())) {Point P = msphericalmercatorprojection.topoint (C.getposition ()));                    Newclustersonscreen.add (P); }}} for (final markerwithposition marker:markerstoremove) {//iteration all need to be moved                Except for the point Boolean onscreen = Visiblebounds.contains (marker.position);                    if (!zoomingin && zoomdelta >-3 && onscreen && should_animate) {//Map zoom out + zoom change no more than 3                    Final point point = Msphericalmercatorprojection.topoint (marker.position); Final point closest = Findclosestcluster (nEwclustersonscreen, point);//Find out the nearest cluster if (closest! = null) {LATLNG Animatet o = msphericalmercatorprojection.tolatlng (closest);//The end point of the animation move markermodifier.animatethenremove (Marke                    R, Marker.position, Animateto); } else {Markermodifier.remove (true, marker.marker);//No Animation}} Els e {markermodifier.remove (onscreen, marker.marker);//No Animation}}//wait for move            Task Completion Markermodifier.waituntilfree () except point;            Mmarkers = newmarkers;//Save the new point DefaultClusterRenderer.this.mClusters = clusters; Mzoom = zoom;//Save the latest zoom Mcallback.run ();//execute thread execution completion callback function}
First, there are two ways to explain.
Shouldrenderascluster (Cluster)-----Determine whether cluster can converge, this step determines the number of Clusteritem included in cluster.
/**     * Determine whether the cluster should be rendered as individual markers or a cluster.     *    /protected Boolean shouldrenderascluster (cluster<t> Cluster) {        return cluster.getsize () > Min_ cluster_size;    }
findclosestcluster ( Existingclustersonscreen, point)-----Find the nearest cluster. This lookup process sets a minimum distance of mindistsquared, and returns null if there is no cluster less than mindistsquared. 
private static point Findclosestcluster (list<point> markers, point point) {        if (markers = = NULL | | markers.isemp Ty ()) {            return null;        }                Double mindistsquared = max_distance_at_zoom * max_distance_at_zoom;        Point closest = null;        for (point candidate:markers) {            Double dist = distancesquared (candidate, point);            if (Dist < mindistsquared) {                closest = candidate;                mindistsquared = dist;            }        }        return closest;    }

Then there is a class that needs to focus on----markermodifier.
Markermodifier is a handler, and this is a very subtle way of doing a lot of work in the main thread of the previous article.
As you can see from the name, this is a modification of the marker on the map.
Private Markermodifier () {            super (Looper.getmainlooper ());        }
Above is its constructor, note looper.getmainlooper ()
We know that handler must bind a Looper object, and each thread has only one Looper object. But Markermodifier binds to mainlooper, which means that everything it does is done in the main thread!!!! 
This handler implements the interface Messagequeue.idlehandler, before reading this source code, the landlord does not know what this is a thing, so understanding is not necessarily right, you'd better check it yourself.
 Messagequeue.idlehandler is the invocation of the interface definition in the idle (idle) state of the system. Queueidle () method, this method must be overridden when implementing an interface. 
In this project, the Markermodifier Handlemessage () method is automatically called when the system is idle. This allows for a large number of operations and does not result in the system being stuck.
Each call to the Markermodifier Add method or the Remove method sends a message to itself to invoke its own Handlemessage method.
Next, we highlight the handlemessage approach. On the code!
        @Override public void Handlemessage (Message msg) {//all new and deleted marker and animated tasks are all done if (!mlistener                Added) {//Add idle Interface Looper.myqueue (). Addidlehandler (this);//When the main thread is idle, send a blank message to perform a point aggregation action            Mlisteneradded = true;            } removemessages (BLANK);            Lock.lock (); try {//Perform all tasks (add, delete, animate) on each of 10 tasks//In batches, avoiding system lag for (int i = 0; i <; I                    + +) {performnexttask ();//Perform a task} if (!isbusy ()) {//Whether all tasks are performed                    mlisteneradded = false; Remove the Idle Interface Looper.myqueue (). Removeidlehandler (this);//all sub-threads complete//Wake Up all waiting threads (can go back                See Rendertask's Run () method) Busycondition.signalall (); } else {//Originally this sentence is unnecessary, but Baidu engineers said that in some cases the system idle state will not successfully call the Queueidle () method//So here Manual delay 10ms call Han again         Dlemessage           Sendemptymessagedelayed (BLANK, 10);            }} finally {Lock.unlock (); }        }

Here, the whole source of the analysis, some other methods are relatively simple, not much to say, we look at the source code it!!
Clusterdemo

Read Baidu Map point aggregation source (bottom)---renderer class analysis

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.