1 package com.fedorvlasov.lazylist;
2
3 import java.io.File;
4 import android.content.Context;
5
6 public class FileCache {
7
8 private File cacheDir;
9
10 public FileCache(Context context){
11 // Find the dir to save cached images
12 if (android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED))
13 cacheDir= new File(android.os.Environment.getExternalStorageDirectory(),"LazyList");
14 else
15 cacheDir=context.getCacheDir();
16 if(!cacheDir.exists())
17 cacheDir.mkdirs();
18 }
19
20 public File getFile(String url){
21 // I identify images by hashcode. Not a perfect solution, good for the demo.
22 String filename=String.valueOf(url.hashCode());
23 // Another possible solution (thanks to grantland)
24 // String filename = URLEncoder.encode(url);
25 File f = new File(cacheDir, filename);
26 return f;
27
28 }
29
30 public void clear(){
31 File[] files=cacheDir.listFiles();
32 if(files== null)
33 return;
34 for(File f:files)
35 f.delete();
36 }
37
38 }
3 import java.io.File;
4 import android.content.Context;
5
6 public class FileCache {
7
8 private File cacheDir;
9
10 public FileCache(Context context){
11 // Find the dir to save cached images
12 if (android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED))
13 cacheDir= new File(android.os.Environment.getExternalStorageDirectory(),"LazyList");
14 else
15 cacheDir=context.getCacheDir();
16 if(!cacheDir.exists())
17 cacheDir.mkdirs();
18 }
19
20 public File getFile(String url){
21 // I identify images by hashcode. Not a perfect solution, good for the demo.
22 String filename=String.valueOf(url.hashCode());
23 // Another possible solution (thanks to grantland)
24 // String filename = URLEncoder.encode(url);
25 File f = new File(cacheDir, filename);
26 return f;
27
28 }
29
30 public void clear(){
31 File[] files=cacheDir.listFiles();
32 if(files== null)
33 return;
34 for(File f:files)
35 f.delete();
36 }
37
38 }
1 package com.fedorvlasov.lazylist;
2
3 import java.util.Collections;
4 import java.util.Iterator;
5 import java.util.LinkedHashMap;
6 import java.util.Map;
7 import java.util.Map.Entry;
8 import android.graphics.Bitmap;
9 import android.util.Log;
10
11 public class MemoryCache {
12
13 private static final String TAG = "MemoryCache";
14 private Map<String, Bitmap> cache=Collections.synchronizedMap(
15 new LinkedHashMap<String, Bitmap>(10,1.5f, true)); // Last argument true for LRU ordering
16 private long size=0; // current allocated size
17 private long limit=1000000; // max memory in bytes
18
19 public MemoryCache(){
20 // use 25% of available heap size
21 setLimit(Runtime.getRuntime().maxMemory()/4);
22 }
23
24 public void setLimit( long new_limit){
25 limit=new_limit;
26 Log.i(TAG, "MemoryCache will use up to "+limit/1024./1024.+"MB");
27 }
28
29 public Bitmap get(String id){
30 if(!cache.containsKey(id))
31 return null;
32 return cache.get(id);
33 }
34
35 public void put(String id, Bitmap bitmap){
36 try{
37 if(cache.containsKey(id))
38 size-=getSizeInBytes(cache.get(id));
39 cache.put(id, bitmap);
40 size+=getSizeInBytes(bitmap);
41 checkSize();
42 } catch(Throwable th){
43 th.printStackTrace();
44 }
45 }
46
47 private void checkSize() {
48 Log.i(TAG, "cache size="+size+" length="+cache.size());
49 if(size>limit){
50 Iterator<Entry<String, Bitmap>> iter=cache.entrySet().iterator(); // least recently accessed item will be the first one iterated
51 while(iter.hasNext()){
52 Entry<String, Bitmap> entry=iter.next();
53 size-=getSizeInBytes(entry.getValue());
54 iter.remove();
55 if(size<=limit)
56 break;
57 }
58 Log.i(TAG, "Clean cache. New size "+cache.size());
59 }
60 }
61
62 public void clear() {
63 cache.clear();
64 }
65
66 long getSizeInBytes(Bitmap bitmap) {
67 if(bitmap== null)
68 return 0;
69 return bitmap.getRowBytes() * bitmap.getHeight();
70 }
71 }
3 import java.util.Collections;
4 import java.util.Iterator;
5 import java.util.LinkedHashMap;
6 import java.util.Map;
7 import java.util.Map.Entry;
8 import android.graphics.Bitmap;
9 import android.util.Log;
10
11 public class MemoryCache {
12
13 private static final String TAG = "MemoryCache";
14 private Map<String, Bitmap> cache=Collections.synchronizedMap(
15 new LinkedHashMap<String, Bitmap>(10,1.5f, true)); // Last argument true for LRU ordering
16 private long size=0; // current allocated size
17 private long limit=1000000; // max memory in bytes
18
19 public MemoryCache(){
20 // use 25% of available heap size
21 setLimit(Runtime.getRuntime().maxMemory()/4);
22 }
23
24 public void setLimit( long new_limit){
25 limit=new_limit;
26 Log.i(TAG, "MemoryCache will use up to "+limit/1024./1024.+"MB");
27 }
28
29 public Bitmap get(String id){
30 if(!cache.containsKey(id))
31 return null;
32 return cache.get(id);
33 }
34
35 public void put(String id, Bitmap bitmap){
36 try{
37 if(cache.containsKey(id))
38 size-=getSizeInBytes(cache.get(id));
39 cache.put(id, bitmap);
40 size+=getSizeInBytes(bitmap);
41 checkSize();
42 } catch(Throwable th){
43 th.printStackTrace();
44 }
45 }
46
47 private void checkSize() {
48 Log.i(TAG, "cache size="+size+" length="+cache.size());
49 if(size>limit){
50 Iterator<Entry<String, Bitmap>> iter=cache.entrySet().iterator(); // least recently accessed item will be the first one iterated
51 while(iter.hasNext()){
52 Entry<String, Bitmap> entry=iter.next();
53 size-=getSizeInBytes(entry.getValue());
54 iter.remove();
55 if(size<=limit)
56 break;
57 }
58 Log.i(TAG, "Clean cache. New size "+cache.size());
59 }
60 }
61
62 public void clear() {
63 cache.clear();
64 }
65
66 long getSizeInBytes(Bitmap bitmap) {
67 if(bitmap== null)
68 return 0;
69 return bitmap.getRowBytes() * bitmap.getHeight();
70 }
71 }
1 package com.fedorvlasov.lazylist;
2
3 import java.io.InputStream;
4 import java.io.OutputStream;
5
6 public class Utils {
7 public static void CopyStream(InputStream is, OutputStream os)
8 {
9 final int buffer_size=1024;
10 try
11 {
12 byte[] bytes= new byte[buffer_size];
13 for(;;)
14 {
15 int count=is.read(bytes, 0, buffer_size);
16 if(count==-1)
17 break;
18 os.write(bytes, 0, count);
19 }
20 }
21 catch(Exception ex){}
22 }
23 }
3 import java.io.InputStream;
4 import java.io.OutputStream;
5
6 public class Utils {
7 public static void CopyStream(InputStream is, OutputStream os)
8 {
9 final int buffer_size=1024;
10 try
11 {
12 byte[] bytes= new byte[buffer_size];
13 for(;;)
14 {
15 int count=is.read(bytes, 0, buffer_size);
16 if(count==-1)
17 break;
18 os.write(bytes, 0, count);
19 }
20 }
21 catch(Exception ex){}
22 }
23 }
1 package com.fedorvlasov.lazylist;
2
3 import java.io.File;
4 import java.io.FileInputStream;
5 import java.io.FileNotFoundException;
6 import java.io.FileOutputStream;
7 import java.io.InputStream;
8 import java.io.OutputStream;
9 import java.net.HttpURLConnection;
10 import java.net.URL;
11 import java.util.Collections;
12 import java.util.Map;
13 import java.util.WeakHashMap;
14 import java.util.concurrent.ExecutorService;
15 import java.util.concurrent.Executors;
16
17 import android.app.Activity;
18 import android.content.Context;
19 import android.graphics.Bitmap;
20 import android.graphics.BitmapFactory;
21 import android.widget.ImageView;
22
23 public class ImageLoader {
24
25 MemoryCache memoryCache= new MemoryCache();
26 FileCache fileCache;
27 private Map<ImageView, String> imageViews=Collections.synchronizedMap( new WeakHashMap<ImageView, String>());
28 ExecutorService executorService;
29
30 public ImageLoader(Context context){
31 fileCache= new FileCache(context);
32 executorService=Executors.newFixedThreadPool(5);
33 }
34
35 final int stub_id=R.drawable.stub;
36 public void DisplayImage(String url, ImageView imageView)
37 {
38 imageViews.put(imageView, url);
39 Bitmap bitmap=memoryCache.get(url);
40 if(bitmap!= null)
41 imageView.setImageBitmap(bitmap);
42 else
43 {
44 queuePhoto(url, imageView);
45 imageView.setImageResource(stub_id);
46 }
47 }
48
49 private void queuePhoto(String url, ImageView imageView)
50 {
51 PhotoToLoad p= new PhotoToLoad(url, imageView);
52 executorService.submit( new PhotosLoader(p));
53 }
54
55 private Bitmap getBitmap(String url)
56 {
57 File f=fileCache.getFile(url);
58
59 // from SD cache
60 Bitmap b = decodeFile(f);
61 if(b!= null)
62 return b;
63
64 // from web
65 try {
66 Bitmap bitmap= null;
67 URL imageUrl = new URL(url);
68 HttpURLConnection conn = (HttpURLConnection)imageUrl.openConnection();
69 conn.setConnectTimeout(30000);
70 conn.setReadTimeout(30000);
71 conn.setInstanceFollowRedirects( true);
72 InputStream is=conn.getInputStream();
73 OutputStream os = new FileOutputStream(f);
74 Utils.CopyStream(is, os);
75 os.close();
76 bitmap = decodeFile(f);
77 return bitmap;
78 } catch (Exception ex){
79 ex.printStackTrace();
80 return null;
81 }
82 }
83
84 // decodes image and scales it to reduce memory consumption
85 private Bitmap decodeFile(File f){
86 try {
87 // decode image size
88 BitmapFactory.Options o = new BitmapFactory.Options();
89 o.inJustDecodeBounds = true;
90 BitmapFactory.decodeStream( new FileInputStream(f), null,o);
91
92 // Find the correct scale value. It should be the power of 2.
93 final int REQUIRED_SIZE=70;
94 int width_tmp=o.outWidth, height_tmp=o.outHeight;
95 int scale=1;
96 while( true){
97 if(width_tmp/2<REQUIRED_SIZE || height_tmp/2<REQUIRED_SIZE)
98 break;
99 width_tmp/=2;
100 height_tmp/=2;
101 scale*=2;
102 }
103
104 // decode with inSampleSize
105 BitmapFactory.Options o2 = new BitmapFactory.Options();
106 o2.inSampleSize=scale;
107 return BitmapFactory.decodeStream( new FileInputStream(f), null, o2);
108 } catch (FileNotFoundException e) {}
109 return null;
110 }
111
112 // Task for the queue
113 private class PhotoToLoad
114 {
115 public String url;
116 public ImageView imageView;
117 public PhotoToLoad(String u, ImageView i){
118 url=u;
119 imageView=i;
120 }
121 }
122
123 class PhotosLoader implements Runnable {
124 PhotoToLoad photoToLoad;
125 PhotosLoader(PhotoToLoad photoToLoad){
126 this.photoToLoad=photoToLoad;
127 }
128
129 @Override
130 public void run() {
131 if(imageViewReused(photoToLoad))
132 return;
133 Bitmap bmp=getBitmap(photoToLoad.url);
134 memoryCache.put(photoToLoad.url, bmp);
135 if(imageViewReused(photoToLoad))
136 return;
137 BitmapDisplayer bd= new BitmapDisplayer(bmp, photoToLoad);
138 Activity a=(Activity)photoToLoad.imageView.getContext();
139 a.runOnUiThread(bd);
140 }
141 }
142
143 boolean imageViewReused(PhotoToLoad photoToLoad){
144 String tag=imageViews.get(photoToLoad.imageView);
145 if(tag== null || !tag.equals(photoToLoad.url))
146 return true;
147 return false;
148 }
149
150 // Used to display bitmap in the UI thread
151 class BitmapDisplayer implements Runnable
152 {
153 Bitmap bitmap;
154 PhotoToLoad photoToLoad;
155 public BitmapDisplayer(Bitmap b, PhotoToLoad p){bitmap=b;photoToLoad=p;}
156 public void run()
157 {
158 if(imageViewReused(photoToLoad))
159 return;
160 if(bitmap!= null)
161 photoToLoad.imageView.setImageBitmap(bitmap);
162 else
163 photoToLoad.imageView.setImageResource(stub_id);
164 }
165 }
166
167 public void clearCache() {
168 memoryCache.clear();
169 fileCache.clear();
170 }
171
172 }
3 import java.io.File;
4 import java.io.FileInputStream;
5 import java.io.FileNotFoundException;
6 import java.io.FileOutputStream;
7 import java.io.InputStream;
8 import java.io.OutputStream;
9 import java.net.HttpURLConnection;
10 import java.net.URL;
11 import java.util.Collections;
12 import java.util.Map;
13 import java.util.WeakHashMap;
14 import java.util.concurrent.ExecutorService;
15 import java.util.concurrent.Executors;
16
17 import android.app.Activity;
18 import android.content.Context;
19 import android.graphics.Bitmap;
20 import android.graphics.BitmapFactory;
21 import android.widget.ImageView;
22
23 public class ImageLoader {
24
25 MemoryCache memoryCache= new MemoryCache();
26 FileCache fileCache;
27 private Map<ImageView, String> imageViews=Collections.synchronizedMap( new WeakHashMap<ImageView, String>());
28 ExecutorService executorService;
29
30 public ImageLoader(Context context){
31 fileCache= new FileCache(context);
32 executorService=Executors.newFixedThreadPool(5);
33 }
34
35 final int stub_id=R.drawable.stub;
36 public void DisplayImage(String url, ImageView imageView)
37 {
38 imageViews.put(imageView, url);
39 Bitmap bitmap=memoryCache.get(url);
40 if(bitmap!= null)
41 imageView.setImageBitmap(bitmap);
42 else
43 {
44 queuePhoto(url, imageView);
45 imageView.setImageResource(stub_id);
46 }
47 }
48
49 private void queuePhoto(String url, ImageView imageView)
50 {
51 PhotoToLoad p= new PhotoToLoad(url, imageView);
52 executorService.submit( new PhotosLoader(p));
53 }
54
55 private Bitmap getBitmap(String url)
56 {
57 File f=fileCache.getFile(url);
58
59 // from SD cache
60 Bitmap b = decodeFile(f);
61 if(b!= null)
62 return b;
63
64 // from web
65 try {
66 Bitmap bitmap= null;
67 URL imageUrl = new URL(url);
68 HttpURLConnection conn = (HttpURLConnection)imageUrl.openConnection();
69 conn.setConnectTimeout(30000);
70 conn.setReadTimeout(30000);
71 conn.setInstanceFollowRedirects( true);
72 InputStream is=conn.getInputStream();
73 OutputStream os = new FileOutputStream(f);
74 Utils.CopyStream(is, os);
75 os.close();
76 bitmap = decodeFile(f);
77 return bitmap;
78 } catch (Exception ex){
79 ex.printStackTrace();
80 return null;
81 }
82 }
83
84 // decodes image and scales it to reduce memory consumption
85 private Bitmap decodeFile(File f){
86 try {
87 // decode image size
88 BitmapFactory.Options o = new BitmapFactory.Options();
89 o.inJustDecodeBounds = true;
90 BitmapFactory.decodeStream( new FileInputStream(f), null,o);
91
92 // Find the correct scale value. It should be the power of 2.
93 final int REQUIRED_SIZE=70;
94 int width_tmp=o.outWidth, height_tmp=o.outHeight;
95 int scale=1;
96 while( true){
97 if(width_tmp/2<REQUIRED_SIZE || height_tmp/2<REQUIRED_SIZE)
98 break;
99 width_tmp/=2;
100 height_tmp/=2;
101 scale*=2;
102 }
103
104 // decode with inSampleSize
105 BitmapFactory.Options o2 = new BitmapFactory.Options();
106 o2.inSampleSize=scale;
107 return BitmapFactory.decodeStream( new FileInputStream(f), null, o2);
108 } catch (FileNotFoundException e) {}
109 return null;
110 }
111
112 // Task for the queue
113 private class PhotoToLoad
114 {
115 public String url;
116 public ImageView imageView;
117 public PhotoToLoad(String u, ImageView i){
118 url=u;
119 imageView=i;
120 }
121 }
122
123 class PhotosLoader implements Runnable {
124 PhotoToLoad photoToLoad;
125 PhotosLoader(PhotoToLoad photoToLoad){
126 this.photoToLoad=photoToLoad;
127 }
128
129 @Override
130 public void run() {
131 if(imageViewReused(photoToLoad))
132 return;
133 Bitmap bmp=getBitmap(photoToLoad.url);
134 memoryCache.put(photoToLoad.url, bmp);
135 if(imageViewReused(photoToLoad))
136 return;
137 BitmapDisplayer bd= new BitmapDisplayer(bmp, photoToLoad);
138 Activity a=(Activity)photoToLoad.imageView.getContext();
139 a.runOnUiThread(bd);
140 }
141 }
142
143 boolean imageViewReused(PhotoToLoad photoToLoad){
144 String tag=imageViews.get(photoToLoad.imageView);
145 if(tag== null || !tag.equals(photoToLoad.url))
146 return true;
147 return false;
148 }
149
150 // Used to display bitmap in the UI thread
151 class BitmapDisplayer implements Runnable
152 {
153 Bitmap bitmap;
154 PhotoToLoad photoToLoad;
155 public BitmapDisplayer(Bitmap b, PhotoToLoad p){bitmap=b;photoToLoad=p;}
156 public void run()
157 {
158 if(imageViewReused(photoToLoad))
159 return;
160 if(bitmap!= null)
161 photoToLoad.imageView.setImageBitmap(bitmap);
162 else
163 photoToLoad.imageView.setImageResource(stub_id);
164 }
165 }
166
167 public void clearCache() {
168 memoryCache.clear();
169 fileCache.clear();
170 }
171
172 }
1 package com.fedorvlasov.lazylist;
2
3 import android.app.Activity;
4 import android.content.Context;
5 import android.view.LayoutInflater;
6 import android.view.View;
7 import android.view.ViewGroup;
8 import android.widget.BaseAdapter;
9 import android.widget.ImageView;
10 import android.widget.TextView;
11
12 public class LazyAdapter extends BaseAdapter {
13
14 private Activity activity;
15 private String[] data;
16 private static LayoutInflater inflater= null;
17 public ImageLoader imageLoader;
18
19 public LazyAdapter(Activity a, String[] d) {
20 activity = a;
21 data=d;
22 inflater = (LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
23 imageLoader= new ImageLoader(activity);
24 }
25
26 public int getCount() {
27 return data.length;
28 }
29
30 public Object getItem( int position) {
31 return position;
32 }
33
34 public long getItemId( int position) {
35 return position;
36 }
37
38 public View getView( int position, View convertView, ViewGroup parent) {
39 View vi=convertView;
40 if(convertView== null)
41 vi = inflater.inflate(R.layout.item, null);
42
43 TextView text=(TextView)vi.findViewById(R.id.text);;
44 ImageView image=(ImageView)vi.findViewById(R.id.image);
45 text.setText("item "+position);
46 imageLoader.DisplayImage(data[position], image);
47 return vi;
48 }
49 }
3 import android.app.Activity;
4 import android.content.Context;
5 import android.view.LayoutInflater;
6 import android.view.View;
7 import android.view.ViewGroup;
8 import android.widget.BaseAdapter;
9 import android.widget.ImageView;
10 import android.widget.TextView;
11
12 public class LazyAdapter extends BaseAdapter {
13
14 private Activity activity;
15 private String[] data;
16 private static LayoutInflater inflater= null;
17 public ImageLoader imageLoader;
18
19 public LazyAdapter(Activity a, String[] d) {
20 activity = a;
21 data=d;
22 inflater = (LayoutInflater)activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
23 imageLoader= new ImageLoader(activity);
24 }
25
26 public int getCount() {
27 return data.length;
28 }
29
30 public Object getItem( int position) {
31 return position;
32 }
33
34 public long getItemId( int position) {
35 return position;
36 }
37
38 public View getView( int position, View convertView, ViewGroup parent) {
39 View vi=convertView;
40 if(convertView== null)
41 vi = inflater.inflate(R.layout.item, null);
42
43 TextView text=(TextView)vi.findViewById(R.id.text);;
44 ImageView image=(ImageView)vi.findViewById(R.id.image);
45 text.setText("item "+position);
46 imageLoader.DisplayImage(data[position], image);
47 return vi;
48 }
49 }
1 package com.fedorvlasov.lazylist;
2
3 import java.io.InputStream;
4 import java.io.OutputStream;
5
6 public class Utils {
7 public static void CopyStream(InputStream is, OutputStream os)
8 {
9 final int buffer_size=1024;
10 try
11 {
12 byte[] bytes= new byte[buffer_size];
13 for(;;)
14 {
15 int count=is.read(bytes, 0, buffer_size);
16 if(count==-1)
17 break;
18 os.write(bytes, 0, count);
19 }
20 }
21 catch(Exception ex){}
22 }
23 }
3 import java.io.InputStream;
4 import java.io.OutputStream;
5
6 public class Utils {
7 public static void CopyStream(InputStream is, OutputStream os)
8 {
9 final int buffer_size=1024;
10 try
11 {
12 byte[] bytes= new byte[buffer_size];
13 for(;;)
14 {
15 int count=is.read(bytes, 0, buffer_size);
16 if(count==-1)
17 break;
18 os.write(bytes, 0, count);
19 }
20 }
21 catch(Exception ex){}
22 }
23 }