Am using custom markers for my google map. All the marker images are getting from server and with help of lazy loading i can display . The issue is that if the maker is set before image downloading completed it never update the map with new image.
Any help would be appreciated
public class ROverViewRender extends DefaultClusterRenderer<RItem> {
private IconGenerator mIconGenerator;
private IconGenerator mClusterIconGenerator;
private RImageView mImageView;
private RImageView mImageVieStar;
private ImageView mClusterImageView;
private Context context;
private ClusterManager<RItem> mClusterManager;
private ImageLoader imageLoader;
public ROverViewRender(Context context, GoogleMap map, ClusterManager<RItem> clusterManager) {
super(context, map, clusterManager);
this.mClusterManager=clusterManager;
Activity activity = (Activity) context;
this.context=context;
mClusterIconGenerator=new IconGenerator(context);
View clustorIcon = activity.getLayoutInflater().inflate(R.layout.clustor_icon, null);
mClusterIconGenerator.setContentView(clustorIcon);
mIconGenerator=new IconGenerator(context);
View markerIcon = activity.getLayoutInflater().inflate(R.layout.marker_icon, null);
mIconGenerator.setContentView(markerIcon);
mImageView=(RImageView)markerIcon.findViewById(R.id.marker_image);
mImageVieStar=(RImageView)markerIcon.findViewById(R.id.marker_route_rating);
mClusterImageView=(NetworkImageView)clustorIcon.findViewById(R.id.cluster_image);
imageLoader=VolleySingleton.getInstance(context).getImageLoader();;
}
#Override
protected void onBeforeClusterItemRendered(final RItem item, MarkerOptions markerOptions) {
setMarkerImage(item,markerOptions);
Bitmap icon = mIconGenerator.makeIcon();
markerOptions.icon(BitmapDescriptorFactory.fromBitmap(icon));
}
#Override
protected void onBeforeClusterRendered(Cluster<RItem> cluster, MarkerOptions markerOptions) {
Drawable drawable =context.getResources().getDrawable(R.drawable.ic_launcher);
mClusterImageView.setImageDrawable(drawable);
Bitmap icon = mClusterIconGenerator.makeIcon(String.valueOf(cluster.getSize()));
markerOptions.icon(BitmapDescriptorFactory.fromBitmap(icon));
}
#Override
protected boolean shouldRenderAsCluster(Cluster cluster) {
// Always render clusters.
return cluster.getSize() > 1;
}
#Override
protected void onClusterItemRendered(RItem clusterItem, Marker marker) {
RLog.e("Invoked the ....>>>>>");
// setMarkerImage(clusterItem);
}
private void setMarkerImage(RItem item,final MarkerOptions markerOptions){
RLog.e("Image setter invoked..."+item.getMarkerUrl());
mImageView.setImageUrl(item.getMarkerUrl(), imageLoader);
imageLoader.get(item.getMarkerUrl(), new ImageListener() {
public void onErrorResponse(VolleyError error) {
// imageView.setImageResource(R.drawable.icon_error); // set an error image if the download fails
}
public void onResponse(ImageContainer response, boolean arg1) {
RLog.e("Image download completed...");
if (response.getBitmap() != null) {
mImageView.setImageBitmap(response.getBitmap());
}
}
});
}
}
Issue Fixed. The google map marker have setIcon method which help us to update the icon. so whenever i got the callback from image downloader i will update it through setIcon method . So far no issues ,working perfectly fine.
Related
I want to change defult cluster group icon with my drawable icon.
Please check my code for cluster program.
MyClusterRenderer.java
public class MyClusterRenderer extends DefaultClusterRenderer<MyItem> {
public MyClusterRenderer(Context context, GoogleMap map,
ClusterManager<MyItem> clusterManager) {
super(context, map, clusterManager);
}
#Override
protected void onBeforeClusterItemRendered(MyItem item, MarkerOptions markerOptions) {
super.onBeforeClusterItemRendered(item, markerOptions);
markerOptions.title("").icon(BitmapDescriptorFactory.fromResource(R.mipmap.location_both));// for marker
}
#Override
protected void onClusterItemRendered(final MyItem clusterItem, Marker marker) {
super.onClusterItemRendered(clusterItem, marker);
}
}
code in my fragment for setup cluser
googleMap.clear();
mClusterManager = new ClusterManager<MyItem>(getActivity(), googleMap);
googleMap.setOnMarkerClickListener(mClusterManager);
googleMap.setOnCameraChangeListener(mClusterManager);
readItems();
mClusterManager.setRenderer(new MyClusterRenderer(getActivity(), googleMap, mClusterManager));
The best way is
private val clusterIconGenerator = IconGenerator(context)
override fun getDescriptorForCluster(cluster: Cluster<PromotionMarker>): BitmapDescriptor {
clusterIconGenerator.setBackground(ContextCompat.getDrawable(context, R.drawable.background_marker))
val icon: Bitmap = clusterIconGenerator.makeIcon(cluster.size.toString())
return BitmapDescriptorFactory.fromBitmap(icon)
}
Try to change Cluster icon in onBeforeClusterRendered method instead of onBeforeClusterItemRendered :
#Override
protected void onBeforeClusterRendered(Cluster<MyItem> cluster, MarkerOptions markerOptions) {
markerOptions.icon(BitmapDescriptorFactory.fromResource(R.mipmap.location_both));
}
public class MyClusterRenderer extends DefaultClusterRenderer<MyItem> {
private final IconGenerator mClusterIconGenerator = new IconGenerator(
getActivity());
public MyClusterRenderer(Context context, GoogleMap map,
ClusterManager<MyItem> clusterManager) {
super(context, map, clusterManager);
View multiProfile = getActivity().getLayoutInflater().inflate(
R.layout.cluster_custome_icon, null);
mClusterIconGenerator.setContentView(multiProfile);
}
#Override
protected void onBeforeClusterItemRendered(MyItem item, MarkerOptions markerOptions) {
markerOptions.title("").icon(BitmapDescriptorFactory.fromResource(R.mipmap.location_both));
super.onBeforeClusterItemRendered(item, markerOptions);
}
// #Override
// protected void onClusterItemRendered(final MyItem clusterItem, Marker marker) {
// super.onClusterItemRendered(clusterItem, marker);
//
// }
#Override
protected void onBeforeClusterRendered(Cluster<MyItem> cluster,
MarkerOptions markerOptions) {
Log.e("get_item_list_nir", "CallMap onBeforeClusterRendered 13");
try {
mClusterIconGenerator.setBackground(null);
Bitmap icon = mClusterIconGenerator.makeIcon(String.valueOf(cluster
.getSize()));
markerOptions.icon(BitmapDescriptorFactory.fromBitmap(icon));
} catch (Exception e) {
e.printStackTrace();
Log.e("get_item_list_nir", "error 13.1 : " + e.toString());
}
Log.e("get_item_list_nir", "CallMap onBeforeClusterRendered 14");
}
}
I am implementing android Mapview with Custom marker. The custom marker has a background view to which an image is asynchronously loaded. I am using Picasso to load image in to marker view. Here is the code I am using,
for(int i=0;i<10;i++)
{
// latitude and longitude
double latitude = getShop_latitude(i);
double longitude = getShop_longitude(i);
// create marker
MarkerOptions marker = new MarkerOptions().position(
new LatLng(latitude, longitude)).title("");
PicassoMarker pm = new PicassoMarker(marker,getActivity());
String url = PPConfig.IMAGE_URL + Uri.encode(getPpyOfferIcon(i));
Picasso.with(getActivity()).load(url).into(pm);
// Changing marker icon
builder.include(marker.getPosition());
// adding marker
googleMap.addMarker(marker);
}
Here is the class that extends Target
public class PicassoMarker implements Target {
private MarkerOptions mMarker;
private FragmentActivity mActivity;
public PicassoMarker(MarkerOptions marker,FragmentActivity activity) {
mMarker = marker;
mActivity = activity;
}
#Override
public int hashCode() {
return mMarker.hashCode();
}
#Override
public boolean equals(Object o) {
if(o instanceof PicassoMarker) {
MarkerOptions marker = ((PicassoMarker) o).mMarker;
return mMarker.equals(marker);
} else {
return false;
}
}
#Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
//Load Custom marker view
View markerView = mActivity.getLayoutInflater().inflate(R.layout.fragment_map_marker,
null);
ImageView image = (ImageView) markerView.findViewById(R.id.markerImage);
image.setImageBitmap(bitmap);
RelativeLayout rl = (RelativeLayout)markerView.findViewById(R.id.markerContent);
rl.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Toast.makeText(view.getContext(), "Clicked", Toast.LENGTH_LONG).show();
}
});
mMarker.icon(BitmapDescriptorFactory
.fromBitmap(createDrawableFromView(mActivity, markerView)));
}
#Override
public void onBitmapFailed(Drawable errorDrawable) {
}
#Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
}
// Convert a view to bitmap
private Bitmap createDrawableFromView(Context context, View view) {
DisplayMetrics displayMetrics = new DisplayMetrics();
mActivity.getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
view.setLayoutParams(new FrameLayout.LayoutParams(FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT));
view.measure(displayMetrics.widthPixels, displayMetrics.heightPixels);
view.layout(0, 0, 107, 117);
view.buildDrawingCache();
Bitmap bitmap = Bitmap.createBitmap(view.getMeasuredWidth(), view.getMeasuredHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
view.draw(canvas);
return bitmap;
}
}
But this code is not loading my custom marker view, instead its loading the default marker pin point. Whats going wrong here?
Thanks.
Your PicassoMarker will be garbage collected before your onBitmapLoaded() method is called asynchronously. So you need a reference to hold your PicassoMarker object.
sample code in Activity:
public class MainActivity extends FragmentActivity {
private PicassoMarker target;
private GoogleMap mMap;
private static LatLng goodLatLng = new LatLng(37, -120);
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
setUpMapIfNeeded();
MarkerOptions markerOne = new MarkerOptions().position(goodLatLng).title("");
target = new PicassoMarker(mMap.addMarker(markerOne));
Picasso.with(MainActivity.this).load("http://icons.iconarchive.com/icons/yellowicon/game-stars/256/Mario-icon.png").into(target);
}
}
Sample code in PicassoMaker:
public class PicassoMarker implements Target {
Marker mMarker;
PicassoMarker(Marker marker) {
Log.d("test: ", "init marker");
mMarker = marker;
}
#Override
public int hashCode() {
return mMarker.hashCode();
}
#Override
public boolean equals(Object o) {
if(o instanceof PicassoMarker) {
Marker marker = ((PicassoMarker) o).mMarker;
return mMarker.equals(marker);
} else {
return false;
}
}
#Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
Log.d("test: ", "bitmap loaded");
mMarker.setIcon(BitmapDescriptorFactory.fromBitmap(bitmap));
}
#Override
public void onBitmapFailed(Drawable errorDrawable) {
Log.d("test: ", "bitmap fail");
}
#Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
Log.d("test: ", "bitmap preload");
}
}
Try this code
Picasso.with(getActivity()).load(url).placeholder(R.mipmap.ic_launcher).resize(200, 200).into(img_profile, new Callback() {
#Override
public void onSuccess() {
DebugLog.e("Success");
dropPinEffect(mMap.addMarker(new MarkerOptions()
.position(markerLatLng)
.title("Title")
.snippet("Description")
.icon(BitmapDescriptorFactory.fromBitmap(createDrawableFromView(getActivity(), marker)))));
}
#Override
public void onError() {
}
});
Anybody please tell me how to do lazy loading of markers because all of my marker image are getting from server. I can be able to download it from server but this will be displayed only if there is a map movement ie The marker image get updated only if it's already cached, the object reference wouldn't work anymore , google map convert marker view to bitmap and set it. If the user keeps the map idle then it wouldn't load the image. :(
I spend almost a week and do the possible but it's -ve. If anyone can give a spark it would be really really helpful.
public class ROverViewRender extends DefaultClusterRenderer<RItem> {
private IconGenerator mIconGenerator;
private IconGenerator mClusterIconGenerator;
private RImageView mImageView;
private RImageView mImageVieStar;
private ImageView mClusterImageView;
private Context context;
private ClusterManager<RItem> mClusterManager;
private ImageLoader imageLoader;
public ROverViewRender(Context context, GoogleMap map, ClusterManager<RItem> clusterManager) {
super(context, map, clusterManager);
this.mClusterManager=clusterManager;
Activity activity = (Activity) context;
this.context=context;
mClusterIconGenerator=new IconGenerator(context);
View clustorIcon = activity.getLayoutInflater().inflate(R.layout.clustor_icon, null);
mClusterIconGenerator.setContentView(clustorIcon);
mIconGenerator=new IconGenerator(context);
View markerIcon = activity.getLayoutInflater().inflate(R.layout.marker_icon, null);
mIconGenerator.setContentView(markerIcon);
mImageView=(RImageView)markerIcon.findViewById(R.id.marker_image);
mImageVieStar=(RImageView)markerIcon.findViewById(R.id.marker_route_rating);
mClusterImageView=(NetworkImageView)clustorIcon.findViewById(R.id.cluster_image);
imageLoader=VolleySingleton.getInstance(context).getImageLoader();;
}
#Override
protected void onBeforeClusterItemRendered(final RItem item, MarkerOptions markerOptions) {
setMarkerImage(item,markerOptions);
Bitmap icon = mIconGenerator.makeIcon();
markerOptions.icon(BitmapDescriptorFactory.fromBitmap(icon));
}
#Override
protected void onBeforeClusterRendered(Cluster<RItem> cluster, MarkerOptions markerOptions) {
Drawable drawable =context.getResources().getDrawable(R.drawable.ic_launcher);
mClusterImageView.setImageDrawable(drawable);
Bitmap icon = mClusterIconGenerator.makeIcon(String.valueOf(cluster.getSize()));
markerOptions.icon(BitmapDescriptorFactory.fromBitmap(icon));
}
#Override
protected boolean shouldRenderAsCluster(Cluster cluster) {
// Always render clusters.
return cluster.getSize() > 1;
}
#Override
protected void onClusterItemRendered(RItem clusterItem, Marker marker) {
RLog.e("Invoked the ....>>>>>");
// setMarkerImage(clusterItem);
}
private void setMarkerImage(RItem item,final MarkerOptions markerOptions){
RLog.e("Image setter invoked..."+item.getMarkerUrl());
mImageView.setImageUrl(item.getMarkerUrl(), imageLoader);
imageLoader.get(item.getMarkerUrl(), new ImageListener() {
public void onErrorResponse(VolleyError error) {
// imageView.setImageResource(R.drawable.icon_error); // set an error image if the download fails
}
public void onResponse(ImageContainer response, boolean arg1) {
RLog.e("Image download completed...");
if (response.getBitmap() != null) {
mImageView.setImageBitmap(response.getBitmap());
}
}
});
}
}
I wrote example code and it might be worked.
I use Picasso for image loading library.
complete code is below
https://github.com/sis-yoshiday/Example/tree/master/app/src/main/java/jp/sis/yoshiday/example/android/widget/map
class MyClusterRenderer extends DefaultClusterRenderer<MyClusterItem> {
private final IconGenerator iconGenerator;
private final IconGenerator clusterIconGenerator;
private final ImageView imageView;
private final ImageView clusterImageView;
private final int markerWidth;
private final int markerHeight;
public MyClusterRenderer(Context context, GoogleMap map,
ClusterManager<MyClusterItem> clusterManager) {
super(context, map, clusterManager);
// initialize cluster icon generator
clusterIconGenerator = new IconGenerator(context.getApplicationContext());
View clusterView = LayoutInflater.from(context).inflate(R.layout.marker_cluster, null);
clusterIconGenerator.setContentView(clusterView);
clusterImageView = (ImageView) clusterView.findViewById(R.id.image);
// initialize cluster item icon generator
iconGenerator = new IconGenerator(context.getApplicationContext());
imageView = new ImageView(context.getApplicationContext());
markerWidth = (int) getResources().getDimension(R.dimen.marker_width);
markerHeight = (int) getResources().getDimension(R.dimen.marker_height);
imageView.setLayoutParams(new ViewGroup.LayoutParams(markerWidth, markerHeight));
int padding = (int) getResources().getDimension(R.dimen.marker_space);
imageView.setPadding(padding, padding, padding, padding);
iconGenerator.setContentView(imageView);
}
#Override protected void onBeforeClusterItemRendered(MyClusterItem item,
MarkerOptions markerOptions) {
loadAsync(imageView, item.getImageUrl());
try {
Bitmap icon = iconGenerator.makeIcon();
markerOptions.icon(BitmapDescriptorFactory.fromBitmap(icon));
BitmapUtils.recycle(icon);
} catch (Exception e) {
// FIXME workaround : called after Bitmap recycled
Log.e(TAG, e.getMessage(), e);
}
}
#Override protected void onBeforeClusterRendered(Cluster<MyClusterItem> cluster,
MarkerOptions markerOptions) {
Iterator<MyClusterItem> iterator = cluster.getItems().iterator();
loadAsync(clusterImageView, iterator.next().getImageUrl());
try {
Bitmap icon = clusterIconGenerator.makeIcon(String.valueOf(cluster.getSize()));
markerOptions.icon(BitmapDescriptorFactory.fromBitmap(icon));
BitmapUtils.recycle(icon);
} catch (Exception e) {
// FIXME workaround : called after Bitmap recycled
Log.e(TAG, e.getMessage(), e);
}
}
#Override
protected boolean shouldRenderAsCluster(Cluster cluster) {
return cluster.getSize() > 1;
}
private void loadAsync(final ImageView imageView, final String url) {
// first, get from cache
Bitmap bitmap = thumbCache.get(url);
if (bitmap == null || bitmap.isRecycled()) {
// if there are no cached entry or already recycled, start async load
Picasso.with(getActivity()).load(url).into(new Target() {
#Override public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
imageView.setImageBitmap(bitmap);
thumbCache.put(url, bitmap);
}
#Override public void onBitmapFailed(Drawable errorDrawable) {
imageView.setImageResource(R.drawable.ic_launcher);
}
#Override public void onPrepareLoad(Drawable placeHolderDrawable) {
}
});
} else {
imageView.setImageBitmap(bitmap);
}
}
}
I'm using Google Maps Android API Utility Library and I'm downloading certain images from internet that I want to use as markers.
The way I'm doing it is like in the following snippet:
class MarkerItemClusterRenderer extends DefaultClusterRenderer<MarkerItem> {
...
#Override
protected void onBeforeClusterItemRendered(MarkerItem item,
final MarkerOptions markerOptions) {
super.onBeforeClusterItemRendered(item, markerOptions);
mImageLoader.get(item.getImageUrl(), new ImageListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.i("XXX", error.toString());
}
#Override
public void onResponse(ImageContainer response, boolean isImmediate) {
if (response != null && response.getBitmap() != null) {
mImageIcon.setImageBitmap(response.getBitmap());
Bitmap icon = mIconGenerator.makeIcon();
Bitmap bhalfsize = Bitmap.createScaledBitmap(icon, 150,
150, false);
markerOptions.icon(BitmapDescriptorFactory
.fromBitmap(bhalfsize));
}
}
});
}
The problem is, that when the image is downloaded, the map (and thus the marker) doesn't refresh, so most of the times (but not always) I still see the red default markers.
I tried to do mImageIcon.invalidate(); mImageIcon.requestLayout(); but there's still no luck.
Is there anyway to achieve this?
Thanks a lot in advance.
You just need to make all this stuff in
protected void onClusterItemRendered(T clusterItem, Marker marker) {
...
}
In onBeforeClusterItemRendered you set icon on MarkerOptions in async callback. At this time it could be added to map and become real Marker. So you icon will be set to already useless object.
That's why you need to do it in onClusterItemRendered
Let's say you have GoogleMap object declared as:
private GoogleMap mMap;
In onResponse() method before applying any change to marker, try writing following statement to clear previous markers:
mMap.clear();
Now set your new marker.
I might be a bit late but i write it down so it can be useful for somebody looking for a solution like i was.
Basically what you have to do is refresh the marker and not the ClusterItem, but i used my own ClusterItem implementation to store some important data.
So your code inside onBeforeClusterItemRendered becomes like this:
LatLngBounds bounds = map.getProjection().getVisibleRegion().latLngBounds; //take visible region on map
if(bounds.contains(item.getPosition()) && !item.hasImage()) { //if item is not inside that region or it has an image already don't load his image
mImageLoader.get(item.getImageUrl(), new ImageListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.i("XXX", error.toString());
}
#Override
public void onResponse(ImageContainer response, boolean isImmediate) {
if (response != null && response.getBitmap() != null) {
mImageIcon.setImageBitmap(response.getBitmap());
Bitmap icon = mIconGenerator.makeIcon();
Bitmap bhalfsize = Bitmap.createScaledBitmap(icon, 150,
150, false);
//Set has image flag
item.setHasImage(true);
//Find the right marker
MarkerManager.Collection markerCollection = mClusterManager.getMarkerCollection();
Collection<Marker> markers = markerCollection.getMarkers();
for (Marker m : markers) {
if (id.equals(m.getTitle())) {
//set the icon
m.setIcon(BitmapDescriptorFactory.fromBitmap(image));
break;
}
}
}
}
});
}
And your MyItem class must have some parameters which are useful for remember our stuff:
public class MyItem implements ClusterItem {
private String itemId;
private LatLng mPosition;
private WMWall wall;
private boolean hasImage = false;
public MyItem(double latitude, double longitude) {
mPosition = new LatLng(latitude, longitude);
}
#Override
public LatLng getPosition() {
return mPosition;
}
public WMWall getWall() {
return wall;
}
public void setWall(WMWall wall) {
this.wall = wall;
}
public String getItemId() {
return itemId;
}
public void setItemId(String itemId) {
this.itemId = itemId;
}
public boolean hasImage() {
return hasImage;
}
public void setHasImage(boolean hasImage) {
this.hasImage = hasImage;
}
}
It is really important to load only the images of markers contained into bounds, otherwise you'll run into OOM.
And if the hasImage() method returns true we don't need to load the image again since it is already stored into the marker object.
I'm using Google Maps Android API Utility Library in order to show several markers in a map in a clustered way.
I've followed the instructions to make it work, as well as taking a look at the examples in the library, but I can't figure out how to show an InfoWindow when a marker is clicked.
I guess getMap().setOnMarkerClickListener(mClusterManager); is the one managing the onClick events, and if commented out, I can override it using getMap().setInfoWindowAdapter(new InfoWindowAdapter() {)); but I have no access to my custom marker object.
Nevertheless, if I use getMap().setOnMarkerClickListener(mClusterManager);, I can't find a way to show the InfoWindow when a marker has been clicked.
Does anybody have any idea about how to achieve this?
Thanks a lot in advance!
You need to extend the DefaultClusterRenderer class and override the onBeforeClusterItemRendered, attaching the title to the MarkerOptions object passed as argument.
After that, you can pass your implementation to the ClusterManager.
Example:
class MyItem implements ClusterItem {
private LatLng mPosition;
private String mTitle;
public MyItem(LatLng position){
mPosition = position;
}
#Override
public LatLng getPosition() {
return mPosition;
}
public String getTitle() {
return mTitle;
}
public void setTitle(String title) {
mTitle = title;
}
}
class MyClusterRenderer extends DefaultClusterRenderer<MyItem> {
public MyClusterRenderer(Context context, GoogleMap map,
ClusterManager<MyItem> clusterManager) {
super(context, map, clusterManager);
}
#Override
protected void onBeforeClusterItemRendered(MyItem item, MarkerOptions markerOptions) {
super.onBeforeClusterItemRendered(item, markerOptions);
markerOptions.title(item.getTitle());
}
#Override
protected void onClusterItemRendered(MyItem clusterItem, Marker marker) {
super.onClusterItemRendered(clusterItem, marker);
//here you have access to the marker itself
}
}
And then you can use it in this way:
ClusterManager<MyItem> clusterManager = new ClusterManager<MyItem>(this, getMap());
clusterManager.setRenderer(new MyClusterRenderer(this, getMap() ,clusterManager));