3

Я пишу приложение, которое работает с картами google и маркерами. Моя задача - создать и отобразить некоторое количество маркеров на карте Google. Маркеры имеют пользовательский образ и текст. Данные загружаются с сервера, и мне нужно отображать новый объем данных каждый раз, когда пользователь перемещает карту Google Map. Поэтому я использую android-maps-utils: 0.4.3 библиотеку для создания пользовательского Bitmap (используя IconGenerator), а затем создаю из него BitmapDescriptor. Вот часть кода:Android, создающий исключение BitmapDescriptor

googleMap.clear() 

     LayoutInflater layoutInflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); 

     IconGenerator clusterIconGenerator = new IconGenerator(getActivity()); 
     View clusterView = layoutInflater.inflate(R.layout.marker_cluster_view, null, false); 
     clusterIconGenerator.setContentView(clusterView); 
     clusterIconGenerator.setBackground(ContextCompat.getDrawable(getActivity(), R.drawable.mark_normal_grey)); 

     List<Cluster> clusters = result.getResult(); // server data 

     for (Cluster cluster : clusters) { 
      Bitmap bitmap = clusterIconGenerator.makeIcon(String.valueOf(cluster.getOffersCount())); 

      Marker marker = googleMap.addMarker(new MarkerOptions() 
        .position(new LatLng(cluster.getLocation().getLatitude(), cluster.getLocation().getLongitude())) 
        .icon(BitmapDescriptorFactory.fromBitmap(bitmap)) // crash here 
        .anchor(0.5f, 0.5f)); 

      markerClusterMap.put(marker, cluster); 
     } 

Все в порядке, за исключением приложения аварии иногда (не очень часто) с 2-мя разными исключениями:

java.lang.RuntimeException: Could not copy bitmap to parcel blob. 
                    at android.graphics.Bitmap.nativeWriteToParcel(Native Method) 
                    at android.graphics.Bitmap.writeToParcel(Bitmap.java:1541) 
                    at com.google.android.gms.maps.model.a.c.a(:com.google.android.gms:237) 
                    at com.google.android.gms.maps.internal.h.a(:com.google.android.gms:227) 
                    at com.google.android.gms.maps.internal.j.a(:com.google.android.gms:183) 
                    at com.google.android.gms.maps.internal.CreatorImpl.a(:com.google.android.gms:32) 
                    at com.google.android.gms.maps.internal.b.a(:com.google.android.gms:227) 
                    at com.google.android.gms.maps.model.a.b.onTransact(:com.google.android.gms:106) 
                    at android.os.Binder.transact(Binder.java:387) 
                    at com.google.android.gms.maps.model.internal.zza$zza$zza.zzc(Unknown Source) 
                    at com.google.android.gms.maps.model.BitmapDescriptorFactory.fromBitmap(Unknown Source) 
                    at com.cheapsta.cheapsta.fragments.GoogleMapFragment.clustersLoadingFinished(GoogleMapFragment.java:187) 

и иногда

java.lang.RuntimeException: Could not allocate dup blob fd. 
                     at android.graphics.Bitmap.nativeCreateFromParcel(Native Method) 
                     at android.graphics.Bitmap.-wrap0(Bitmap.java) 
                     at android.graphics.Bitmap$1.createFromParcel(Bitmap.java:1516) 
                     at android.graphics.Bitmap$1.createFromParcel(Bitmap.java:1515) 
                     at maps.bx.a$a.onTransact(:com.google.android.gms.alldynamite:101) 
                     at android.os.Binder.transact(Binder.java:387) 
                     at com.google.android.gms.maps.model.a.c.a(:com.google.android.gms:242) 
                     at com.google.android.gms.maps.internal.h.a(:com.google.android.gms:227) 
                     at com.google.android.gms.maps.internal.j.a(:com.google.android.gms:183) 
                     at com.google.android.gms.maps.internal.CreatorImpl.a(:com.google.android.gms:32) 
                     at com.google.android.gms.maps.internal.b.a(:com.google.android.gms:227) 
                     at com.google.android.gms.maps.model.a.b.onTransact(:com.google.android.gms:106) 
                     at android.os.Binder.transact(Binder.java:387) 
                     at com.google.android.gms.maps.model.internal.zza$zza$zza.zzc(Unknown Source) 
                     at com.google.android.gms.maps.model.BitmapDescriptorFactory.fromBitmap(Unknown Source) 
                     at com.cheapsta.cheapsta.fragments.GoogleMapFragment.clustersLoadingFinished(GoogleMapFragment.java:187) 

Что может я делаю с этим? Думаю, я использую свою память для создания BitmapDescriptors. Это почти 20 BitmapDescriptos каждые 3 секунды, если пользователь слишком сильно перемещает камеру. Должен ли я кэшировать его каким-то образом? Thx много для ваших ответов и времени!

ответ

4

Хорошо, вот что я получил. Похоже, что BitmapFactory не может создать Bitmap, если у него недостаточно памяти. Так что если GC не выполнял работу, и у вас недостаточно памяти, вы получите это исключение. В моем случае это было довольно часто, потому что мне нужно генерировать около 10-20 маркеров каждый раз, когда пользователь перемещает карту Google Map.

Прежде всего, не будь глупым, как я, и не используйте андроид-карты-утилиты только для IconGenerator :) Я написал свой собственный класс, который генерирует BitmapDescriptor из Bitmap и кэширует его в LruCache. Here's good tutorial для кэширования растровых изображений. Вы можете сделать почти то же самое для BitmapDescriptor. Обратите внимание на размер LruCache. Вы не можете получить размер BitmapDescriptor в байтах, поэтому вам нужно подумать о количестве этих объектов в LruCache. Просто посмотрите на размер растрового изображения и выполните некоторые вычисления.

Если вам нужен текст в изображении сделать что-то вроде этого:

Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.mark_active_grey).copy(Bitmap.Config.ARGB_8888, true); 

    Canvas canvas = new Canvas(bitmap); 
    canvas.drawText(offersCount, 
      canvas.getWidth()/2, 
      canvas.getHeight()/2 - ((clustersPaint.getFontMetrics().ascent + clustersPaint.getFontMetrics().descent)/2) , 
      clustersPaint); 

Извините за плохой английский и я надеюсь, что эта информация будет полезна кому-то одному.

+0

У вас есть пример 'IconGenerator' с' 'LruCache' для BitmapDescriptor', и особенно то, как вы решили' sizeOf' относительно размера растрового изображения? – Dellkan

+2

У меня нет примера моего IconGenerator прямо сейчас. Но LruCache в нем почти такой же, как в учебнике Google о кешировании растровых изображений (ссылка в моем anser). И да, размерОф - проблема. Я просто вычислил размер памяти моего растрового изображения и предположил, что BitmapDescriptor будет почти того же размера. Таким образом, я вычислил, что LruCach с размером = 100 битМадДескрипторы будут иметь максимальную максимальную память 1,6 мб. И все было в порядке. У меня вообще не было проблем с памятью. – Kuva

1

У меня была такая же проблема. С ответом Kuva, я сделать новый класс, как это:

public class MapBmpContainter 
{ 
    private int mBmpSize; 
    public BitmapDescriptor mBmpDescriptor; 

    public MapBmpContainter(Bitmap bmp) 
    { 
     mBmpSize=bmp.getByteCount()/1014; 
     mBmpDescriptor= BitmapDescriptorFactory.fromBitmap(bmp); 
    } 

    public int getSize() 
    { 
     return mBmpSize; 
    } 
} 

Я кэшировать новый объект класса в LruCache вместо Bitmap. То же самое с Кувой Я думаю, что битмап и битмапДескриптор почти такого же размера. И это сработало

+0

почему 1014 вместо 1024? опечатка? –

-1

Использование Picasso, Glide или Fresco Литературно для точного кэширования растровых изображений.

Picasso.with(getContext()) 
    .load(R.drawable.marker) 
    .resize(width, width) 
    .into(new Target() { 
    @Override 
    public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) { 
    markerOptionsHome = new MarkerOptions(); 
    markerOptionsHome.title("Home location"); 
    markerOptionsHome.snippet(""); 
    markerOptionsHome.position(latlng); 
    markerOptionsHome.icon(BitmapDescriptorFactory.fromBitmap(bitmap)); 
    homeLocationMarker = map.addMarker(markerOptionsHome); 

      } 

    @Override 
    public void onBitmapFailed(Drawable errorDrawable) { } 
    @Override 
    public void onPrepareLoad(Drawable placeHolderDrawable) { } 
      });