Android-maps-utils および マーカーアイコンのグライド を使用するアプリがあります。
Firebaseクラッシュレポートを使用してエラーレポートを取得しましたが、_gms.maps.model.Marker.setIcon
_はプライベートであるためソースコードで追跡できません。そのため、この問題に関するサポートを求めています。
質問のフォロー部分は次のように分かれています。
ユーザーが何をしていたか
地図でズームイン/ズームアウトしていました(_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)
そして:
一部のプロジェクト構成
SucursalRender extends DefaultClusterRenderer<Sucursal>
_)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));
私が試みた/理解しようとした/見つけた/修正したもの
gms.maps.model.Marker.setIcon
_または_com.google.maps.api.Android.lib6
_に関する同様のエラーまたはコードをWebで検索しましたMarker.setIcon
_]で指定された難読化されたコードを理解しようとしましたIllegalArgumentException:アンマネージディスクリプターのコードを_try-catch block
_でラップして、アプリケーションがクラッシュするのを回避することができますが、それは単に回避策です。
update 2DefaultClusterRenderer
のコード:
_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);
}
_
で地図をクリアするとき
googleMap.clear();
**remove any reference to all the markers**
マップ上。問題があり、マーカーへの参照を削除するのを忘れて、cleared Marker
のアイコンを変更しようとしたコードに問題があることがわかりました。
私も同じ例外を受け取っていましたが、私の場合、ユーザーが現在の場所を見ることができないため、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()メソッドから取得します。例外が示唆するように、記述子は古いマーカーでは管理されていないため、新しいマーカーを使用する方が適切です。
マーカーを削除した後にマーカーにアクセスすると、これが発生することがわかりました。コールバックでマーカーを操作するのはまさにその場合です。 MapのAPIで述べたように:
マーカーが削除された後、そのすべてのメソッドの動作は未定義です。 https://developers.google.com/Android/reference/com/google/Android/gms/maps/model/Marker.html#remove()
最適なオプションは、マーカーがマップから削除されているかどうかを確認することです。
しかし、そのようなAPIはありません。そして、別の回避策を見つけました。マーカーのsetTag
とgetTag
を使用できます。マーカーが削除されると、タグは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));
}
}
}
}
この例外は、マーカーが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
はDefaultClusterRenderer
を拡張します。
同じ環境(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にネットワークから強制的にロードさせるため)。次に、マップを開いて、マーカーが読み込まれる前にズームイン/ズームアウトを実行します。
マーカーに使用しているアイコンがベクターではなく、.pngイメージであることを確認してください。
次のメソッドを呼び出して修正しました。
clusterManager.clearItems()
その後、マーカーにビットマップを設定できます。