J'ai une application qui utilise Android-maps-utils et glissez pour les icônes de marqueur .
J'ai reçu un rapport d'erreur en utilisant le rapport d'incident Firebase que je ne peux pas suivre dans le code source car gms.maps.model.Marker.setIcon
Est privé, donc je demande de l'aide avec ce problème.
La partie suivante de la question est divisée en:
Ce que faisait l'utilisateur
Il faisait un zoom avant et arrière sur une carte (Fragment
qui utilise com.google.Android.gms.maps.SupportMapFragment
)
Quel crash Firebase m'a signalé
Exception Java.lang.IllegalArgumentException: descripteur non géré
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)
Et:
Certaines configurations de projet
SucursalRender extends DefaultClusterRenderer<Sucursal>
)Glide.with(context).load(id).fitCenter().placeholder(R.drawable.ic_no_image).into(simpleTarget);
Le simpleTarget
est l'endroit où je gère les images téléchargées/mises en cache pour Glide. Je publie tout le code sur simpleTarget
parce que le crash commence là:
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));
}
}
}
Le crash est lancé dans la dernière ligne de code: markerToChange.setIcon(BitmapDescriptorFactory.fromBitmap(icon));
Ce que j'ai essayé/trouvé en essayant de le comprendre/le réparer
gms.maps.model.Marker.setIcon
Ou com.google.maps.api.Android.lib6
Marker.setIcon
Je suppose que je peux envelopper le code dans un try-catch block
Pour cette IllegalArgumentException: descripteur non géré pour éviter que l'application ne soit fermée parce que le crash mais c'est juste un travail autour.
mise à jour 2
Le code 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);
}
}
Le ImageLoaderManager
n'est qu'une façade pour 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);
}
Lors de l'effacement de la carte avec
googleMap.clear();
**remove any reference to all the markers**
Sur la carte. J'ai eu le problème et j'ai compris que le problème était avec mon code que j'ai oublié de supprimer la référence à un marqueur et j'ai essayé de changer l'icône d'un cleared Marker
J'obtenais également la même exception et la définition d'une exception silencieuse avec try/catch n'aurait pas été une solution car l'utilisateur n'est pas en mesure de voir l'emplacement actuel dans mon cas:
Java.lang.IllegalArgumentException: descripteur non géré sur com.google.maps.api.Android.lib6.common.kb (: com.google.Android.gms.DynamiteModulesB: 162) sur com.google.maps.api.Android.lib6 .impl.oc (: com.google.Android.gms.DynamiteModulesB: 75) sur com.google.maps.api.Android.lib6.impl.db.a (: com.google.Android.gms.DynamiteModulesB: 334) sur com.google.Android.gms.maps.model.internal.q.onTransact (: com.google.Android.gms.DynamiteModulesB: 204) sur Android.os.Binder.transact (Binder.Java:361) sur com. google.Android.gms.maps.model.internal.zzf $ zza $ zza.zzL (Source inconnue) sur com.google.Android.gms.maps.model.Marker.setIcon (Source inconnue)
Ce que je faisais:
Réduisez l'écran du fragment de carte en appuyant sur le bouton d'accueil, puis en lançant l'application à partir du lanceur.
Quel code faisait:
Vérifier que le marqueur n'est pas nul et que l'emplacement n'est pas nul définir l'emplacement et l'icône.
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);
}
}
L'exception était à: markerCurrentLocation.setIcon ();
Comment je me suis débarrassé de cette exception:
J'ai supprimé la ligne suivante
if (markerCurrentLocation == null && googleMap != null)
Ce qui signifie que j'initialise à nouveau le marqueur. Si vous rencontrez cette erreur, essayez de ne pas définir setIcon () sur l'ancien marqueur, au lieu de gonfler le nouveau marqueur, puis utilisez setIcon ().
Explication:
J'ASSUME (pas sûr) que la raison de l'exception était si le code essayait de définir à nouveau SetIcon () sur le marqueur sur lequel il est déjà défini, à un cas particulier comme dans mon cas, la carte reprend ou peut être dans votre cas, le marqueur sort de la partie visible de carte et vient ou quelque chose de similaire.
Pour sûr, il n'y a aucun problème avec le descripteur que nous obtenons de la méthode BitmapDescriptorFactory.fromBitmap () ou BitmapDescriptorFactory.fromResource (). Comme l'indique l'exception, le descripteur n'a pas été géré sur un ancien marqueur, mieux vaut en utiliser un nouveau.
J'ai constaté que cela se produisait lors de l'accès au marqueur après sa suppression. Interagir avec le marqueur dans le rappel est exactement ce cas. Comme mentionné dans l'API de Map:
Après la suppression d'un marqueur, le comportement de toutes ses méthodes n'est pas défini. https://developers.google.com/Android/reference/com/google/Android/gms/maps/model/Marker.html#remove ()
La meilleure option serait de vérifier si le marqueur a été retiré de la carte ou non.
Mais nous n'avons pas une telle API. Et j'ai trouvé une autre solution de contournement, nous pouvons utiliser setTag
et getTag
de Marker. La balise est définie sur null lorsque le marqueur est supprimé:
Google Maps Android ne lit ni n'écrit cette propriété, sauf que lorsqu'un marqueur est supprimé de la carte, cette propriété est définie sur null. https://developers.google. com/Android/reference/com/google/Android/gms/maps/model/Marker.html # setTag (Java.lang.Object)
Lors de la création d'un marqueur, utilisez une balise pour cela.
Lors de la mise à jour, la balise de vérification du marqueur n'est pas nulle.
Cela pourrait vous aider dans votre cas.
@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);
}
Et en rappel
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));
}
}
}
}
Cette exception se produit lorsque votre marqueur a été reclassé par ClusterManager
. ClusterManager
recrée le marqueur sur le clustering. Donc, pour l'éviter, vous devez obtenir votre marqueur à partir du rendu de ClusterManeger
:
ClusterIconRender render = (ClusterIconRender) mClusterManager.getRenderer();
Marker trueMarker = render.getMarker(clusterMarker);
if (trueMarker != null) {
trueMarker.setIcon(...);
... // do whatever else your want with marker
}
Dans le code ci-dessus, ClusterMarker
implémente ClusterItem
et ClusterIconRender
étend DefaultClusterRenderer
.
J'ai le même environnement (maps-utils + rendu personnalisé + Glide) et la même erreur IllegalArgumentException: Unmanaged descriptor
.
J'ai résolu l'erreur en vérifiant si le marqueur est "valide" avant de définir l'icône, en utilisant les méthodes DefaultClusterRenderer.getCluster(Marker)
et DefaultClusterRenderer.getClusterItem(Marker)
. Si les deux renvoient null
, je ne fais rien sur la méthode onResourceReady(...)
.
Dans votre cas, j'essaierais la modification suivante 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));
}
}
}
PS.: Je peux facilement reproduire le problème sur un appareil lent et vider le cache de l'application avant de tester (pour forcer Glide à se charger depuis le réseau). Ensuite, j'ouvre la carte et effectue un zoom avant/arrière avant le chargement des marqueurs.
Assurez-vous que l'icône que vous utilisez pour le marqueur ne doit pas être vectorielle, mais plutôt une image .png.
Je l'ai corrigé en appelant la méthode suivante.
clusterManager.clearItems()
Après cela, vous pouvez définir le bitmap sur les marqueurs.