IllegalArgumentException:使用gms.maps.model.Marker.setIcon的非托管描述符

Mig*_*ieC 47 android google-maps android-maps-utils android-glide

我有一个使用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.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.g lide.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));
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

碰撞被抛出的代码最后一行:markerToChange.setIcon(BitmapDescriptorFactory.fromBitmap(icon));

我尝试/发现试图理解/修复它

  • 试图在4个真实设备中重现错误但没有成功.
  • 在网上搜索类似的错误或关于gms.maps.model.Marker.setIcon或的代码com.google.maps.api.android.lib6
  • 试图理解Android Studio中给出的混淆代码 Marker.setIcon

我想我可以将代码包装在一个try-catch blockfor 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);
        }
    }
Run Code Online (Sandbox Code Playgroud)

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);
}
Run Code Online (Sandbox Code Playgroud)

Mor*_*goo 16

用清除地图时

    googleMap.clear();
Run Code Online (Sandbox Code Playgroud)

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

  • 大提示!这不完全是我的情况,但确实指出了我的问题。重置设备或按下后退按钮事件后,地图重新加载,所有保存的地图标记均无效。我很好地重新创建了所有标记,但是我忘记了1个与阵列列表分开维护的引用。一旦我从旧的标记中重新创建了标记,就解决了我的错误。 (2认同)

Sou*_*rma 13

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

java.lang.IllegalArgumentException:com.google.maps.api.api.andi.lib6上com.google.maps.api.android.lib6.common.kb(:com.google.android.gms.DynamiteModulesB:162)中的非托管描述符com.google.maps.api.android.lib6.impl.db.a上的.impl.oc(:com.google.android.gms.DynamiteModulesB:75)(:com.google.android.gms.DynamiteModulesB:334) com.google.android.gms.maps.model.internal.q.onTransact(:com.google.android.gms.DynamiteModulesB:204)at com的android.os.Binder.transact(Binder.java:361). com.google.android.gms.maps.model.Marker.setIcon上的google.android.gms.maps.model.internal.zzf $ zza $ zza.zzL(未知来源)(未知来源)

我在做什么:

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

代码在做什么:

检查标记不为空,位置不是空设置位置和图标.

     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);
                }       
         }
Run Code Online (Sandbox Code Playgroud)

例外是:markerCurrentLocation.setIcon();

我如何摆脱这个例外:

我删除了以下行

 if (markerCurrentLocation == null && googleMap != null) 
Run Code Online (Sandbox Code Playgroud)

这意味着我再次初始化标记.如果遇到此错误,请尝试不要在旧标记上设置Icon(),而是膨胀新标记然后使用setIcon().

说明:

我ASSUME(不确定)异常原因是,如果代码在已经设置的标记上再次尝试setIcon(),在特殊情况下就像在我的情况下Map正在恢复或者可能在你的情况下标记出来的可见部分地图和进来或类似的东西.

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

  • 赏金即将结束,就像我之前说过的,你的答案是最接近问题的答案,所以我会给你赏金。但是考虑一下您的答案(此时此刻)只是为我提供了一条可以查找错误的路径或方向。要像接受的答案一样检查答案,它应该复制代码,显示错误发生的位置和方式以及如何修复它。 (2认同)

Sta*_*hin 12

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

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

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

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);
}
Run Code Online (Sandbox Code Playgroud)

并在回调中

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));
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


小智 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
}
Run Code Online (Sandbox Code Playgroud)

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

  • 这对我来说是正确的解决方案。我正在存储对标记的引用,但是在使用 ClusterItem 时,您应该存储对 ClusterItem 的引用,并通过它获取标记。 (3认同)

小智 0

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

  • 不幸的是它们都是.png (2认同)