diff --git a/library/src/main/java/com/google/maps/android/clustering/ClusterManager.java b/library/src/main/java/com/google/maps/android/clustering/ClusterManager.java index 27f1fe2dc..0685ee56a 100644 --- a/library/src/main/java/com/google/maps/android/clustering/ClusterManager.java +++ b/library/src/main/java/com/google/maps/android/clustering/ClusterManager.java @@ -123,11 +123,17 @@ public void setAlgorithm(Algorithm algorithm) { public void setAlgorithm(ScreenBasedAlgorithm algorithm) { algorithm.lock(); try { - if (mAlgorithm != null) { - algorithm.addItems(mAlgorithm.getItems()); - } - + final Algorithm oldAlgorithm = getAlgorithm(); mAlgorithm = algorithm; + + if (oldAlgorithm != null) { + oldAlgorithm.lock(); + try { + algorithm.addItems(oldAlgorithm.getItems()); + } finally { + oldAlgorithm.unlock(); + } + } } finally { algorithm.unlock(); } @@ -156,11 +162,12 @@ public Algorithm getAlgorithm() { * {@link #cluster()} for the map to be cleared. */ public void clearItems() { - mAlgorithm.lock(); + final Algorithm algorithm = getAlgorithm(); + algorithm.lock(); try { - mAlgorithm.clearItems(); + algorithm.clearItems(); } finally { - mAlgorithm.unlock(); + algorithm.unlock(); } } @@ -171,11 +178,12 @@ public void clearItems() { * @return true if the cluster manager contents changed as a result of the call */ public boolean addItems(Collection items) { - mAlgorithm.lock(); + final Algorithm algorithm = getAlgorithm(); + algorithm.lock(); try { - return mAlgorithm.addItems(items); + return algorithm.addItems(items); } finally { - mAlgorithm.unlock(); + algorithm.unlock(); } } @@ -186,11 +194,12 @@ public boolean addItems(Collection items) { * @return true if the cluster manager contents changed as a result of the call */ public boolean addItem(T myItem) { - mAlgorithm.lock(); + final Algorithm algorithm = getAlgorithm(); + algorithm.lock(); try { - return mAlgorithm.addItem(myItem); + return algorithm.addItem(myItem); } finally { - mAlgorithm.unlock(); + algorithm.unlock(); } } @@ -201,11 +210,12 @@ public boolean addItem(T myItem) { * @return true if the cluster manager contents changed as a result of the call */ public boolean removeItems(Collection items) { - mAlgorithm.lock(); + final Algorithm algorithm = getAlgorithm(); + algorithm.lock(); try { - return mAlgorithm.removeItems(items); + return algorithm.removeItems(items); } finally { - mAlgorithm.unlock(); + algorithm.unlock(); } } @@ -216,11 +226,12 @@ public boolean removeItems(Collection items) { * @return true if the item was removed from the cluster manager as a result of this call */ public boolean removeItem(T item) { - mAlgorithm.lock(); + final Algorithm algorithm = getAlgorithm(); + algorithm.lock(); try { - return mAlgorithm.removeItem(item); + return algorithm.removeItem(item); } finally { - mAlgorithm.unlock(); + algorithm.unlock(); } } @@ -232,11 +243,12 @@ public boolean removeItem(T item) { * contained within the cluster manager and the cluster manager contents are unchanged */ public boolean updateItem(T item) { - mAlgorithm.lock(); + final Algorithm algorithm = getAlgorithm(); + algorithm.lock(); try { - return mAlgorithm.updateItem(item); + return algorithm.updateItem(item); } finally { - mAlgorithm.unlock(); + algorithm.unlock(); } } @@ -294,11 +306,12 @@ public void onInfoWindowClick(Marker marker) { private class ClusterTask extends AsyncTask>> { @Override protected Set> doInBackground(Float... zoom) { - mAlgorithm.lock(); + final Algorithm algorithm = getAlgorithm(); + algorithm.lock(); try { - return mAlgorithm.getClusters(zoom[0]); + return algorithm.getClusters(zoom[0]); } finally { - mAlgorithm.unlock(); + algorithm.unlock(); } } diff --git a/library/src/main/java/com/google/maps/android/clustering/algo/PreCachingAlgorithmDecorator.java b/library/src/main/java/com/google/maps/android/clustering/algo/PreCachingAlgorithmDecorator.java index a39106a68..00757e19a 100644 --- a/library/src/main/java/com/google/maps/android/clustering/algo/PreCachingAlgorithmDecorator.java +++ b/library/src/main/java/com/google/maps/android/clustering/algo/PreCachingAlgorithmDecorator.java @@ -23,6 +23,8 @@ import java.util.Collection; import java.util.Set; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; @@ -35,6 +37,7 @@ public class PreCachingAlgorithmDecorator extends Abstrac // TODO: evaluate maxSize parameter for LruCache. private final LruCache>> mCache = new LruCache>>(5); private final ReadWriteLock mCacheLock = new ReentrantReadWriteLock(); + private final Executor mExecutor = Executors.newCachedThreadPool(); public PreCachingAlgorithmDecorator(Algorithm algorithm) { mAlgorithm = algorithm; @@ -101,12 +104,10 @@ public Set> getClusters(float zoom) { Set> results = getClustersInternal(discreteZoom); // TODO: Check if requests are already in-flight. if (mCache.get(discreteZoom + 1) == null) { - // It seems this cannot use a thread pool due to thread locking issues (#660) - new Thread(new PrecacheRunnable(discreteZoom + 1)).start(); + mExecutor.execute(new PrecacheRunnable(discreteZoom + 1)); } if (mCache.get(discreteZoom - 1) == null) { - // It seems this cannot use a thread pool due to thread locking issues (#660) - new Thread(new PrecacheRunnable(discreteZoom - 1)).start(); + mExecutor.execute(new PrecacheRunnable(discreteZoom - 1)); } return results; } diff --git a/library/src/main/java/com/google/maps/android/clustering/view/DefaultClusterRenderer.java b/library/src/main/java/com/google/maps/android/clustering/view/DefaultClusterRenderer.java index c9cbabaee..b56683c78 100644 --- a/library/src/main/java/com/google/maps/android/clustering/view/DefaultClusterRenderer.java +++ b/library/src/main/java/com/google/maps/android/clustering/view/DefaultClusterRenderer.java @@ -66,6 +66,8 @@ import java.util.Queue; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; @@ -79,6 +81,7 @@ public class DefaultClusterRenderer implements ClusterRen private final ClusterManager mClusterManager; private final float mDensity; private boolean mAnimate; + private final Executor mExecutor = Executors.newSingleThreadExecutor(); private static final int[] BUCKETS = {10, 20, 50, 100, 200, 500, 1000}; private ShapeDrawable mColoredCircleBackground; @@ -331,8 +334,7 @@ public void run() { }); renderTask.setProjection(projection); renderTask.setMapZoom(mMap.getCameraPosition().zoom); - // It seems this cannot use a thread pool due to thread locking issues (#660) - new Thread(renderTask).start(); + mExecutor.execute(renderTask); } public void queue(Set> clusters) {