IllegalArgumentException: неуправляемый дескриптор с использованием gms.maps.model.Marker.setIcon
У меня есть приложение, которое использует android-maps-utils и скольжение для маркера иконки.
Я получил отчет об ошибке с использованием отчетов об аварийных ситуациях Firebase, которые я не могу отслеживать в исходном коде, потому что gms.maps.model.Marker.setIcon
является закрытым, поэтому я прошу о помощи в этой проблеме.
Следующая часть вопроса делится на:
- Что делал пользователь
- О том, что произошло с пожарной базой,
- Некоторые конфигурации проекта
- Что я пытался/нашел, пытаясь понять/исправить его.
Что делал пользователь
Он увеличивал и увеличивал масштаб изображения на карте (Fragment
, которая использует com.google.android.gms.maps.SupportMapFragment
)
О том, что произошло с пожарной базой,
Исключение java.lang.IllegalArgumentException: неуправляемый дескриптор
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)
Соу. 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)
А:
Некоторые конфигурации проекта
- Я использую Custom Render (
SucursalRender extends DefaultClusterRenderer<Sucursal>
)
- Я загружаю значок маркера с помощью Glide, как я уже говорил:
Glide.with(context).load(id).fitCenter().placeholder(R.drawable.ic_no_image).into(simpleTarget);
simpleTarget
- это то, где я обрабатываю изображения, загруженные/кэшированные для Glide. Я размещаю весь код simpleTarget
, потому что там начинается авария:
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));
}
}
}
В последней строке кода бросается авария: markerToChange.setIcon(BitmapDescriptorFactory.fromBitmap(icon));
Что я пытался/нашел, пытаясь понять/исправить его
- Попробовал безошибочно воспроизвести ошибку на 4 реальных устройствах.
- Поиск в Интернете для подобных ошибок или кода о
gms.maps.model.Marker.setIcon
или com.google.maps.api.android.lib6
- Пытался понять запутанный код, указанный в Android Studio для
Marker.setIcon
Я предполагаю, что я могу обернуть код в try-catch block
для этого IllegalArgumentException: неуправляемый дескриптор, чтобы избежать закрытия приложения, потому что сбой, но это просто работа вокруг него.
обновление 2
Код 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
- это просто фасад для скольжения.
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);
}
Ответы
Ответ 1
При очистке карты с помощью
googleMap.clear();
**remove any reference to all the markers**
на карте.
У меня была проблема и выяснилось, что проблема связана с моим кодом, который я забыл удалить ссылкой на маркер и попытался изменить значок cleared Marker
Ответ 2
Я тоже получал такое же исключение, и установка молчащего исключения с try/catch не была бы решением, поскольку пользователь не смог увидеть текущее местоположение в моем случае:
java.lang.IllegalArgumentException: неуправляемый дескриптор на 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:361) в com.google.android.gms.maps.model.internal.zzf $zza $zza.zzL(Неизвестно Источник) на com.google.android.gms.maps.model.Marker.setIcon(Неизвестный источник)
Что я делал:
Скройте экран фрагмента карты, нажав кнопку "домой", а затем запустив приложение из панели запуска.
Что делал код:
Проверка маркера не равна null, а местоположение не имеет нулевого местоположения и значка.
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);
}
}
Исключение было: markerCurrentLocation.setIcon();
Как я избавился от этого исключения:
Я удалил следующую строку
if (markerCurrentLocation == null && googleMap != null)
Это означает, что я снова инициализирую маркер.
Если вы столкнулись с этой ошибкой, не ставьте setIcon() на старый маркер, а вместо этого надувайте новый маркер, а затем используйте setIcon().
Объяснение:
Я ЕСМЬ (не уверен) причина исключения была, если код пытается снова установить параметр Icon() на маркер, на котором он уже установлен, в конкретном экземпляре, например, в моем случае Карта возобновляется или может быть в вашем случае маркер выходит из видимая часть карты и входит или что-то подобное.
Конечно нет проблем с дескриптором, который мы получаем из метода BitmapDescriptorFactory.fromBitmap() или
BitmapDescriptorFactory.fromResource().
В качестве подсказок исключения дескриптор получил неуправляемый старый маркер, лучше использовать новый.
Ответ 3
Я обнаружил, что это происходит при доступе к маркеру после его удаления. Взаимодействие с маркером в обратном вызове - это как раз тот случай. Как упоминалось в Map API:
После удаления маркера поведение всех его методов не определено. https://developers.google.com/android/reference/com/google/android/gms/maps/model/Marker.html#remove()
Лучшим вариантом будет проверить, удален ли маркер с карты или нет.
Но у нас нет такого API. И я нашел другой обходной путь, мы можем использовать Marker setTag
и getTag
. При удалении маркера тег устанавливается в нуль:
Google Maps Android API не читает и не записывает это свойство, за исключением того, что при удалении маркера с карты это свойство имеет значение null. https://developers.google.com/android/reference/com/google/android/gms/maps/model/Marker.html#setTag(java.lang.Object)
При создании маркера используйте какой-нибудь тег для него.
При обновлении маркера проверка тега не равна нулю.
Это может помочь в вашем случае.
@Override
protected void onClusterItemRendered(Sucursal clusterItem, Marker marker) {
// we don't care about tag 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);
}
И в обратном вызове
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));
}
}
}
}
Ответ 4
Это исключение случается, когда ваш маркер был повторно открыт с помощью ClusterManager
. ClusterManager
воссоздает маркер при кластеризации.
Таким образом, чтобы избежать этого, вы должны получить маркер от рендера ClusterManeger
:
ClusterIconRender render = (ClusterIconRender) mClusterManager.getRenderer();
Marker trueMarker = render.getMarker(clusterMarker);
if (trueMarker != null) {
trueMarker.setIcon(...);
... // do whatever else your want with marker
}
В коде выше ClusterMarker
реализуется ClusterItem
и ClusterIconRender
extends DefaultClusterRenderer
.
Ответ 5
У меня такая же среда (maps-utils + custom renderer + Glide) и та же ошибка IllegalArgumentException: Unmanaged descriptor
.
Я решил ошибку, проверив, является ли маркер "действительным" перед установкой значка, используя методы DefaultClusterRenderer.getCluster(Marker)
и DefaultClusterRenderer.getClusterItem(Marker)
. Если оба возвращают null
, я ничего не делаю по методу onResourceReady(...)
.
В вашем случае я попробую следующее изменение на 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.: Я могу легко воспроизвести проблему на медленном устройстве и очистить кэш приложения перед тестированием (чтобы заставить Glide загружаться из сети). Затем я открываю карту и выполняю некоторое увеличение/уменьшение масштаба до загрузки маркеров.
Ответ 6
Убедитесь, что значок, который вы используете для маркера, не должен быть вектором, это должно быть .png изображение.
Ответ 7
Я исправил это, вызвав следующий метод.
clusterManager.clearItems()
После этого вы можете установить растровое изображение на маркеры.
Ответ 8
Маркер centerPoint
когда вы делаете это: centerPoint.remove();
А потом вы делаете это снова:
marker.setIcon(BitmapDescriptorFactory.fromBitmap(bitmap));
Выдает ошибку:
Неуправляемый дескриптор
Вам следует
centerPoint.remove();
centerPoint=null;