Подтвердить что ты не робот

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

Ответ 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;