MY Solution to Cache problem


#1

NOTE: IM NOT ABOVE FLAWS AND CORRECTION please tell me what you feel.
steps:

  1. I created a cache manager which extends a HandlerThread that has a singleton of a cache which performs basic cache operations.

  2. I instantiated and passed the looper of the PhotogalleryFragment so that it caches as thumbnailDownloader tries to download image.

  3. i ensures that for every item greater than the 10th item are preloaded and postpreloaded(ie. prevoius 10 and next 10 are preloaded). except for the items <10 and the items10 less than the size of the listadapter(foor this cases. they are postpreloaded).

see the code below.

thumbnail downloader

package com.example.photogallery;

import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

import android.annotation.SuppressLint;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.os.Process;
import android.util.Log;
import android.widget.ImageView;

import com.example.photogallery.LRUCacheManager.PreloadListener;

public class ThumbnailDownloader extends HandlerThread{
private static final String TAG=“ThumbnailDownloader”;
private static final int MESSAGE_DOWNLOAD=0;
private Handler mHandler;
private Handler mResponseHandler;
private LRUCacheManager mLRUCache;
private static final int mFourMB=410241024;
Listener mListenerTokens;
private Map<Token,String> mRequestMap= Collections.synchronizedMap(new HashMap<Token, String>());

public interface Listener<Token>{
	void onThumbNailDownloaded(Token token,Bitmap bitmap);
	 
}

@SuppressLint("HandlerLeak")
@Override
protected void onLooperPrepared() {
	mHandler= new Handler(){
		@SuppressWarnings("unchecked")
		@Override
		public void handleMessage(Message msg) {
			 if(msg.what== MESSAGE_DOWNLOAD){
				 Token token=(Token)msg.obj;
				 Log.i(TAG, "Got a request for url: " + mRequestMap.get(token));
				 handleRequest(token);
			 }
		}
	};
	
}

 
public ThumbnailDownloader(Handler responseHandler,int size, LRUCacheManager<ImageView> lRUCache) {
	super(TAG);
	mResponseHandler=responseHandler;
	mLRUCache=(LRUCacheManager<Token>) lRUCache;
	
}



private void handleRequest(final Token token){
	
	final String  requestUrl=mRequestMap.get(token);
	if(requestUrl==null) return;
	byte[] imageByte;
	try {
		imageByte = new FlickrFetcher().getUrlBytes(requestUrl);
		final Bitmap bitmap=BitmapFactory.decodeByteArray(imageByte, 0, imageByte.length);
		
		mResponseHandler.post(new Runnable() {
			
			@Override
			public void run() {
				 if(mRequestMap.get(token)!=requestUrl) return;//ensure that the imageView matches with the url
				 mRequestMap.remove(token);
				  
				 if(mLRUCache.getImageFromCache(token)!=null){//if there is image in the cache then save display it otherwise, put one
					Log.d("CACHED IMAGES", "Image gotten from cache");
					 mListenerTokens.onThumbNailDownloaded(token, mLRUCache.getImageFromCache(token));
				 } 
				 else{
					 mLRUCache.putImageInCache(token, bitmap);
					 mListenerTokens.onThumbNailDownloaded(token, bitmap);
				 }
				 
				 
				
			}
		});
		Log.i(TAG, "Bitmap created");
	} catch (IOException e) {
		Log.e(TAG, "Error downloading image", e);
		e.printStackTrace();
	}
	
}

public void clearQueue(){
	//clear queue incase user rotate device
	mHandler.removeMessages(MESSAGE_DOWNLOAD);
	mLRUCache.clearQueue();
	mRequestMap.clear();
}
public void queueMessageThumbNail(Token token,String url, int sizeofGallery){
	Log.i(TAG, "Got an URL: " + url);
	mRequestMap.put(token, url);
 
	mLRUCache.setUrl(token,url,sizeofGallery);
	
	 
		
	
	
	Log.d("LOOPER","my preload looper is set");
	 
	Message messge=mHandler.obtainMessage(MESSAGE_DOWNLOAD,token);
	messge.sendToTarget();
	
}

public Listener<Token> getListenerTokens() {
	return mListenerTokens;
}

public void setListenerTokens(Listener<Token> listenerTokens) {
	mListenerTokens = listenerTokens;
}

}

myLRUCacheManager

package com.example.photogallery;

import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import android.annotation.SuppressLint;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
import android.os.Handler.Callback;
import android.os.HandlerThread;
import android.os.Message;
import android.os.StrictMode;
import android.support.v4.util.LruCache;
import android.util.Log;

@SuppressLint(“NewApi”)
public class LRUCacheManager extends HandlerThread{
private static final String LRU_TAG=“LRUTAG”;
private static final int DOWNLOAD=1;
private static LruCache<Object, Bitmap> mMyDRUCache;
private PreloadListener preloadListener;
private Handler mPreloaderHandler;
private Handler mPreloaderResponseHandler;
private int mSizeOfGallery=0;
private transient Map<Object,String> mUrl= new HashMap<Object,String>();

public LRUCacheManager(int size) {
	super(LRU_TAG);
	newInstance(size);
}
private static LruCache<Object, Bitmap> newInstance(int size){
	if(mMyDRUCache==null){
		mMyDRUCache=new LruCache<Object, Bitmap>(size){
			@Override
			protected int sizeOf(Object key, Bitmap value) {
				 
				return  value.getByteCount()/1024;
			}
		};
		return mMyDRUCache; 
	}
	return mMyDRUCache;
}




public LRUCacheManager(Handler handler, int size) {
	this(size);
	this.mPreloaderResponseHandler=handler;
	StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
	StrictMode.setThreadPolicy(policy); 
}


public interface PreloadListener<Token>{
	void onTenBefore(Token imageview,Bitmap image );
	void onTenAfter(Token imageview,Bitmap image);
}

@SuppressLint("HandlerLeak")
@Override
protected void onLooperPrepared() {
	synchronized (this) {
		mPreloaderHandler= new Handler(){
			@Override
			public void handleMessage(final Message msg) {
				if(msg.what==DOWNLOAD){
					doWork((Token) msg);
					 
					 
				}
			}
		  
	};
	}
}
 
public synchronized Map<Object,String>  getUrl() {
	return mUrl;
}
 
public void clearQueue(){
	mPreloaderHandler.removeMessages(DOWNLOAD);
}

public  void putImageInCache(Object key, Bitmap bitmap){
	if(null==getImageFromCache(key)){
		LRUCacheManager.mMyDRUCache.put(key, bitmap);
	}
}
public  Bitmap getImageFromCache(Object key){
	//check prepreloadmap or postpreloadmap
	return LRUCacheManager.mMyDRUCache.get(key);
}
public void setUrl(Token token,String url, int sizeofGallery) {
	if(url==null || token==null) return;
	mUrl.put(token,url);
	mSizeOfGallery=sizeofGallery;
	
	 mPreloaderHandler.obtainMessage(DOWNLOAD, token).sendToTarget();//obtain message and send to target
}
private void doWork(Token token){
	int currentPosition=mUrl.size();
	
	if((currentPosition>1 &&currentPosition<10) || (currentPosition==(mSizeOfGallery-10))){
		//preload previous 10
		Log.d("postdownload", "postdownload images ");
		try{
			postdownloadImages();
		}catch(Exception ex){
			ex.printStackTrace();
		}
		
	
	}else if(currentPosition>=10 || currentPosition ==mSizeOfGallery){
		
		Log.d("predownload", "predownload images ");
		try{
			predownloadImages();	
		}catch(Exception ex){
			ex.printStackTrace();

		}
		//preload next 10
		
	}
}
private  void predownloadImages(){
	
	byte[] by=null;
	Bitmap bitmap=null;
	if(getUrl()!=null){
		int count=0;
		 Set<Object> keys=getUrl().keySet();
		 for (Iterator iterator = keys.iterator(); iterator.hasNext();) {
			 Token key = (Token) iterator.next(); 
			 if(key==null) return;
			 if(count>=10) break;
			String url=getUrl().get(key);
			 mUrl.remove(key);//remove the object to avoid reloading
			 bitmap=getBitmap(url, by, bitmap);
			// mPrePreloadUrlBitmap.put(url, bitmap);
			 Log.d("PREDOWNLOADING","got url"+url+"the bitmap is:"+bitmap);

			putImageInCache(key, bitmap);
			synchronized (preloadListener) {
				preloadListener.onTenBefore(key,bitmap);
			}
			
			 mPreloaderResponseHandler.sendMessage((Message) key);//update the ui thread before sending another
			count++;
		}
		 

		 
	}
}

private  void postdownloadImages(){
	byte[] by=null;
	Bitmap bitmap=null;
	if(getUrl()!=null){
		 Set<Object> keys=getUrl().keySet();
		 int count=0;
		 for (Iterator iterator = keys.iterator(); iterator.hasNext();) {
			 Token key = (Token) iterator.next();
			 if(key==null) return;
			 if(count>=10) break;
			String url=getUrl().get(key);
 			 mUrl.remove(key);//remove the object to avoid reloading
			 bitmap=getBitmap(url, by, bitmap);
			 putImageInCache(key, bitmap);
				Log.d("TenAfter", "Bitmap created before ontenAfter and value is"+bitmap);
			 synchronized (preloadListener) {
				 preloadListener.onTenAfter (key,bitmap);
			}
			
			mPreloaderResponseHandler.sendMessage((Message) key);//update the ui thread before sending another
			 count++;
		}
		 

		 
	}
}
private  Bitmap getBitmap(String url,byte[] b,Bitmap bitmap){
	try {
		b=new FlickrFetcher().getUrlBytes(url);
		bitmap=BitmapFactory.decodeByteArray(b, 0, b.length);
		Log.d("getBitmap", "Bitmap created");

		return bitmap;
	} catch (IOException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
		return null;
	}
	
	 
}


public PreloadListener<Token> getPreloadListener() {
	Log.d("preloader", "preloader gotten");
	return preloadListener;
}


public void setPreloadListener(PreloadListener<Token> preloadListener) {
	Log.d("preloader", "preloader set");
	this.preloadListener = preloadListener;
}

}

myPhotogalleryFragment which extends a singleRetainableFragment(just a fragment that setsretainable to true in its onCreateMethod)

package com.example.photogallery;

import java.util.ArrayList;
import java.util.List;

import com.example.photogallery.LRUCacheManager.PreloadListener;
import com.example.photogallery.ThumbnailDownloader.Listener;

import android.content.Context;
import android.graphics.Bitmap;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Process;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.ArrayAdapter;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

public class PhotoGalleryFragment extends SingleRetainableFragment {
private GridView mGridView;
private String TAG=“PHOTO GALLERY”;
private List mPhotoGallery;
private int mGridViewSize;
private FlickrFetcher mFetcher;
private cMyAdapter adapter;
private int mCurrentPage=1;
private ProgressBar progress;
private boolean mCanScroll=false;
private int mTotalCount = 0;
private int scrollPosition=0;
private ThumbnailDownloader mThumbnailHandler;
private ArrayList pages= new ArrayList();
private LRUCacheManager mLRUCache;

@Override
protected int getShowView() {
	return R.layout.fragment_photo_gallery;
}
@Override
public void onCreate(Bundle savedInstanceState) {
	  super.onCreate(savedInstanceState);
	  mFetcher= new FlickrFetcher();
	 progress= new ProgressBar(getActivity());
	 mPhotoGallery= new ArrayList<GalleryItem>();    
	 progress.setMax(100);
	 progress.setVisibility(View.VISIBLE);
	  
	 new mCTask().execute();
	 final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);//get the max memory at runtime

	    // Use 1/8th of the available memory for this memory cache.
	    final int cacheSize = maxMemory / 8;
	  startMLRUcache(cacheSize);
	 mThumbnailHandler= new ThumbnailDownloader<ImageView>(new Handler(),cacheSize,mLRUCache);//pass a reference of the UI handler which contain looper to manage updating of the UI
	 mThumbnailHandler.setListenerTokens(new Listener<ImageView>() {

		@Override
		public void onThumbNailDownloaded(ImageView imageViewAKAToken, Bitmap bitmap) {
			if (isVisible()) {
				imageViewAKAToken.setImageBitmap(bitmap);
			}
		}
	 });
	 mThumbnailHandler.start();
	 Looper looper=mThumbnailHandler.getLooper();
	 Log.i(TAG, "Background thread started");
	
	
}
private void startMLRUcache(int size) {
	 
	mLRUCache= new LRUCacheManager<ImageView>(new Handler(),size);
	 
	 mLRUCache.setPreloadListener(new PreloadListener<ImageView>() {

		@Override
		public void onTenBefore(final ImageView imageview, final Bitmap image) {
			if (isVisible()) {
				imageview.setImageBitmap(image);
				Log.d(TAG,"home ten before called");
			}
			 

		}

		@Override
		public void onTenAfter(ImageView imageview, Bitmap image) {
			if (isVisible()) {
				imageview.setImageBitmap(image);
				Log.d(TAG,"home ten after called");

			}
		}
	});
	 mLRUCache.start();
	 mLRUCache.getLooper();
}
 
private class mCTask extends AsyncTask<String, Integer, List<GalleryItem>>{
	 
		@Override
		protected void onProgressUpdate(Integer... values) {
 			for(Integer val: values){
 				progress.setProgress(val);
 				/**/
 			}
		}
		
		@Override
		protected List<GalleryItem>  doInBackground(String... urls) {
			Integer[] progress={0,10,24,27,29,60,80,100};
			try {
				 publishProgress(progress);
				List<GalleryItem> items= mFetcher.getItems(mCurrentPage);
				
				 return items; 
				 
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
				return null;
			}
		}
		@Override
		protected void onPostExecute(List<GalleryItem> result) {
			
			mPhotoGallery.addAll(result);
			 if(adapter==null)
			 setUpAdapter();
			 else{
				 adapter.addAll(mPhotoGallery);
				 adapter.notifyDataSetChanged();
			 }
		 
			 mGridView.setSelection(scrollPosition);
			 if(mCurrentPage==1)
			 pages.add(mCurrentPage);


		}
	 
 };
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
	 View view= super.onCreateView(inflater, container, savedInstanceState);
	 mGridView=(GridView)view.findViewById(R.id.photoGridView);
	//update or set the size of the gridview
		mGridView.setOnScrollListener(new OnScrollListener() {
			
			@Override
			public void onScrollStateChanged(AbsListView view, int scrollState) {
				 if(scrollState==SCROLL_STATE_TOUCH_SCROLL){
					 mCanScroll=true;
				 }else if(scrollState==SCROLL_STATE_FLING){
					 mCanScroll=false;
				 }
				
			}
			
			@Override
			public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
				  
				/*if(pages.contains(mCurrentPage)){
					  Toast.makeText(getActivity(), "page exist returning", Toast.LENGTH_LONG).show();
					  return; 
				  } */
				//setGridViewSize(mGridView.getAdapter().getCount());
				if((firstVisibleItem + visibleItemCount ==totalItemCount) && (visibleItemCount >0) && (totalItemCount>mTotalCount) && mCanScroll){
					scrollPosition = firstVisibleItem + visibleItemCount;
					mTotalCount=totalItemCount;
					mCurrentPage++;
					pages.add(mCurrentPage);
					Log.d("PAGES", pages.toString());
					 
					new mCTask().execute();
				}
				
			}
		});
	 return view;
}
 

private void setUpAdapter(){
	if(getActivity() ==null || mGridView ==null) return;
	if(mPhotoGallery !=null){
		adapter=new cMyAdapter(getActivity(), mPhotoGallery);
		mGridView.setAdapter(adapter);
		
		Toast.makeText(getActivity(), "items recieved", Toast.LENGTH_LONG).show();
	}else{
		mGridView.setAdapter(null);
		Toast.makeText(getActivity(), "no items recieved", Toast.LENGTH_LONG).show();

	}
}

 
 private class cMyAdapter extends ArrayAdapter<GalleryItem>{

	public cMyAdapter(Context context ,List<GalleryItem> objects) {
		super(context, 0, objects);
		 
	}
	
	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		if(convertView==null){
			convertView=getActivity().getLayoutInflater().inflate(R.layout.gallery_item,parent,false);
		}
		 TextView captionTitle=(TextView)convertView.findViewById(R.id.gallery_image_captionTitle);
		 ImageView imageView=(ImageView)convertView.findViewById(R.id.gallery_image);
		 
		 //get the Gallery item and queue if for the handler thread to download
		 GalleryItem item =this.getItem(position);
		 
		 mThumbnailHandler.queueMessageThumbNail(imageView, item.getUrl(),this.getCount());
		return convertView;
	}
	 
 }
public int getGridViewSize() {
	return mGridViewSize;
}
public void setGridViewSize(int gridViewSize) {
	mGridViewSize = getGridViewSize()+gridViewSize;
}

@Override
public void onDestroy() {
	  super.onDestroy();
	  
	  if(mThumbnailHandler.isAlive()) {
		
		  mThumbnailHandler.quit();
	  }
	  
}
@Override
public void onDestroyView() {
	super.onDestroyView();
	 mThumbnailHandler.clearQueue();
}

}

thats aall . its simple