66

我有一个使用android-maps-utilsglide 标记图标的应用程序。
我收到了使用 Firebase 崩溃报告的错误报告,因为它是私有的,所以我无法在源代码中跟踪它gms.maps.model.Marker.setIcon,所以我正在寻求解决这个问题的一些帮助。
问题的以下部分分为:

  • 用户在做什么
  • 什么firebase崩溃报告给我
  • 一些项目配置
  • 我尝试/发现试图理解/修复它

用户在做什么
他正在放大和缩小地图(Fragment使用com.google.android.gms.maps.SupportMapFragment

什么firebase崩溃报告给我

异常 java.lang.IllegalArgumentException:非托管描述符
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)

和:

在此处输入图像描述

一些项目配置

  • 我正在使用自定义渲染 ( SucursalRender extends DefaultClusterRenderer<Sucursal>)
  • 我正在用 Glide 下载 Marker 图标,就像我之前说的: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 blockIllegalArgumentException: Unmanaged descriptor 中,以避免应用程序因为崩溃而关闭,但这只是一种解决方法。

更新 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只是 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);
}
4

10 回答 10

26

清除地图时

    googleMap.clear();

**remove any reference to all the markers**在地图上。我遇到了问题并发现问题出在我的代码上,我忘记删除对标记的引用并试图更改cleared Marker

于 2017-11-01T15:07:15.323 回答
19

我在删除标记后访问标记时发现了这种情况。在回调中与标记交互就是这种情况。如 Map 的 API 中所述:

删除标记后,其所有方法的行为都未定义。 https://developers.google.com/android/reference/com/google/android/gms/maps/model/Marker.html#remove()

最好的选择是检查是否从地图中删除了标记。
但是我们没有这样的 API。我找到了另一种解决方法,我们可以使用 MarkersetTaggetTag. 当标记被删除时,标记设置为空:

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

在回调中

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));
            }
        }
    }
}
于 2017-03-23T12:04:57.707 回答
14

我也遇到了同样的异常,并且使用 try/catch 设置静默异常不是解决方案,因为在我的情况下用户无法看到当前位置:

java.lang.IllegalArgumentException:com.google.maps.api.android.lib6 的 com.google.maps.api.android.lib6.common.kb(:com.google.android.gms.DynamiteModulesB:162) 的非托管描述符.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:361) 在 com。 google.android.gms.maps.model.internal.zzf$zza$zza.zzL(Unknown Source) at com.google.android.gms.maps.model.Marker.setIcon(Unknown Source)

我在做什么:

通过按主页按钮然后从启动器启动应用程序来最小化地图片段屏幕。

代码在做什么:

检查标记不为空且位置不为空设置位置和图标。

     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()。

解释:

我假设(不确定)异常原因是代码是否尝试在已设置的标记上再次 setIcon() ,在特定情况下,例如在我的情况下 Map 正在恢复,或者在您的情况下标记超出可见部分地图和进来或类似的东西。

确保我们从方法 BitmapDescriptorFactory.fromBitmap() 或 BitmapDescriptorFactory.fromResource() 获得的描述符没有问题。作为异常提示,描述符在旧标记上不受管理,最好使用新标记。

于 2017-02-10T11:51:06.213 回答
5

当您的标记被 重新聚集时,会发生此异常ClusterManagerClusterManager在聚类上重新创建标记。因此,为避免这种情况,您必须从以下渲染中获取标记ClusterManeger

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

在上面的代码中ClusterMarker实现ClusterItemClusterIconRender扩展DefaultClusterRenderer

于 2017-05-12T12:43:37.130 回答
3

我有相同的环境(maps-utils + custom renderer + Glide)和相同的错误IllegalArgumentException: Unmanaged descriptor

我通过在设置图标之前检查标记是否“有效”,使用方法DefaultClusterRenderer.getCluster(Marker)DefaultClusterRenderer.getClusterItem(Marker). 如果两者都 return 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 从网络加载)。然后我打开地图并在任何标记加载之前执行一些放大/缩小。

于 2017-02-13T14:29:30.323 回答
1

添加集群后有同样的异常。我通过调用修复它:

clusterManager.clearItems()

而不是调用我以前使用的方法,没有集群:

googleMap.clear();
于 2020-10-24T11:19:59.743 回答
0

标记中心点

当你这样做时:centerPoint.remove();

然后你再做一次:</p>

marker.setIcon(BitmapDescriptorFactory.fromBitmap(bitmap));

会报错:</p>

非托管描述符

你应该 centerPoint.remove(); centerPoint=null;

于 2019-09-29T07:45:48.277 回答
0

确保您用于标记的图标不应该是矢量,它应该是 .png 图像。

于 2017-02-09T07:12:14.650 回答
0

我通过调用以下方法修复了它。

clusterManager.clearItems()

之后,您可以将位图设置为标记。

于 2019-05-24T07:07:54.570 回答
0

尝试

mClusterManager.markerCollection.clear();
mMap.clear()
于 2020-08-28T23:50:58.950 回答