private void DisplayFinished
        (
            AndroidCachingBitmapDrawable cachedDrawable
        )
        {
            _cacheLock.EnterWriteLock();

            try
            {
                if (_displayedCache.ContainsKey(cachedDrawable.CacheKey))
                {
                    // demote displayed entry to reuse pool

                    cachedDrawable.DisplayStarted  = null;
                    cachedDrawable.DisplayFinished = DisplayStarted;

                    _displayedCache.Remove(cachedDrawable.CacheKey);

                    _reusePool.Add(cachedDrawable.CacheKey,
                                   cachedDrawable);
                }
            }
            finally
            {
                _cacheLock.ExitWriteLock();
            }
        }
        private void ProcessRemoval
        (
            AndroidCachingBitmapDrawable bitmapDrawable,
            bool isEvicted
        )
        {
            if (_cacheLock.IsWriteLockHeld == false)
            {
                throw new SynchronizationLockException(nameof(_cacheLock));
            }

            // We only really care about evictions because we do direct Remove()als
            // all the time when promoting to the displayed cache. Only when the
            // entry has been evicted is it truly not longer being held by us.

            if (isEvicted)
            {
                UpdateByteUsage(bitmapDrawable.Bitmap,
                                //decrement: true,
                                eviction: true);

                bitmapDrawable.DisplayStarted  = null;
                bitmapDrawable.DisplayFinished = null;
                bitmapDrawable.IsCached        = false;
            }
        }
        public void AddBitmapDrawableToCache
        (
            string bitmapKey,
            AndroidCachingBitmapDrawable bitmapDrawable
        )
        {
            if ((bitmapDrawable == null) || (bitmapDrawable.Bitmap == null))
            {
                throw new ArgumentException("Attempt to add null value, refusing to cache.",
                                            nameof(bitmapDrawable));
            }

            _cacheLock.EnterWriteLock();

            try
            {
                if ((_displayedCache.ContainsKey(bitmapKey) == false) &&
                    (_reusePool.ContainsKey(bitmapKey) == false))
                {
                    _reusePool.Add(bitmapKey,
                                   bitmapDrawable);

                    bitmapDrawable.IsCached       = true;
                    bitmapDrawable.CacheKey       = bitmapKey;
                    bitmapDrawable.DisplayStarted = DisplayStarted;

                    UpdateByteUsage(bitmapDrawable.Bitmap,
                                    //decrement : false,
                                    eviction: false);
                }
            }
            finally
            {
                _cacheLock.ExitWriteLock();
            }
        }
        public AndroidCachingBitmapDrawable GetReusableBitmapDrawable
        (
            int bitmapWidth,
            int bitmapHeight
        )
        {
            // Only attempt to get a bitmap for reuse if the reuse cache is full.
            // This prevents us from prematurely depleting the pool and allows
            // more cache hits, as the most recently added entries will have a high
            // likelihood of being accessed again so we don't want to steal those bytes
            // too soon.

            _cacheLock.EnterWriteLock(); // because of _refillNeeded

            try
            {
                if (_reusePool.CacheSizeInBytes < _lowWatermark && _refillNeeded)
                {
                    // Reuse pool is not full, refusing reuse request:
                    //total_reuse_misses++;

                    return(null);
                }

                _refillNeeded = false;

                AndroidCachingBitmapDrawable reuseDrawable = null;

                if (_reusePool.Count > 0)
                {
                    foreach (string poolKey in _reusePool.Keys)
                    {
                        AndroidCachingBitmapDrawable poolDrawable = GetBitmapDrawableFromPool(poolKey,
                                                                                              updateLru: false);
                        // TODO: Implement check for KitKat and higher since
                        // bitmaps that are smaller than the requested size can be used.

                        if (Build.VERSION.SdkInt >= BuildVersionCodes.Kitkat)
                        {
                            // We can reuse a bitmap if the allocation to be reused is larger
                            // than the requested bitmap:

                            if ((poolDrawable.Bitmap.Width >= bitmapWidth) &&
                                (poolDrawable.Bitmap.Height >= bitmapHeight) &&
                                (poolDrawable.IsRetained == false))
                            {
                                reuseDrawable = poolDrawable;
                                break; // reuse hit, using larger allocation.
                            }
                        }
                        else if ((poolDrawable.Bitmap.Width == bitmapWidth) &&
                                 (poolDrawable.Bitmap.Height == bitmapHeight) &&
                                 (poolDrawable.Bitmap.IsMutable) &&
                                 (poolDrawable.IsRetained == false))
                        {
                            reuseDrawable = poolDrawable;
                            break; // reuse hit
                        }
                    }

                    if (reuseDrawable != null)
                    {
                        reuseDrawable.IsRetained = true;

                        UpdateByteUsage(reuseDrawable.Bitmap,
                                        //decrement : true,
                                        eviction: true);

                        // Cleanup the entry
                        reuseDrawable.DisplayStarted  = null;
                        reuseDrawable.DisplayFinished = null;
                        reuseDrawable.IsCached        = false;

                        _reusePool.Remove(reuseDrawable.CacheKey);

                        //total_reuse_hits++;
                    }
                }

                if (reuseDrawable == null)
                {
                    //total_reuse_misses++;

                    // Indicate that the pool may need to be refilled.
                    // There is little harm in setting this flag since it will be unset
                    // on the next reuse request if the threshold is
                    // _reusePool.CacheSizeInBytes >= _lowWatermark.

                    _refillNeeded = true;
                }

                return(reuseDrawable);
            }
            finally
            {
                _cacheLock.ExitWriteLock();
            }
        }