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 }
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 }
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 }
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 }
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 }
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 }