简体   繁体   中英

Android creating BitmapDescriptor exception

I'm writing an application that works alot with google map and markers on it. My task is to create and display some amount of markers on google map. Markers have custom image and text in it. Data is loading from server and i need to display new amount of data every time user moves google map camera. So i'm using android-maps-utils:0.4.3 library for creating custom Bitmap (using IconGenerator) and then create BitmapDescriptor from it. Here is part of the code:

  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);
        }

Everything is ok except application is crashes sometimes (not very often) with 2 different exceptions:

  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)

and sometimes

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)

What can i do with this? I guess i'm using my memory to much to create BitmapDescriptors. It's nearly 20 BitmapDescriptos every 3 seconds if user moving camera too much. Should i cache it somehow? Thx a lot for your answers and time!

Well here is what i got. Looks like BitmapFactory can't create Bitmap if it don't have enought memory. So if GC didn't do job and u don't have enough memory u'll get this exception. In my case that was pretty often because i need to generate about 10-20 markers every time user moves google map camera.

First of all don't be stupid like me and don't use android-maps-utils just for IconGenerator :) I wrote my own class that generate's BitmapDescriptor from Bitmap and caches it in LruCache. Here's good tutorial for caching Bitmaps. You can do almost the same for BitmapDescriptor. Pay attention to LruCache size. You can't get BitmapDescriptor size in bytes, so you need to think about amount of these objects in LruCache. Just look at your bitmap size and do some calculations.

If you need text in your image do something like this:

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

Sorry for bad english and i hope this information will be useful to some one.

I had same problem. With Kuva's answer I make a new class like this:

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;
    }
}

I cache new class object in LruCache instead of Bitmap. Same with Kuva I think Bitmap and BitmapDescriptor almost same size. And It worked

For me the problem was related to the size of the icon. I just change dynamically the size of the Bitmap and works fine.

Bitmap image2 = Bitmap.createScaledBitmap(iconBitmap, 120, 120, false);

Use Picasso , Glide or Fresco Literary to cache bitmaps efficiently.

 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) {  }
          });

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM