Android 创建 BitmapDescriptor 异常

8

我正在编写一个与谷歌地图和上面的标记有关的应用程序。 我的任务是在谷歌地图上创建和显示一定数量的标记。标记具有自定义图像和文本。数据从服务器加载,我需要每次用户移动谷歌地图相机时显示新的数据量。因此,我使用 android-maps-utils:0.4.3 库来创建自定义位图(使用 IconGenerator),然后从中创建 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);
        }

除了应用程序偶尔会崩溃外,一切都正常(不是很频繁),且出现了两种不同的异常:

  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。如果用户频繁移动相机,每3秒钟就会有近20个BitmapDescriptors。我应该通过某种方式进行缓存吗?非常感谢您的答复和时间!

4个回答

12

这是我得到的信息。看起来如果BitmapFactory没有足够的内存,它就无法创建位图。因此,如果垃圾回收器未执行任务并且您没有足够的内存,则会出现此异常。在我的情况下,这种情况经常发生,因为每次用户移动Google地图相机时,我需要生成大约10-20个标记。

首先,不要像我一样愚蠢地仅为IconGenerator使用android-maps-utils :) 我编写了自己的类,从位图生成BitmapDescriptor并将其缓存在LruCache中。这里是缓存位图的好教程。您可以对BitmapDescriptor执行几乎相同的操作。请注意LruCache大小。您无法获取位图描述符的字节大小,因此需要考虑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);

抱歉我的英语不好,希望这个信息对某些人有用。


你有使用LruCacheBitmapDescriptor生成IconGenerator的例子吗?尤其是在处理位图大小时,你是如何解决sizeOf的问题的? - Dellkan
3
我现在没有我的IconGenerator的示例。但其中的LruCache几乎与谷歌关于缓存位图的教程中的相同(链接在我的回答中)。是的,sizeOf是一个问题。我只是计算了我的Bitmap的内存大小,并建议BitmapDescriptor的大小几乎相同。所以我计算了大小为100个BitMapDescriptors的LruCache最多占用1.6 MB内存。一切都很好。我完全没有遇到任何内存问题。 - Kuva

1
对我来说,问题与图标的大小有关。我只需动态更改位图的大小,就可以正常工作。
位图 image2 = Bitmap.createScaledBitmap(iconBitmap, 120, 120, false);

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中缓存新的类对象,而不是位图。 我认为Kuva中Bitmap和BitmapDescriptor的大小几乎相同。 这样做成功了。


为什么使用1014而不是1024?是打字错误吗? - Someone Somewhere

-2
使用Picasso、Glide或Fresco Literary来高效地缓存位图。
 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) {  }
          });

执行“BitmapDescriptorFactory.fromBitmap(位图)”需要一些时间。 我们应该缓存“BitmapDescriptor”。 - ipcjs

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接