Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

IllegalArgumentException: Unmanaged descriptor using gms.maps.model.Marker.setIcon

I have an app that use android-maps-utils and glide for marker icons.
I got an error report using Firebase crash reporting which I can't track in source code because gms.maps.model.Marker.setIcon is private, so I'm asking for some help with this problem.
The follow part of the question is divided into:

  • What the user was doing
  • What firebase crash reported to me
  • Some project configs
  • What I tried/found trying to understand/fix it

What the user was doing
He was zooming in and out in a map (Fragment that uses com.google.android.gms.maps.SupportMapFragment)

What firebase crash reported to me

Exception java.lang.IllegalArgumentException: Unmanaged descriptor
com.google.maps.api.android.lib6.common.k.b (:com.google.android.gms.DynamiteModulesB:162)
com.google.maps.api.android.lib6.impl.o.c (:com.google.android.gms.DynamiteModulesB:75)
com.google.maps.api.android.lib6.impl.db.a (:com.google.android.gms.DynamiteModulesB:334)
com.google.android.gms.maps.model.internal.q.onTransact (:com.google.android.gms.DynamiteModulesB:204)
android.os.Binder.transact (Binder.java:387)
com.google.android.gms.maps.model.internal.zzf$zza$zza.zzL () com.google.android.gms.maps.model.Marker.setIcon ()
co.com.spyspot.ui.content.sucursal.SucursalRender$CustomSimpleTarget.onResourceReady (SucursalRender.java:156)
co.com.spyspot.ui.content.sucursal.SucursalRender$CustomSimpleTarget.onResourceReady (SucursalRender.java:130)
com.bumptech.glide.request.GenericRequest.onResourceReady (GenericRequest.java:525)
com.bumptech.glide.request.GenericRequest.onResourceReady (GenericRequest.java:507)
com.bumptech.glide.load.engine.EngineJob.handleResultOnMainThread (EngineJob.java:158)
com.bumptech.glide.load.engine.EngineJob.access$100 (EngineJob.java:22)
com.bumptech.glide.load.engine.EngineJob$MainThreadCallback.handleMessage (EngineJob.java:202)
android.os.Handler.dispatchMessage (Handler.java:98)
android.os.Looper.loop (Looper.java:148)
android.app.ActivityThread.main (ActivityThread.java:5443)
java.lang.reflect.Method.invoke (Method.java)
com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run (ZygoteInit.java:728)
com.android.internal.os.ZygoteInit.main (ZygoteInit.java:618)

And:

enter image description here

Some project configs

  • I'm using a Custom Render (SucursalRender extends DefaultClusterRenderer<Sucursal>)
  • I'm downloading the Marker icon with Glide like I said before: Glide.with(context).load(id).fitCenter().placeholder(R.drawable.ic_no_image).into(simpleTarget);

The simpleTarget is where I handle the images downloaded/cached for Glide. I'm posting all code about simpleTarget because the crash is starting there:

private class CustomSimpleTarget extends SimpleTarget<GlideDrawable> {     Sucursal sucursal;     Marker markerToChange = null;      @Override     public void onResourceReady(GlideDrawable resource, GlideAnimation<? super GlideDrawable> glideAnimation) {         mImageView.setImageDrawable(resource);         //currentSelectedItem is the current element selected in the map (Sucursal type)         //mIconGenerator is a: CustomIconGenerator extends IconGenerator         if (currentSelectedItem != null && sucursal.idalmacen.contentEquals(currentSelectedItem.idalmacen))             mIconGenerator.customIconBackground.useSelectionColor(true, ContextCompat.getColor(mContext, R.color.colorAccent));         else             mIconGenerator.customIconBackground.useSelectionColor(false, 0);          Bitmap icon = mIconGenerator.makeIcon();          if (markerToChange == null) {             for (Marker marker : mClusterManager.getMarkerCollection().getMarkers()) {                 if (marker.getPosition().equals(sucursal.getPosition())) {                     markerToChange = marker;                 }             }         }          // if found - change icon         if (markerToChange != null) {             //GlideShortcutDrawable is a WeakReference<>(drawable)             sucursal.setGlideShortCutDrawable(resource);             markerToChange.setIcon(BitmapDescriptorFactory.fromBitmap(icon));         }     } } 

The crash is being thrown in last line of code: markerToChange.setIcon(BitmapDescriptorFactory.fromBitmap(icon));

What I tried/found trying to understand/fix it

  • Tried to reproduce the error in 4 real devices without success.
  • Searched in web for similar errors or code about gms.maps.model.Marker.setIcon or com.google.maps.api.android.lib6
  • Tried to understand the obfuscated code given in Android Studio for Marker.setIcon

I guess I can wrap the code in a try-catch block for that IllegalArgumentException: Unmanaged descriptor to avoid application get closed because the crash but it's just a work around it.

update 2
The code of DefaultClusterRenderer:

public class SucursalRender extends DefaultClusterRenderer<Sucursal> {     /**      * Create a customized icon for markers with two background colors. Used with {@link com.google.maps.android.clustering.ClusterItem}.      */     private final CustomIconGenerator mIconGenerator;     /**      * Marker image.      */     private final ImageView mImageView;     /**      * Create a customized icon for {@link Cluster<Sucursal>} with a single background.      */     private final IconGenerator mClusterIconGenerator;     /**      * Cluster image.      */     private final ImageView mClusterImageView;     private final Context mContext;     /**      * Keep a reference to the current item highlighted in UI (the one with different background).      */     public Sucursal currentSelectedItem;     /**      * The {@link ClusterManager<Sucursal>} instance.      */     private ClusterManager<Sucursal> mClusterManager;      public SucursalRender(Context context, GoogleMap map, ClusterManager<Sucursal> clusterManager) {         super(context, map, clusterManager);          mContext = context;         mClusterManager = clusterManager;         mIconGenerator = new CustomIconGenerator(mContext.getApplicationContext());         mClusterIconGenerator = new IconGenerator(mContext.getApplicationContext());          int padding = (int) mContext.getResources().getDimension(R.dimen.custom_profile_padding);         int dimension = (int) mContext.getResources().getDimension(R.dimen.custom_profile_image);          //R.layout.map_cluster_layout is a simple XML with the visual elements to use in markers and cluster         View view = ((AppCompatActivity)mContext).getLayoutInflater().inflate(R.layout.map_cluster_layout, null);         mClusterIconGenerator.setContentView(view);         mClusterImageView = (ImageView) view.findViewById(R.id.image);         mClusterImageView.setPadding(padding, padding, padding, padding);          mImageView = new ImageView(mContext.getApplicationContext());         mImageView.setLayoutParams(new ViewGroup.LayoutParams(dimension, dimension));         mImageView.setPadding(padding, padding, padding, padding);         mIconGenerator.setContentView(mImageView);          CustomIconBackground customIconBackground = new CustomIconBackground(false);         mIconGenerator.setBackground(customIconBackground);         mIconGenerator.customIconBackground = customIconBackground;         mClusterIconGenerator.setBackground(new CustomIconBackground(true));     }      ...      @Override     protected void onBeforeClusterItemRendered(final Sucursal sucursal, MarkerOptions markerOptions) {          mImageView.setImageDrawable(ContextCompat.getDrawable(mContext, R.drawable.ic_no_image));         Bitmap icon = mIconGenerator.makeIcon();         markerOptions.icon(BitmapDescriptorFactory.fromBitmap(icon));     }      @Override     protected void onClusterItemRendered(Sucursal clusterItem, Marker marker) {         CustomSimpleTarget simpleTarget = new CustomSimpleTarget();         simpleTarget.sucursal = clusterItem;         simpleTarget.markerToChange = marker;         ImageLoaderManager.setImageFromId(simpleTarget, clusterItem.logo, mContext);     }      @Override     protected void onBeforeClusterRendered(Cluster<Sucursal> cluster, MarkerOptions markerOptions) {         mClusterImageView.setImageDrawable(ResourcesCompat.getDrawable(mContext.getResources(), R.drawable.ic_sucursales, null));         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;     }      /**      * Just extends {@link IconGenerator} and give the ability to change background.      * Used to know highlight the current selected item in UI.      */     private class CustomIconGenerator extends IconGenerator {         private CustomIconBackground customIconBackground;         private CustomIconGenerator(Context context) {             super(context);         }     }       /**      * Create a custom icon to use with {@link Marker} or {@link Cluster<Sucursal>}      */     private class CustomIconBackground  extends Drawable {          private final Drawable mShadow;         private final Drawable mMask;         private int mColor = Color.WHITE;          private boolean useSelectionColor;         private int mColorSelection;          private CustomIconBackground(boolean isCluster) {             useSelectionColor = false;              if (isCluster) {                 mMask = ContextCompat.getDrawable(mContext, R.drawable.map_pin_negro_cluster);                 mShadow = ContextCompat.getDrawable(mContext, R.drawable.map_pin_transparente_cluster);             }             else {                 mMask = ContextCompat.getDrawable(mContext, R.drawable.map_pin_negro);                 mShadow = ContextCompat.getDrawable(mContext, R.drawable.map_pin_transparente);             }         }          public void setColor(int color) {             mColor = color;         }          private void useSelectionColor(boolean value, int color) {             useSelectionColor = value;             mColorSelection = color;         }         @Override         public void draw(@NonNull Canvas canvas) {             mMask.draw(canvas);             canvas.drawColor(mColor, PorterDuff.Mode.SRC_IN);             mShadow.draw(canvas);              if (useSelectionColor) {                 canvas.drawColor(mColorSelection, PorterDuff.Mode.SRC_IN);                 useSelectionColor = false;             }         }          @Override         public void setAlpha(int alpha) {             throw new UnsupportedOperationException();         }          @Override         public void setColorFilter(ColorFilter cf) {             throw new UnsupportedOperationException();         }          @Override         public int getOpacity() {             return PixelFormat.TRANSLUCENT;         }          @Override         public void setBounds(int left, int top, int right, int bottom) {             mMask.setBounds(left, top, right, bottom);             mShadow.setBounds(left, top, right, bottom);         }          @Override         public void setBounds(@NonNull Rect bounds) {             mMask.setBounds(bounds);             mShadow.setBounds(bounds);         }          @Override         public boolean getPadding(@NonNull Rect padding) {             return mMask.getPadding(padding);         }     } 

The ImageLoaderManager is just a Facade for Glide.

public static void setImageFromId(SimpleTarget<GlideDrawable> simpleTarget, String id, Context context) {      if (context instanceof AppCompatActivity) {         AppCompatActivity activity = (AppCompatActivity)context;         if (activity.isDestroyed())             return;     }     Glide.with(context)             .load(id)             .fitCenter()             .placeholder(R.drawable.ic_no_image)             .into(simpleTarget); } 
like image 382
MiguelHincapieC Avatar asked Jan 27 '17 20:01

MiguelHincapieC


2 Answers

When clearing the map with

    googleMap.clear(); 

**remove any reference to all the markers** on the map. I had the problem and figured out that the problem was with my code which I forgot to remove reference to a marker and tried to change icon of a cleared Marker

like image 135
Morteza Rastgoo Avatar answered Sep 21 '22 14:09

Morteza Rastgoo


I found this happening when accessing marker after it was removed. Interacting with marker in callback is exactly that case. As mentioned in Map's API:

After a marker has been removed, the behavior of all its methods is undefined. https://developers.google.com/android/reference/com/google/android/gms/maps/model/Marker.html#remove()

Best option would be checking is marker removed from map or not.
But we don't have such API. And I found another workaround, we can use Marker's setTag and getTag. Tag is set to null, when marker is removed:

Google Maps Android API neither reads nor writes this property, except that when a marker is removed from the map, this property is set to null. https://developers.google.com/android/reference/com/google/android/gms/maps/model/Marker.html#setTag(java.lang.Object)

When creating marker use some tag for it.
When updating marker check tag is not null.

This could help in your case.

@Override protected void onClusterItemRendered(Sucursal clusterItem, Marker marker) {     // we don't care about tag's type so don't reset original one     if (marker.getTag() == null) {         marker.setTag("anything");     }     CustomSimpleTarget simpleTarget = new CustomSimpleTarget();     simpleTarget.sucursal = clusterItem;     simpleTarget.markerToChange = marker;     ImageLoaderManager.setImageFromId(simpleTarget, clusterItem.logo, mContext); } 

And in callback

private class CustomSimpleTarget extends SimpleTarget<GlideDrawable> {     ...      @Override     public void onResourceReady(GlideDrawable resource, GlideAnimation<? super GlideDrawable> glideAnimation) {         ...          // if found - change icon         if (markerToChange != null) {             //GlideShortcutDrawable is a WeakReference<>(drawable)             sucursal.setGlideShortCutDrawable(resource);             if (markerToChange.getTag != null) {                 markerToChange.setIcon(BitmapDescriptorFactory.fromBitmap(icon));             }         }     } } 
like image 30
Stas Parshin Avatar answered Sep 21 '22 14:09

Stas Parshin