2017-01-27 5 views
29

У меня есть приложение, которое использует android-maps-utils и glide for marker icons.
У меня есть отчет об ошибке с использованием отчетов о сбоях Firebase, которые я не могу отслеживать в исходном коде, потому что gms.maps.model.Marker.setIcon является закрытым, поэтому я прошу о помощи в решении этой проблемы.
Последующая часть вопроса делится на:
IllegalArgumentException: Неуправляемый дескриптор с использованием gms.maps.model.Marker.setIcon

  • То, что пользователь делает
  • Что firebase аварии сообщила мне
  • Некоторых конфиги проекта
  • То, что я пробовал/нашел, пытаясь понять,/исправить

То, что пользователь делает
Он был увеличение и уменьшение масштаба в карте (Fragment, который использует com.google.android.gms.maps.SupportMapFragment)

Что firebase аварии сообщила мне

Exception java.lang.IllegalArgumentException: неуправляемый дескриптору
ком. 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 (Activi tyThread.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

Некоторые проектные конфиги

  • Я использую Выборочная Render (SucursalRender extends DefaultClusterRenderer<Sucursal>)
  • I ' м загрузки значок маркера с 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 просто фасад для 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); 
} 
+0

Я не думаю, что в этом проблема - но весь 'markerToChange' вещь выглядит плохой. Почему бы не передать маркер в конструктор 'CustomSimpleTarget'? Таким образом, обратный вызов имеет (слабую?) Ссылку на маркер, и вам не нужно обходить все маркеры и не догадываться, что это правильный маркер. –

+0

Привет @YoniGross 'CustomSimpleTarget' является асинхронным процессом (когда Glide загружает изображение). Когда изображение загружается, вы не знаете состояния маркеров в этот момент, и маркер, который вы могли бы передать в конструктор, может оставаться в памяти, но не отображаться на карте (потому что он синхронизируется с новыми маркерами на карте , посмотрите, как обновлялись маркеры при увеличении/удалении в android-maps-utils, и вы поймете, о чем я говорю). – MiguelHincapieC

+0

Кроме того, я предполагаю, что вы не получите такой маркер, я имею в виду, в этом методе, прежде чем пытаться «setIcon()». Я уверен, что у меня есть маркер ... предположим, это тот, который вы пытаетесь получить в конструктор ... проблема все еще остается в следующей строке кода 'markerToChange.setIcon' – MiguelHincapieC

ответ

0

Убедитесь, что значок, который вы используете для маркера, не должен быть вектором, это должно быть .png изображение.

+2

К сожалению, все они .png – MiguelHincapieC

8

я тоже получаю же исключение и установка бесшумного исключения с TRY/уловом не было бы решением, поскольку пользователь не может видеть текущее местоположение в моем случае:

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 : 361) at com.google.android.gms.maps.model.internal.zzf $ zza $ zza.zzL (Неизвестно Источник) по адресу com.google.android.gms.maps.model.Marker.setIcon (Неизвестно Источник)

Что я делал:

Минимизация карту фрагмента экрана, нажав кнопку домой, а затем, начиная приложение от запуска.

Какой код делает:

Проверка является маркером не является нулевым и расположение не нулевое положение и значок.

 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(). В качестве подсказок исключений дескриптор получил неуправляемый старый маркер, лучше использовать новый.

+0

Понятно, что это ближайший ответ на мою проблему. Вы получаете ту же ошибку по другой причине, но она дала мне представление о том, что может быть проблемой. Трудно, что у меня нет устройств, где происходит ошибка (это отчет об аварийном сбое для приложения в магазине). Итак, что я собираюсь попробовать, что вы и @JadavLalit сказали, опубликовать новую версию, а затем ждать, если я буду получать отчет об ошибке ... или нет. – MiguelHincapieC

+1

Бунти скоро закончится, и, как я уже говорил, ваш ответ является самым близким к проблеме, поэтому я дам вам щедрость. Но учтите, что ваш ответ (в этот момент) просто дает мне путь или направление, где я могу искать ошибку. Чтобы проверить ответ как принятый ответ, он должен повторить код, показать, где и как произошла ошибка, и как его исправить. – MiguelHincapieC

1

У меня такая же среда (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 к загрузка из сети). Затем я открываю карту и выполняю несколько операций увеличения/уменьшения изображения перед загрузкой маркеров.

5

Это было обнаружено при доступе к маркеру после его удаления. Взаимодействие с маркером в обратном вызове - это именно тот случай.Как упоминалось в API карты:

После удаления маркера поведение всех его методов не определено. https://developers.google.com/android/reference/com/google/android/gms/maps/model/Marker.html#remove()

Лучший вариант будет проверять это маркер удаляется с карты или нет.
Но у нас нет такого API. И я нашел другое обходное решение, мы можем использовать Marker's setTag и getTag. Тег установлен на нуль, когда маркер удален:

API 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)); 
      } 
     } 
    } 
} 
+0

Я согласен с вашим анализом, хотя я нашел добавление обертки вокруг маркера, чтобы управлять этим «был ли он удален», а другие данные были полезны, просто потому, что API говорит: «После удаления маркера поведение всех его методов не определено ». который в теории включал бы «getTag», но, очевидно, в реализации «getTag», очевидно, все еще работает. – Andy

+0

Продолжая, API-документы API (по вашей ссылке) больше не указывают, что он устанавливает тег в null при удалении, но советует клиенту вызывать setTag (null) (что противоречит оператору «поведение всех других методов неопределенным», но все же кажется function – Andy

0

Это исключение происходит, когда ваш маркер reclustered по 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.

+0

Это было правильное решение для меня. Я хранили ссылку на маркер, но при работе с ClusterItem вы должны вместо этого хранить ссылку на ClusterItem и получать через нее маркер. – Benjiko99

4

При очистке карты с

googleMap.clear(); 

**remove any reference to all the markers** на карте. У меня была проблема и выяснилось, что проблема связана с моим кодом, который я забыл удалить ссылку на маркер и попытался изменить значок cleared Marker

+1

Отличный совет! Это было не совсем мое дело, но это указывало на мою проблему. После того, как событие сброса устройства или назад нажало кнопку, перезагрузка карты и все сохраненные маркеры карты стали недействительными. Я отлично воссоздавал все маркеры, но я забыл одну ссылку, которую я поддерживал отдельно от своего списка массивов. Как только я воссоздал бродячий маркер из старого, это исправило мою ошибку. – ByteSlinger