Google maps Не удалось выделить dup blob fd - PullRequest
0 голосов
/ 07 мая 2018

После многих попыток решить эту ошибку, я действительно из идей, что делать, я понимаю, из чего он исходит, из кеша не хватает памяти. Итак, после прочтения какого-то решения, лучшим из них, действительно улучшающим производительность и обеспечивающим выживание приложения более 20 секунд, было следующее:

https://stackoverflow.com/a/36903036/9753681

У меня более 30 тыс. Кластеров на карте с текстом значка, что каждый кластер является представлением. Так какой идентификатор я унаследовал IconGenerator и создать свой собственный кэш с LruCache, и по границам камеры я удаляю сам кластер из ClusterManger и само растровое изображение из кэша и сохраняю саму модель кластера только в другом кэше, чтобы обеспечить, если камера перемещается и кластер не находится в кэше точечные рисунки будут предоставлены кэшем модели удаления. Кроме того, когда камера останавливается, я рисую кластер. Но все равно он не работает, и все равно выдает ту же ошибку:

Could not allocate dup blob fd

Вот мой код:

CustomClusterMarker:

    class CustomClusterMarker(private var context: Context,
                          private var map: GoogleMap,
                          private var clusterManager: ClusterManager<ClusterMarkerBase>,
                          clusters: List<ClusterMarkerBase>) :
        DefaultClusterRenderer<ClusterMarkerBase>(context, map, clusterManager) {



    private val TAG: String = CustomClusterMarker::class.simpleName.toString()

    private var mIconGenerator: CachedIconGenerator
    private var mClusterIconGenerator: CachedIconGenerator
    private var bounds: LatLngBounds? = null
    private var isNotMove = true


    init {
        mDimension = context.resources.getDimension(R.dimen.custom_profile_image).toInt()
        mIconGenerator = CachedIconGenerator(context)
        mClusterIconGenerator = CachedIconGenerator(context)
        mIconGenerator.setBackground(ColorDrawable(Color.TRANSPARENT))
        mClusterIconGenerator.setBackground(ColorDrawable(Color.TRANSPARENT))

    }


    override fun onBeforeClusterItemRendered(item: ClusterMarkerBase?, markerOptions: MarkerOptions?) {
        //For single item

        if (item == null && markerOptions == null)
            return

//        // Prepare View
        val itemCluster = LayoutInflater.from(context).inflate(R.layout.view_marker, null)
        val imgItem = itemCluster.findViewById<ImageView>(R.id.image)
        val imgStatus = itemCluster.findViewById<ImageView>(R.id.status_icon)
        val tvCount = itemCluster.findViewById<TextView>(R.id.txt_counter)
        val txtDescription = itemCluster.findViewById<TextView>(R.id.description_marker)
        imgItem.setImageResource(item!!.mIcon)
        txtDescription.text = item.title

        if (tvCount.visibility == View.VISIBLE)
            tvCount.visibility = View.GONE

        if (imgStatus.visibility == View.GONE)
            imgStatus.visibility = View.VISIBLE

        if (item.isTech) {
            imgStatus.setImageResource(getIconOnCall(item.onCall, imgStatus))
        } else {
            imgStatus.setImageResource(getIconOpenCall(item.onCall, imgStatus))
        }


  //Start cleaning cache before set the icon

        if (bounds != null) {
            if (!bounds!!.contains(item.position)) {
                Completable.fromAction({
                    mIconGenerator.removeIcon(item.mId)
                    mIconGenerator.saveBeforeRemoveCluster(item)
                    clusterManager.removeItem(item)
                }).doOnError {
                    Log.e(TAG, "Show error single remove: " + it.toString())
                }
            }
        }


        if (bounds != null) {
            //Adding the cluster that were delete depends by the bounds
            mIconGenerator.getRemovesClusters(bounds!!)
                    .subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe({
                        clusterManager.addItems(it)
                    }) {
                        Log.e(TAG, "Show add single error: " + it.toString())
                    }
        }

        if (isNotMove) {
            //Convert view to Bitmap and set it on the map with the marker option
            try {
                mIconGenerator.setContentView(itemCluster)
                val descriptorFactory = BitmapDescriptorFactory
                        .fromBitmap(mIconGenerator.makeIcon(item.mId))
                markerOptions!!.icon(descriptorFactory)
            } catch (e: Exception) {
                Log.e(TAG, "Show single exeption: " + e.toString())
            }
        }
    }


    override fun onBeforeClusterRendered(cluster: Cluster<ClusterMarkerBase>?, markerOptions: MarkerOptions?) {

        //Check for cluster and mark option is Null
        //For group only!!!!
        if (cluster == null || markerOptions == null)
            return

//        // Inflate View
        val itemCluster = LayoutInflater.from(context).inflate(R.layout.view_marker, null)
        val tvCount = itemCluster.findViewById<TextView>(R.id.txt_counter)
        val imgItem = itemCluster.findViewById<ImageView>(R.id.image)
        val imgStatus = itemCluster.findViewById<ImageView>(R.id.status_icon)

        if (imgStatus.visibility == View.VISIBLE)
            imgStatus.visibility = View.GONE

        if (tvCount.visibility == View.GONE)
            tvCount.visibility = View.VISIBLE

        if (cluster.items.isNotEmpty())
            imgItem.setImageResource(cluster.items.first().mIcon)

        tvCount.text = (cluster.size - 1).toString()

        //Convert view to Bitmap and set it on the map with the marker option

        //Start cleaning cache before set the icon

        if (bounds != null) {
            if (!bounds!!.contains(cluster.position)) {
                Completable.fromAction({
                    mClusterIconGenerator.removeIcon(cluster.toString())
                    mClusterIconGenerator.saveBeforeRemoveCluster(cluster.items)
                    for (item in cluster.items) {
                        clusterManager.removeItem(item)
                    }
                }).doOnError {
                    Log.e(TAG, "Show group error remove: " + it.toString())
                }
            }
        }
        //Adding all clusters that were delete depends by the bounds
        if (bounds != null) {
            mClusterIconGenerator.getRemovesClusters(bounds!!)
                    .subscribeOn(Schedulers.io())
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe({
                        clusterManager.addItems(it)
                    }) {
                        Log.e(TAG, "Show group error add: " + it.toString())
                    }
        }

        if (isNotMove) {
            try {
                mClusterIconGenerator.setContentView(itemCluster)
                val descriptorFactory = BitmapDescriptorFactory
                        .fromBitmap(mClusterIconGenerator.makeIcon(cluster.toString()))
                markerOptions.icon(descriptorFactory)
            } catch (e: Exception) {
                Log.e(TAG,"Show gourp exeption: "+e.toString())
            }
        }
    }

    fun setBounds(bounds: LatLngBounds) {
        this.bounds = bounds
    }

    fun setOnCameraState(isNotMove: Boolean) {
        this.isNotMove = isNotMove
    }

    override fun shouldRenderAsCluster(cluster: Cluster<ClusterMarkerBase>?): Boolean {
        return super.shouldRenderAsCluster(cluster)
    }

    private fun getIconOnCall(isOnCall: Boolean, image: ImageView): Int {
        return if (isOnCall) {
            image.visibility = View.GONE
            0
        } else {
            image.visibility = View.VISIBLE
            R.drawable.free
        }
    }

    private fun getIconOpenCall(onCall: Boolean, image: ImageView): Int {
        return if (onCall) {
            image.visibility = View.VISIBLE
            R.drawable.open_call
        } else {
            image.visibility = View.GONE
            0
        }
    }

Мой собственный кеш:

CachedIconGenerator:

    class CachedIconGenerator(context: Context) : IconGenerator(context) {

    private val mBitmapsCache: LruCache<String, Bitmap>
    private val mModelClusterCache: LruCache<String, ClusterMarkerBase>
    private var bounds: LatLngBounds? = null

    private val TAG = CachedIconGenerator::class.simpleName

    init {

        val maxMemory = (Runtime.getRuntime().maxMemory() / 1024).toInt()

        // Use 1/8th of the available memory for this memory cache.
        val cacheSize = maxMemory / 8
        mBitmapsCache = object : LruCache<String, Bitmap>(cacheSize) {
            override fun sizeOf(key: String, bitmap: Bitmap): Int {
                // The cache size will be measured in kilobytes rather than
                // number of items.
                return bitmap.byteCount / 1024
            }
        }
//        val sizeModel = maxMemory
        mModelClusterCache = LruCache(cacheSize)

    }

    fun saveBeforeRemoveCluster(cluster: ClusterMarkerBase) {
        mModelClusterCache.put(cluster.mId, cluster)
    }

    fun saveBeforeRemoveCluster(clusters: Collection<ClusterMarkerBase>) {
        for (item in clusters) {
            mModelClusterCache.put(item.mId, item)
        }
    }

    fun setBounds(bounds: LatLngBounds) {
        this.bounds = bounds
    }

    fun removeIcon(id: String) {
        if (mBitmapsCache[id] != null)
            mBitmapsCache.remove(id)
    }

    fun isIconExsit(id: String): Boolean {
        return mBitmapsCache[id] != null
    }

    fun makeIcon(id: String): Bitmap? {
        return if (mBitmapsCache[id] == null) {
            val bitmap = super.makeIcon()
            mBitmapsCache.put(id, bitmap)
            bitmap
        } else
            mBitmapsCache[id]

    }

    fun getRemovesClusters(bounds: LatLngBounds): Observable<ArrayList<ClusterMarkerBase>> {
        val list = ArrayList<ClusterMarkerBase>()
        for (item in mModelClusterCache.snapshot().values) {
            if (bounds.contains(item.position))
                list.add(item)
        }
        return Observable.just(list)

    }

    fun isCacheEmtpty(): Boolean {
        return mBitmapsCache.size() == 0
    }

}
...