I'm currently trying to write an app that shows several locations on a map using markers. Even when working with only a relatively 'small' set of Markers (I've seen StackOverflow issues where people want to draw more than 100k), I get terrible performance and my map takes a few seconds to move.
I also have a screenshot of my GPU profiling, and I'm wondering if anyone can understand why these calls are so extremely high:
I have tried to profile my application using the DDMS, but this doesn't seem to give me any conclusive answers.
The code I'm using is as follows:
tab_map.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
android:name="com.google.android.gms.maps.SupportMapFragment"
class="com.google.android.gms.maps.SupportMapFragment"
android:id="@+id/city_map"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="?attr/actionBarSize" />
</FrameLayout>
LocationFragment.java
/* a whole list of imports and package definition */
/**
* Created by Ruben on 05-08-17.
*/
public class LocationsFragment extends Fragment
implements OnMapReadyCallback {
private static final String TAG = LocationsFragment.class.getSimpleName();
private LocationsFragment instance;
private City city;
private List<Marker> markers;
private List<Location> locations;
private Target iconTarget;
private GoogleMap map;
private SupportMapFragment mapFragment;
private BitmapDescriptor markerIcon;
private Bitmap markerIconBitmap;
private ApiService apiService;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.tab_map, null);
}
@Override
public void onActivityCreated(Bundle savedInstanceStace) {
super.onActivityCreated(savedInstanceStace);
// Set up the view
instance = this;
mapFragment = (SupportMapFragment) getChildFragmentManager().findFragmentById(R.id.city_map);
apiService = RestClient.getClient(getContext()).create(ApiService.class);
city = getArguments().getParcelable("city");
markers = new ArrayList<>();
iconTarget = new Target() {
@Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
markerIconBitmap = bitmap;
updateMarkerIcons();
}
@Override
public void onBitmapFailed(Drawable errorDrawable) {
}
@Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
}
};
// Start async processes
// Load Google map
mapFragment.getMapAsync(this);
// Load locations from API
getLocations();
// Load custom marker for city
getCityMarker();
}
private void getCityMarker() {
Picasso
.with(getContext())
.load(city.getMarker().getUrl())
.resize(ScreenUtils.convertDIPToPixels(getContext(), 24), ScreenUtils.convertDIPToPixels(getContext(), 24))
.into(iconTarget);
}
@Override
public void onMapReady(GoogleMap map) {
this.map = map;
if(markerIconBitmap == null) {
markerIcon = BitmapDescriptorFactory.fromAsset("default1.png");
}else{
markerIcon = BitmapDescriptorFactory.fromBitmap(markerIconBitmap);
}
if (ContextCompat.checkSelfPermission(this.getContext(), "ACCESS_FINE_LOCATION") == PackageManager.PERMISSION_GRANTED) {
map.setMyLocationEnabled(true);
} else {
Toast.makeText(instance.getContext(), getString(R.string.no_location_permission), Toast.LENGTH_LONG).show();
}
map.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(city.getCenter().getLat(), city.getCenter().getLong()), 12));
setMarkers();
}
public void getLocations() {
Call<LocationsResponse> call = apiService.getLocations(city.getId());
call.enqueue(new Callback<LocationsResponse>() {
@Override
public void onResponse(Call<LocationsResponse> call, Response<LocationsResponse> response) {
if(response.code() != 200) {
Toast.makeText(instance.getContext(), getString(R.string.retrieving_locations) + "1", Toast.LENGTH_LONG).show();
return;
}
Log.d(TAG, "Received locations: " + response.body().getLocations().size());
locations = response.body().getLocations();
setMarkers();
}
@Override
public void onFailure(Call<LocationsResponse> call, Throwable t) {
t.printStackTrace();
Toast.makeText(instance.getContext(), getString(R.string.retrieving_locations) + "1", Toast.LENGTH_LONG).show();
}
});
}
public void setMarkers() {
Log.d(TAG, "Calling setMarkers");
if(map != null && locations != null) {
// Remove all the old markers
if(!markers.isEmpty()) {
int numMarkers = markers.size();
for(int i = 0; i < numMarkers; i++) {
markers.get(i).remove();
}
markers.clear();
}
// Create a new marker for each location
int numLocations = locations.size();
Log.d(TAG, "Setting markers for " + numLocations + " locations");
for(int i = 0; i < numLocations; i++) {
Location location = locations.get(i);
markers.add(map.addMarker(
new MarkerOptions()
.position(new LatLng(location.getPosition().getLat(), location.getPosition().getLong()))
//.icon(markerIcon)
//.title(location.getName())
//.snippet(getString(R.string.location_marker_text, NumberFormat.getInstance().format(location.getStones())))
));
}
}
}
public void updateMarkerIcons() {
Log.d(TAG, "Calling updateMarkerIcons");
if(map != null && markerIconBitmap != null) {
markerIcon = BitmapDescriptorFactory.fromBitmap(markerIconBitmap);
}
if(map != null && !markers.isEmpty()) {
int numMarkers = markers.size();
for(int i = 0; i < numMarkers; i++) {
Marker marker = markers.get(i);
//marker.setIcon(markerIcon);
}
}
}
@Override
public void onDestroyView() {
Picasso.with(this.getContext()).cancelRequest(iconTarget);
mapFragment.onDestroyView();
super.onDestroyView();
}
public void updateLocations() {
if(locations != null) {
getLocations();
}
}
public void setCity(City city) {
this.city = city;
getCityMarker();
}
}
As you can see, I uncommented some lines where I set the .snippet or .icon for the markers, as I suspected this might have been the issue. Sadly, this didn't seem to be the problem.
Using breakpoints, I tried to confirm that none of my functions where I set / edit markers are being called after they should, and I found out that this is definitely not the case. Once the markers are set, no additional code is executed in this file. All my other activities seem to run fine, so I'm starting to believe it's an issue related to Google Maps. What is worth noting, is that when I move away from the markers, meaning not being in the boundaries of my screen anymore, the performance is decent and scrolling looks smooth.
