utils utilidades studio polyutil libreria library implement google con compile android google-maps android-glide android-maps-utils

utilidades - libreria google maps android studio



IllegalArgumentException: Descriptor no gestionado utilizando gms.maps.model.Marker.setIcon (6)

Al limpiar el mapa con

googleMap.clear();

**remove any reference to all the markers** en el mapa. Tuve el problema y me di cuenta de que el problema estaba en mi código que olvidé eliminar la referencia a un marcador e intenté cambiar el ícono de un cleared Marker

Tengo una aplicación que usa android-maps-utils y se desliza para los iconos de marcador .
Recibí un informe de error con el informe de bloqueo de Firebase que no puedo rastrear en el código fuente porque gms.maps.model.Marker.setIcon es privado, por lo que estoy pidiendo ayuda con este problema.
La siguiente parte de la pregunta se divide en:

  • Lo que el usuario estaba haciendo
  • Qué informe de bloqueo de Firebase
  • Algunas configuraciones de proyectos
  • Lo que intenté / encontré tratando de entender / arreglarlo

Lo que el usuario estaba haciendo
Estaba acercándose y alejándose en un mapa ( Fragment que usa com.google.android.gms.maps.SupportMapFragment )

Qué informe de bloqueo de Firebase

Excepción java.lang.IllegalArgumentException: descriptor no administrado
com.google.maps.api.android.lib6.common.kb (: com.google.android.gms.DynamiteModulesB: 162)
com.google.maps.api.android.lib6.impl.oc (: 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)

Y:


Algunas configuraciones de proyectos

  • Estoy usando una Renderización Personalizada ( SucursalRender extends DefaultClusterRenderer<Sucursal> )
  • Estoy descargando el ícono de Marker con Glide como dije antes: Glide.with(context).load(id).fitCenter().placeholder(R.drawable.ic_no_image).into(simpleTarget);

El simpleTarget es donde manejo las imágenes descargadas / almacenadas en caché para Glide. Estoy publicando todo el código sobre simpleTarget porque el bloqueo está comenzando allí:

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)); } } }

El bloqueo se está markerToChange.setIcon(BitmapDescriptorFactory.fromBitmap(icon)); en la última línea de código: markerToChange.setIcon(BitmapDescriptorFactory.fromBitmap(icon));

Lo que intenté / encontré tratando de entender / arreglarlo

  • Intenté reproducir el error en 4 dispositivos reales sin éxito.
  • Se buscó en la web errores similares o código sobre gms.maps.model.Marker.setIcon o com.google.maps.api.android.lib6
  • Intenté comprender el código ofuscado dado en Android Studio para Marker.setIcon

Creo que puedo envolver el código en un try-catch block para esa excepción IllegalArgumentException: Unmanaged descriptor para evitar que la aplicación se cierre porque se cuelga, pero es solo una solución.

actualización 2
El código de 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); } }

ImageLoaderManager es solo una fachada para 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); }


Asegúrese de que el ícono que está utilizando para el marcador no debe ser vectorial, debe ser una imagen .png.


Descubrí que esto sucedía cuando accedía al marcador después de eliminarlo. La interacción con marcador en devolución de llamada es exactamente ese caso. Como se menciona en la API de Map:

Después de eliminar un marcador, el comportamiento de todos sus métodos no está definido. https://developers.google.com/android/reference/com/google/android/gms/maps/model/Marker.html#remove()

La mejor opción sería verificar si el marcador se eliminó del mapa o no.
Pero no tenemos esa API. Y encontré otra solución, podemos usar setTag y getTag . Tag se establece en nulo, cuando se elimina marcador:

Google Maps Android API no lee ni escribe esta propiedad, excepto que cuando se quita un marcador del mapa, esta propiedad se establece en nulo. https://developers.google.com/android/reference/com/google/android/gms/maps/model/Marker.html#setTag(java.lang.Object)

Cuando crees un marcador usa alguna etiqueta para él.
Al actualizar la etiqueta de verificación de marcador no es nulo.

Esto podría ayudar en su caso.

@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); }

Y en devolución de llamada

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)); } } } }


Esta excepción ocurre cuando su marcador fue reclusado por ClusterManager . ClusterManager recrea marcador en la agrupación. Entonces, para evitarlo, debes obtener tu marcador del renderizado de ClusterManeger :

ClusterIconRender render = (ClusterIconRender) mClusterManager.getRenderer(); Marker trueMarker = render.getMarker(clusterMarker); if (trueMarker != null) { trueMarker.setIcon(...); ... // do whatever else your want with marker }

En el código anterior, ClusterMarker implementa ClusterItem y ClusterIconRender extiende DefaultClusterRenderer .


También estaba obteniendo la misma excepción y establecer la excepción silenciosa con try / catch no habría sido una solución, ya que el usuario no puede ver la ubicación actual en mi caso:

java.lang.IllegalArgumentException: descriptor no administrado en com.google.maps.api.android.lib6.common.kb (: com.google.android.gms.DynamiteModulesB: 162) en com.google.maps.api.android.lib6 .impl.oc (: com.google.android.gms.DynamiteModulesB: 75) en com.google.maps.api.android.lib6.impl.db.a (: com.google.android.gms.DynamiteModulesB: 334) en com.google.android.gms.maps.model.internal.q.onTransact (: com.google.android.gms.DynamiteModulesB: 204) en android.os.Binder.transact (Binder.java:361) en com. google.android.gms.maps.model.internal.zzf $ zza $ zza.zzL (Fuente desconocida) en com.google.android.gms.maps.model.Marker.setIcon (Fuente desconocida)

Lo que estaba haciendo :

Minimice la pantalla del fragmento del mapa presionando el botón de inicio y luego iniciando la aplicación desde el iniciador.

Qué código estaba haciendo:

La comprobación es marcador no es nulo y la ubicación no es nula conjunto de ubicación e icono.

if (markerCurrentLocation == null && googleMap != null) { markerCurrentLocation = googleMap.addMarker(new MarkerOptions() .position(new LatLng(0.0, 0.0)) .icon(null)); markerCurrentLocation.setTag(-101); } if (markerCurrentLocation != null && location != null) { markerCurrentLocation.setPosition(new LatLng(location.getLatitude(), location.getLongitude())); if (ORDER_STARTED) { markerCurrentLocation.setIcon(CURRENT_MARKER_ORANGE); } else { markerCurrentLocation.setIcon(CURRENT_MARKER_GRAY); } }

La excepción fue en: markerCurrentLocation.setIcon ();

Cómo me deshice de esta excepción:

Eliminé la siguiente línea

if (markerCurrentLocation == null && googleMap != null)

Lo que significa que estoy inicializando marcador de nuevo. Si encuentra este error, intente no configurarIcon () en el marcador anterior, en su lugar infle el marcador nuevo y luego use setIcon ().

Explicación:

Yo ASUMO (no estoy seguro) el motivo de excepción fue si el código está tratando de volver a establecer (nuevamente) en el marcador en el que ya está configurado, en una instancia particular como en mi caso, el Mapa se está reanudando o puede estar en su marcador de caso fuera de la parte visible de mapa y entra o algo similar.

Por supuesto, no hay problema con el descriptor que obtenemos del método BitmapDescriptorFactory.fromBitmap () o BitmapDescriptorFactory.fromResource (). Como los consejos de la excepción, el descriptor no se administró en un marcador anterior, mejor use uno nuevo.


Tengo el mismo entorno (maps-utils + renderizador personalizado + Glide) y el mismo error IllegalArgumentException: Unmanaged descriptor .

Resolví el error comprobando si el marcador es "válido" antes de configurar el ícono, usando los métodos DefaultClusterRenderer.getCluster(Marker) y DefaultClusterRenderer.getClusterItem(Marker) . Si ambos devuelven null , no hago nada en el onResourceReady(...) .

En su caso, probaría el siguiente cambio en CustomSimpleTarget :

private class CustomSimpleTarget extends SimpleTarget<GlideDrawable> { Sucursal sucursal; Marker markerToChange = null; @Override public void onResourceReady(GlideDrawable resource, GlideAnimation<? super GlideDrawable> glideAnimation) { if (getCluster(markerToChange) != null || getClusterItem(markerToChange) != null) { 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(); //GlideShortcutDrawable is a WeakReference<>(drawable) sucursal.setGlideShortCutDrawable(resource); markerToChange.setIcon(BitmapDescriptorFactory.fromBitmap(icon)); } } }

PD .: Puedo reproducir el problema fácilmente en un dispositivo lento y borrar el caché de la aplicación antes de realizar la prueba (para forzar a Glide a cargar desde la red). Luego abro el mapa y realizo un acercamiento / alejamiento antes de que se carguen los marcadores.