web-dev-qa-db-ja.com

IllegalArgumentException:gms.maps.model.Marker.setIconを使用したアンマネージ記述子

Android-maps-utils および マーカーアイコンのグライド を使用するアプリがあります。
Firebaseクラッシュレポートを使用してエラーレポートを取得しましたが、_gms.maps.model.Marker.setIcon_はプライベートであるためソースコードで追跡できません。そのため、この問題に関するサポートを求めています。
質問のフォロー部分は次のように分かれています。

  • ユーザーが何をしていたか
  • Firebaseのクラッシュが私に報告したこと
  • いくつかのプロジェクト構成
  • 私がそれを理解/修正しようとして/発見したこと

ユーザーが何をしていたか
地図でズームイン/ズームアウトしていました(_com.google.Android.gms.maps.SupportMapFragment_を使用するFragment

Firebaseのクラッシュが私に報告したもの

例外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)
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)

そして:

enter image description here

一部のプロジェクト構成

  • カスタムレンダリングを使用しています(_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));
        }
    }
}
_

crashは、コードの最後の行でスローされています:markerToChange.setIcon(BitmapDescriptorFactory.fromBitmap(icon));

私が試みた/理解しようとした/見つけた/修正したもの

  • 成功せずに4つの実際のデバイスでエラーを再現しようとしました。
  • _gms.maps.model.Marker.setIcon_または_com.google.maps.api.Android.lib6_に関する同様のエラーまたはコードをWebで検索しました
  • Android Studio for _Marker.setIcon_]で指定された難読化されたコードを理解しようとしました

IllegalArgumentException:アンマネージディスクリプターのコードを_try-catch block_でラップして、アプリケーションがクラッシュするのを回避することができますが、それは単に回避策です。

update 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);
}
_
43
MiguelHincapieC

で地図をクリアするとき

    googleMap.clear();

**remove any reference to all the markers**マップ上。問題があり、マーカーへの参照を削除するのを忘れて、cleared Markerのアイコンを変更しようとしたコードに問題があることがわかりました。

14
Morteza Rastgoo

私も同じ例外を受け取っていましたが、私の場合、ユーザーが現在の場所を見ることができないため、try/catchでサイレント例外を設定しても解決しませんでした:

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)at 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)at 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)

私がやっていたこと:

ホームボタンを押してからランチャーからアプリを起動して、マップフラグメント画面を最小化します。

コードが実行していたこと:

マーカーがnullではなく、場所が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()を使用してください。

説明:

例外の理由は、コードが既に設定されているマーカーでsetIcon()を再度試行しようとした場合、たとえば私の場合のようにマップが再開した場合、またはマーカーがマップして入ってくるか、似たようなものです。

確かに記述子に問題はありません。BitmapDescriptorFactory.fromBitmap()メソッドまたはBitmapDescriptorFactory.fromResource()メソッドから取得します。例外が示唆するように、記述子は古いマーカーでは管理されていないため、新しいマーカーを使用する方が適切です。

13
Sourab Sharma

マーカーを削除した後にマーカーにアクセスすると、これが発生することがわかりました。コールバックでマーカーを操作するのはまさにその場合です。 MapのAPIで述べたように:

マーカーが削除された後、そのすべてのメソッドの動作は未定義です。 https://developers.google.com/Android/reference/com/google/Android/gms/maps/model/Marker.html#remove()

最適なオプションは、マーカーがマップから削除されているかどうかを確認することです。
しかし、そのようなAPIはありません。そして、別の回避策を見つけました。マーカーの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)

マーカーを作成するときは、何らかのタグを使用します。
マーカーチェックタグを更新するときはnullではありません。

これはあなたの場合に役立ちます。

@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));
            }
        }
    }
}
11
Stas Parshin

この例外は、マーカーが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
}

上記のコードでは、ClusterMarkerClusterItemを実装し、ClusterIconRenderDefaultClusterRendererを拡張します。

3
Nikita Levanov

同じ環境(maps-utils +カスタムレンダラー+ 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にネットワークから強制的にロードさせるため)。次に、マップを開いて、マーカーが読み込まれる前にズームイン/ズームアウトを実行します。

2
user2595794

マーカーに使用しているアイコンがベクターではなく、.pngイメージであることを確認してください。

0
Harmantj

次のメソッドを呼び出して修正しました。

clusterManager.clearItems()

その後、マーカーにビットマップを設定できます。

0
user3813078