Caching solution using the Cache Management Pattern and LruC


#1

I have a solution for the challenge which uses an implementation of the Cache Management Pattern
but I have altered the Cache Management Pattern in such a way that it is also a Decorator Pattern
I did this because I think it will result in a more elegant solution

Exhibit A: The Interface

public interface CacheProcess 
{
	public abstract Bitmap handleRequest(String imageUrl);
	
	public abstract Bitmap precache(String imageUrl);	
}

Exhibit B: The Cache Manager

public class CacheManager implements CacheProcess
{
private static final String TAG="CacheManager";
private static CacheManager cacheManager=new CacheManager();
private Cache cache;

	private CacheManager()
	{cache=new Cache();}
	
	//Make this a singleton
	public static CacheManager getInstance()
		{return cacheManager;}
	
	/**
	 * This is the Top layer of a Decorator pattern which
	 * also is similar to the cache management pattern
	 * It has 2 purposes
	 * 1) it gets a bitmaps for displaying 
	 * 2) it gets a bitmaps for pre-caching
	 * *****NOTE all caching is done by Cache
	 */
	
	public Bitmap handleRequest(String imageUrl)
	{
	Bitmap bitmap=cache.handleRequest(imageUrl);
	//return a valid or a null bitmap
	return bitmap;
	}
	
	public Bitmap precache(String imageUrl)
	{
	//Get the image from the cache or the downloader
	//Cache has already pre-cached this bitmap but it
	//is still passed back to verify that it is not NULL
	Bitmap bitmap= cache.precache(imageUrl);
	return bitmap;
	}

}

Exhibit C: The Cache

public class Cache implements CacheProcess
{
private LruCache mMemoryCache;
private Downloader downloader;

	//Initialize an implementation of the Android LruCache
	public Cache()
	{
	mMemoryCache= new LruCache<String, Bitmap>(35);
	downloader =new Downloader();
	}
	
	/**
	 * This is the Middle layer of a Decorator pattern which
	 * also is similar to the Cache Management pattern
	 * It has 3 purposes
	 * 1) it gets and returns bitmaps for displaying 
	 * 2) it gets and returns bitmaps for pre-caching
	 * 3) it caches every bitmap that it encounters for easy future retrieval
	 */

	@Override
	public Bitmap handleRequest(String imageUrl) 
	{
	//Get the image from the cache or the downloader
	Bitmap bitmap= imageRetriever(imageUrl);
	return bitmap;
	}
	
	public Bitmap precache(String imageUrl)
	{
	//Get the image from the cache or the downloader
	Bitmap bitmap= imageRetriever(imageUrl);
	return bitmap;
	}
	
	public Bitmap imageRetriever(String imageUrl)
	{
	//Get the image from the cache or the downloader
	Bitmap bitmap= (Bitmap) mMemoryCache.get(imageUrl);
		//Oh sorry we have none of those bitmaps in stock
		//Just a minute and ill download one for you
		if(bitmap==null)
		{
		bitmap=downloader.handleRequest(imageUrl);
			//Ill save a copy of this for your next visit
			if(bitmap != null)
				{storageRequest(imageUrl,bitmap);}
		}
	return bitmap;	
	}
	
	//Place the image into the cache
	private void storageRequest(String imageUrl, Bitmap bit)
	{
	mMemoryCache.put(imageUrl, bit);
	}

}

Exhibit D: The Downloader

public class Downloader implements CacheProcess
{
private static final String TAG="CacheManager.Downloader";

/**
 * This is the Bottom layer of a Decorator pattern which
 * also is similar to the cache management pattern
 * It has 2 purposes
 * 1) it downloads and returns bitmaps for displaying 
 * 2) it downloads and returns bitmaps for pre-caching
 */

	//Download the images
	public Bitmap handleRequest(String imageUrl)
	{
	Bitmap bitmap=imageDownloader(imageUrl);
	//return a valid or a null bitmap
	return bitmap;
	}
	
	//download this images for precaching
	public Bitmap precache(String imageUrl)
	{
	Bitmap bitmap=imageDownloader(imageUrl);
	//return a valid or a null bitmap
	return bitmap;
	}
	
	private Bitmap imageDownloader(String imageUrl)
	{
	Bitmap bitmap=null;
		try
		{
		//Get the bytes from flickrfetcher and turn it into a bitmap
		byte[] bitmapBytes=new FlickrFetchr().getUrlBytes(imageUrl);
		bitmap=BitmapFactory.decodeByteArray(bitmapBytes, 0, bitmapBytes.length);	
		}
		catch(IOException iOX)
		{Log.e(TAG, "Error downloading image", iOX);}	
	return bitmap;
	}

}

Exhibit E: Making The Cacher Run

private void handleRequest(final Handle handle)
	{
	Log.i(TAG, "Handeling request for bitmap");
		............
		//We now use the download manager to get the image
		//The download manager is based on the cache management pattern EXCEPT
		//that each member of the cache management pattern is wrapped inside the one another
		//So this pattern also is a decorator pattern 
		final Bitmap bitmap=cacheManager.handleRequest(imageUrl);
		Log.i(TAG, "Bitmap Created");
		mResponseHandler.post(new Runnable()
			{
					...........
			});
	}

Exhibit F: Making The Precache Run (part 1)

public class PhotoGalleryFragment extends Fragment 
{
       public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
		{
		View v=inflater.inflate(R.layout.fragment_photo_gallery,container, false);
		mGridView=(GridView)v.findViewById(R.id.photo_gallery_gridview);
		galleryAdapter=new GalleryItemAdapter(mItems);
			mGridView.setOnScrollListener(new OnScrollListener() 
			{
				@Override
				public void onScrollStateChanged(AbsListView view, int scrollState)
				{				
				lastVisibleScrollPosition= mGridView.getLastVisiblePosition();   
				firstVisibleScrollPosition= mGridView.getFirstVisiblePosition();
				//Now is the best time to pre-cache images
				galleryAdapter.precache(4);
				Log.d(TAG,String.format("Scroll state changed  showing entry %d of %d", lastVisibleScrollPosition, sizeOfScrollList));	
				}
                       }
                }

}

Exhibit G: Making The Precache Run (part 2)

private class GalleryItemAdapter extends ArrayAdapter<GalleryItem>
{			
..............
//We need to precache the images
				public void precache(int precacheSize)
				{
				int previousBuffer=firstVisibleScrollPosition-precacheSize;
				int nextBuffer=lastVisibleScrollPosition+precacheSize;
				Log.i(TAG, "------precache------");
					//We only precache when the scrolling has stopped
					Log.d(TAG,String.format("----precache--pre-- firstVisible=%d previousBuffer=%d >10?",firstVisibleScrollPosition, previousBuffer));
					//We must make sure that we are trying to cache legitimate images
					if(previousBuffer>10)
					{	
						//Here we precache the previous images that the user will see if they scroll upwards
						while(previousBuffer<firstVisibleScrollPosition)
						{
						GalleryItem item = getItem(previousBuffer);
						mThumbnailThread.precacheThumbnail(imageView, item.getUrl());
						previousBuffer++;
						Log.i(TAG, String.format("Pre caching %d < %d",previousBuffer, firstVisibleScrollPosition));
						}
					}
					Log.d(TAG,String.format("----precache--next-- lastVisibleScrollPosition=%d nextBuffer=%d >10?", lastVisibleScrollPosition, nextBuffer));
					//We must make sure that we are trying to cache legitimate images
					if(nextBuffer>lastVisibleScrollPosition)
					{	
					Log.d(TAG,String.format("----precache---Caching next while %d > %d && %d < %d",nextBuffer,lastVisibleScrollPosition,nextBuffer,mItems.size()));
						//Here we precache the next images that the user will see if they scroll down
					int bufferStartPoint=nextBuffer;
						while((nextBuffer>lastVisibleScrollPosition)&&(nextBuffer < mItems.size()))
						{	
							//Lets NOT pre-cache images multiple times
							if(nextBuffer<precachedUpTo)
							{
							Log.d(TAG,String.format("Loop Finished %d > %d",nextBuffer,precachedUpTo));
							break;
							}
						GalleryItem item = getItem(nextBuffer);
						mThumbnailThread.precacheThumbnail(imageView, item.getUrl());
						nextBuffer--;
						Log.i(TAG, String.format("Pre caching %d > %d",nextBuffer, lastVisibleScrollPosition));
						}
					int precachedUpTo =bufferStartPoint;
					}                               
}

#2

Falcobot, have you beaten the situation before the first scroll?
When the first screen of gridview have loaded but no scroll state changes were made.
I have similiar approach (precaching on scroll state changed) and this issue is pissing me off. All works fine after first scroll, but before it - there is no precaching =/