@@ -0,0 +1,3078 @@
+package com.google.maps.android.clustering.algo;%0A%0Aimport android.annotation.TargetApi;%0Aimport android.os.Build;%0Aimport android.util.Log;%0Aimport android.util.LruCache;%0A%0Aimport com.google.maps.android.clustering.Cluster;%0Aimport com.google.maps.android.clustering.ClusterItem;%0A%0Aimport java.util.Set;%0Aimport java.util.concurrent.locks.ReadWriteLock;%0Aimport java.util.concurrent.locks.ReentrantReadWriteLock;%0A%0A@TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1)%0Apublic class PreCachingDecorator%3CT extends ClusterItem%3E implements Algorithm%3CT%3E %7B%0A    private final static String TAG = PreCachingDecorator.class.getName();%0A    private final Algorithm%3CT%3E mAlgorithm;%0A    private final LruCache%3CInteger, Set%3C? extends Cluster%3CT%3E%3E%3E mCache = new LruCache%3CInteger, Set%3C? extends Cluster%3CT%3E%3E%3E(5);%0A    private final ReadWriteLock mCacheLock = new ReentrantReadWriteLock();%0A%0A    public PreCachingDecorator(Algorithm%3CT%3E algorithm) %7B%0A        mAlgorithm = algorithm;%0A    %7D%0A%0A    public void addItem(T item) %7B%0A        mAlgorithm.addItem(item);%0A        clearCache();%0A    %7D%0A%0A    public void removeItem(T item) %7B%0A        mAlgorithm.removeItem(item);%0A        clearCache();%0A    %7D%0A%0A    private void clearCache() %7B%0A        mCache.evictAll();%0A    %7D%0A%0A    @Override%0A    public Set%3C? extends Cluster%3CT%3E%3E getClusters(double zoom) %7B%0A        int discreteZoom = (int) zoom;%0A        Set%3C? extends Cluster%3CT%3E%3E results = getClustersInternal(discreteZoom);%0A        new Thread(new PrecacheRunnable(discreteZoom + 1)).start();%0A        new Thread(new PrecacheRunnable(discreteZoom - 1)).start();%0A        return results;%0A    %7D%0A%0A    public Set%3C? extends Cluster%3CT%3E%3E getClustersInternal(int discreteZoom) %7B%0A        Log.d(TAG, %22getClusters: zoom %22 + discreteZoom);%0A        Set%3C? extends Cluster%3CT%3E%3E results;%0A        mCacheLock.readLock().lock();%0A        results = mCache.get(discreteZoom);%0A        mCacheLock.readLock().unlock();%0A%0A        if (results == null) %7B%0A            mCacheLock.writeLock().lock();%0A            results = mCache.get(discreteZoom);%0A            if (results == null) %7B%0A                Log.d(TAG, %22cache miss for zoom %22 + discreteZoom);%0A                results = mAlgorithm.getClusters(discreteZoom);%0A                mCache.put(discreteZoom, results);%0A            %7D else %7B%0A                Log.d(TAG, %22cache hit for zoom %22 + discreteZoom);%0A            %7D%0A            mCacheLock.writeLock().unlock();%0A        %7D else %7B%0A            Log.d(TAG, %22cache hit for zoom %22 + discreteZoom);%0A        %7D%0A        return results;%0A    %7D%0A%0A    private class PrecacheRunnable implements Runnable %7B%0A        private final int mZoom;%0A%0A        public PrecacheRunnable(int zoom) %7B%0A            mZoom = zoom;%0A        %7D%0A%0A        @Override%0A        public void run() %7B%0A            try %7B%0A                Thread.sleep(1000);%0A            %7D catch (InterruptedException e) %7B%0A                // ignore. keep going.%0A            %7D%0A            long start = System.currentTimeMillis();%0A            getClustersInternal(mZoom);%0A            Log.d(TAG, %22pre-filling cache for zoom %22 + mZoom + %22 took %22 + (System.currentTimeMillis() - start));%0A        %7D%0A    %7D%0A%7D%0A

Add a decorator Algorithm that quantizes zoom level, fetches and caches (with LruCache) the clusters for the two adjacent zoom levels.