Skip to content
This repository
Browse code

lots of changes for image loader, add possibility to use lrucache, de…

…fine specific resource images for default and error, scale bitmap of resource images are cache
  • Loading branch information...
commit 0926e680ecad9c369c95581b4e54e4a35e4ecc16 1 parent 30016ae
Luigi Agosti luigi-agosti authored

Showing 35 changed files with 883 additions and 581 deletions. Show diff stats Hide diff stats

  1. +83 0 core/src/main/java/com/android/camera/gallery/LruCache.java
  2. +0 143 core/src/main/java/com/novoda/imageloader/core/BaseImageLoader.java
  3. +0 182 core/src/main/java/com/novoda/imageloader/core/CacheManager.java
  4. +6 8 core/src/main/java/com/novoda/imageloader/core/{ImageManager.java → ImageLoader.java}
  5. +0 62 core/src/main/java/com/novoda/imageloader/core/SettingsBuilder.java
  6. +15 0 core/src/main/java/com/novoda/imageloader/core/cache/BitmapCache.java
  7. +0 23 core/src/main/java/com/novoda/imageloader/core/cache/ImageCache.java
  8. +35 0 core/src/main/java/com/novoda/imageloader/core/cache/LruBitmapCache.java
  9. +2 22 core/src/main/java/com/novoda/imageloader/core/cache/MapCache.java
  10. +2 22 core/src/main/java/com/novoda/imageloader/core/cache/NoCache.java
  11. +2 24 core/src/main/java/com/novoda/imageloader/core/cache/SoftMapCache.java
  12. +15 0 core/src/main/java/com/novoda/imageloader/core/exception/ImageCopyException.java
  13. +7 5 core/src/main/java/com/novoda/imageloader/core/file/FileUtil.java
  14. +179 0 core/src/main/java/com/novoda/imageloader/core/loader/SingleThreadedImageLoader.java
  15. +131 0 core/src/main/java/com/novoda/imageloader/core/loader/SingleThreadedLoader.java
  16. +39 0 core/src/main/java/com/novoda/imageloader/core/model/ImageTag.java
  17. +40 0 core/src/main/java/com/novoda/imageloader/core/model/ImageTagFactory.java
  18. +74 0 core/src/main/java/com/novoda/imageloader/core/model/ImageWrapper.java
  19. +27 0 core/src/main/java/com/novoda/imageloader/core/util/BitmapDisplayer.java
  20. +10 16 core/src/main/java/com/novoda/imageloader/core/util/BitmapUtil.java
  21. +1 39 core/src/main/java/com/novoda/imageloader/core/{ → util}/Settings.java
  22. +25 0 core/src/main/java/com/novoda/imageloader/core/util/SettingsBuilder.java
  23. +0 2  core/src/main/java/com/novoda/imageloader/core/util/UrlUtil.java
  24. +2 0  demo/AndroidManifest.xml
  25. +75 0 demo/assets/contents/images.sql
  26. BIN  demo/res/drawable-hdpi/bg_img_notfound.png
  27. +1 1  demo/res/layout/image_item.xml
  28. +15 0 demo/res/layout/small_image_item.xml
  29. +22 9 demo/src/com/novoda/imageloader/demo/DemoApplication.java
  30. +2 3 demo/src/com/novoda/imageloader/demo/activity/FromCacheOnly.java
  31. +11 0 demo/src/com/novoda/imageloader/demo/activity/ImageLongList.java
  32. +29 0 demo/src/com/novoda/imageloader/demo/activity/LongSmallImageList.java
  33. +24 19 demo/src/com/novoda/imageloader/demo/activity/base/SingleTableBaseListActivity.java
  34. +5 0 demo/src/com/novoda/imageloader/demo/provider/CustomUriMatcher.java
  35. +4 1 demo/src/com/novoda/imageloader/demo/provider/DatabaseManager.java
83 core/src/main/java/com/android/camera/gallery/LruCache.java
... ... @@ -0,0 +1,83 @@
143 core/src/main/java/com/novoda/imageloader/core/BaseImageLoader.java
... ... @@ -1,143 +0,0 @@
1   -package com.novoda.imageloader.core;
2   -
3   -import java.io.File;
4   -
5   -import android.content.Context;
6   -import android.content.Intent;
7   -import android.graphics.Bitmap;
8   -import android.util.Log;
9   -import android.widget.ImageView;
10   -
11   -import com.novoda.imageloader.core.cache.ImageCache;
12   -import com.novoda.imageloader.core.cache.SoftMapCache;
13   -import com.novoda.imageloader.core.file.FileUtil;
14   -import com.novoda.imageloader.core.service.CacheCleaner;
15   -import com.novoda.imageloader.core.util.BitmapUtil;
16   -import com.novoda.imageloader.core.util.UrlUtil;
17   -
18   -public class BaseImageLoader implements ImageManager {
19   -
20   - private static final String TAG = "ImageLoader";
21   -
22   - private UrlUtil urlUtil = new UrlUtil();
23   - private BitmapUtil bitmapUtil;
24   - private CacheManager cacheManager;
25   - private Settings settings;
26   -
27   - public BaseImageLoader(Context context, Settings settings) {
28   - this.bitmapUtil = new BitmapUtil();
29   - this.settings = settings;
30   - this.cacheManager = new CacheManager(this, initCache(context, settings));
31   - }
32   -
33   - @Override
34   - public void load(String url, Context activity, ImageView imageView) {
35   - try {
36   - if (cacheManager.hasImageInCache(url)) {
37   - Bitmap b = cacheManager.getImageFromCache(url);
38   - if (b != null) {
39   - imageView.setImageBitmap(b);
40   - return;
41   - }
42   - }
43   - cacheManager.push(new CacheManager.Image(url, imageView));
44   - } catch (Throwable t) {
45   - Log.e(TAG, t.getMessage(), t);
46   - }
47   - }
48   -
49   - @Override
50   - public void loadFromCacheOnly(String url, Context activity, ImageView imageView) {
51   - try {
52   - if (cacheManager.hasImageInCache(url)) {
53   - Bitmap b = cacheManager.getImageFromCache(url);
54   - if (b != null) {
55   - imageView.setImageBitmap(b);
56   - return;
57   - }
58   - return;
59   - }
60   - imageView.setImageBitmap(cacheManager.getDefaultImage());
61   - } catch (Throwable t) {
62   - Log.e(TAG, t.getMessage(), t);
63   - }
64   - }
65   -
66   - @Override
67   - public Bitmap getBitmap(String url) {
68   - return getBitmap(url, false);
69   - }
70   -
71   - @Override
72   - public String getFilePath(String imageUrl) {
73   - File f = getFile(imageUrl);
74   - if (f.exists()) {
75   - return f.getAbsolutePath();
76   - }
77   - return null;
78   - }
79   -
80   - @Override
81   - public Bitmap getBitmap(String url, boolean scale) {
82   - if (url != null && url.length() >= 0) {
83   - File f = getFile(url);
84   - if (f.exists()) {
85   - Bitmap b = bitmapUtil.decodeFileAndScale(f, scale, settings);
86   - if (b != null) {
87   - return b;
88   - }
89   - }
90   - new FileUtil().retrieveImage(url, f);
91   - return bitmapUtil.decodeFileAndScale(f, scale, settings);
92   - }
93   - return null;
94   - }
95   -
96   - private File getFile(String url) {
97   - url = processUrl(url);
98   - String filename = String.valueOf(url.hashCode());
99   - return new File(settings.getCacheDir(), filename + ".jpg");
100   - }
101   -
102   - protected String processUrl(String url) {
103   - if (!settings.isQueryIncludedInHash()) {
104   - return url;
105   - }
106   - return urlUtil.removeQuery(url);
107   - }
108   -
109   - @Override
110   - public void deleteFileCache(Context context) {
111   - sendCacheCleanUpBroadcast(context, 0);
112   - }
113   -
114   - @Override
115   - public void reduceFileCache(Context context) {
116   - long expirationPeriod = settings.getExpirationPeriod();
117   - sendCacheCleanUpBroadcast(context, expirationPeriod);
118   - }
119   -
120   - @Override
121   - public void cleanCache() {
122   - cacheManager.resetCache(createCache());
123   - }
124   -
125   - protected ImageCache createCache() {
126   - return new SoftMapCache();
127   - }
128   -
129   - private ImageCache initCache(Context context, Settings settings) {
130   - ImageCache cache = createCache();
131   - cache.setDefaultImage(bitmapUtil.decodeDefaultAndScaleBitmap(context, settings));
132   - cache.setNotFoundImage(bitmapUtil.decodeNotFoundAndScaleBitmap(context, settings));
133   - return cache;
134   - }
135   -
136   - private void sendCacheCleanUpBroadcast(Context context, long expirationPeriod) {
137   - String path = settings.getCacheDir().getAbsolutePath();
138   - Intent i = CacheCleaner.getCleanCacheIntent(path, expirationPeriod);
139   - i.setPackage(context.getPackageName());
140   - context.startService(i);
141   - }
142   -
143   -}
182 core/src/main/java/com/novoda/imageloader/core/CacheManager.java
... ... @@ -1,182 +0,0 @@
1   -package com.novoda.imageloader.core;
2   -
3   -import java.util.ArrayList;
4   -import java.util.List;
5   -import java.util.Stack;
6   -
7   -import android.app.Activity;
8   -import android.graphics.Bitmap;
9   -import android.text.TextUtils;
10   -import android.util.Log;
11   -import android.widget.ImageView;
12   -
13   -import com.novoda.imageloader.core.cache.ImageCache;
14   -import com.novoda.imageloader.core.exception.ImageNotFoundException;
15   -
16   -public class CacheManager {
17   -
18   - private static final String TAG = "ImageLoader";
19   -
20   - private ImageCache cache;
21   - private PhotosLoader thread;
22   - private Stack<Image> stack;
23   - private List<String> notFoundImages;
24   - private ImageManager imageLoader;
25   -
26   - public static class Image {
27   - public String url;
28   - public ImageView imageView;
29   -
30   - public Image(String u, ImageView i) {
31   - url = u;
32   - imageView = i;
33   - }
34   - }
35   -
36   - public CacheManager(ImageManager imageLoader, ImageCache cache) {
37   - this.imageLoader = imageLoader;
38   - this.cache = cache;
39   - this.stack = new Stack<Image>();
40   - this.notFoundImages = new ArrayList<String>();
41   - thread = new PhotosLoader();
42   - thread.setPriority(Thread.NORM_PRIORITY - 1);
43   - }
44   -
45   - public void push(Image p) {
46   - p.imageView.setImageBitmap(cache.getDefaultImage());
47   - if (TextUtils.isEmpty(p.url)) {
48   - return;
49   - }
50   - pushOnStack(p);
51   - if (thread.getState() == Thread.State.NEW) {
52   - thread.start();
53   - } else {
54   - synchronized (thread) {
55   - if (thread.isWaiting) {
56   - try {
57   - thread.isWaiting = false;
58   - thread.notify();
59   - } catch (Exception ie) {
60   - Log.e(TAG, "Check and resume the thread " + ie.getMessage());
61   - }
62   - }
63   - }
64   - }
65   - }
66   -
67   - public synchronized int size() {
68   - return stack.size();
69   - }
70   -
71   - public synchronized Image pop() {
72   - Image i = stack.pop();
73   - return i;
74   - }
75   -
76   - private synchronized void clean(Image p) {
77   - for (int j = 0; j < stack.size(); j++) {
78   - if (stack.get(j).url != null && stack.get(j).url.equals(p.url)) {
79   - stack.remove(j);
80   - j--;
81   - }
82   - }
83   - }
84   -
85   - private synchronized void pushOnStack(Image p) {
86   - stack.push(p);
87   - }
88   -
89   - private class PhotosLoader extends Thread {
90   - boolean isWaiting = false;
91   -
92   - @Override
93   - public void run() {
94   - while (true) {
95   - pauseThreadIfnecessary();
96   - Image image = stack.pop();
97   - loadAndShowImage(image);
98   - }
99   - }
100   -
101   - private void pauseThreadIfnecessary() {
102   - if (stack.size() == 0) {
103   - synchronized (this) {
104   - try {
105   - isWaiting = true;
106   - wait();
107   - } catch (Exception e) {
108   - Log.v(TAG, "Pausing the thread error " + e.getMessage());
109   - }
110   - }
111   - }
112   - }
113   -
114   - private void loadAndShowImage(Image image) {
115   - try {
116   - Bitmap bmp = imageLoader.getBitmap(image.url, true);
117   - if (bmp == null) {
118   - bmp = cache.getDefaultImage();
119   - clean(image);
120   - } else {
121   - cache.put(image.url, bmp);
122   - }
123   - displayImage(image, bmp);
124   - } catch(ImageNotFoundException inf) {
125   - notFoundImages.add(image.url);
126   - } catch (Throwable e) {
127   - Log.e(TAG, "Throwable : " + e.getMessage(), e);
128   - }
129   - }
130   -
131   - private void displayImage(Image image, Bitmap bmp) {
132   - if (image.imageView.getTag() != null
133   - && ((String) image.imageView.getTag()).equals(image.url)) {
134   - BitmapDisplayer bd = new BitmapDisplayer(bmp, image.imageView);
135   - Activity a = (Activity) image.imageView.getContext();
136   - a.runOnUiThread(bd);
137   - }
138   - }
139   - }
140   -
141   - private class BitmapDisplayer implements Runnable {
142   - private Bitmap bitmap;
143   - private ImageView imageView;
144   -
145   - public BitmapDisplayer(Bitmap b, ImageView i) {
146   - bitmap = b;
147   - imageView = i;
148   - }
149   -
150   - @Override
151   - public void run() {
152   - if (bitmap != null) {
153   - imageView.setImageBitmap(bitmap);
154   - } else {
155   - imageView.setImageBitmap(cache.getDefaultImage());
156   - }
157   - }
158   - }
159   -
160   - public boolean hasImageInCache(String url) {
161   - if(notFoundImages.contains(url)) {
162   - return true;
163   - }
164   - return cache.hasImage(url);
165   - }
166   -
167   - public Bitmap getImageFromCache(String url) {
168   - if(notFoundImages.contains(url)) {
169   - return cache.getNotFoundImage();
170   - }
171   - return cache.get(url);
172   - }
173   -
174   - public void resetCache(ImageCache cache) {
175   - this.cache.clean();
176   - }
177   -
178   - public Bitmap getDefaultImage() {
179   - return cache.getDefaultImage();
180   - }
181   -
182   -}
14 ...ava/com/novoda/imageloader/core/ImageManager.java → ...java/com/novoda/imageloader/core/ImageLoader.java
@@ -4,22 +4,20 @@
4 4 import android.graphics.Bitmap;
5 5 import android.widget.ImageView;
6 6
7   -public interface ImageManager {
  7 +public interface ImageLoader {
8 8
9   - void load(String url, Context activity, ImageView imageView);
  9 + void load(ImageView imageView);
10 10
11   - void loadFromCacheOnly(String url, Context activity, ImageView imageView);
  11 + void loadFromCacheOnly(ImageView imageView);
12 12
13   - Bitmap getBitmap(String url, boolean scale);
14   -
15   - Bitmap getBitmap(String url);
  13 + Bitmap getBitmap(String url, int width, int height);
16 14
17 15 void deleteFileCache(Context context);
18 16
19   - void cleanCache();
20   -
21 17 void reduceFileCache(Context context);
22 18
  19 + void cleanCache();
  20 +
23 21 String getFilePath(String imageUrl);
24 22
25 23 }
62 core/src/main/java/com/novoda/imageloader/core/SettingsBuilder.java
... ... @@ -1,62 +0,0 @@
1   -package com.novoda.imageloader.core;
2   -
3   -import android.content.Context;
4   -import android.view.Display;
5   -import android.view.WindowManager;
6   -
7   -import com.novoda.imageloader.core.exception.MissingSettingException;
8   -import com.novoda.imageloader.core.file.FileUtil;
9   -
10   -public class SettingsBuilder {
11   -
12   - private Settings settings;
13   - private boolean isSizeSet = false;
14   -
15   - public SettingsBuilder() {
16   - settings = new Settings();
17   - }
18   -
19   - public SettingsBuilder imageSize(int height, int width) {
20   - settings.setImageHeight(height);
21   - settings.setImageWidth(width);
22   - isSizeSet = true;
23   - return this;
24   - }
25   -
26   - public SettingsBuilder defaultImageId(int defaultImageId) {
27   - settings.setDefaultImageId(defaultImageId);
28   - return this;
29   - }
30   -
31   - public SettingsBuilder fileNotFoundImageId(int fileNotFoundImageId) {
32   - settings.setNotFoundImageId(fileNotFoundImageId);
33   - return this;
34   - }
35   -
36   - public SettingsBuilder enableQueryInHashGeneration(boolean enableQueryInHashGeneration) {
37   - settings.setQueryIncludedInHash(enableQueryInHashGeneration);
38   - return this;
39   - }
40   -
41   - public Settings build(Context context){
42   - settings.setCacheDir(new FileUtil().prepareCacheDirectory(context));
43   - if(!isSizeSet) {
44   - setDisplayImageSize(context);
45   - }
46   - if(settings.getDefaultImageId() == -1) {
47   - throw new MissingSettingException("You need to set a default image id");
48   - }
49   - if(settings.getNotFoundImageId() == -1) {
50   - settings.setNotFoundImageId(settings.getDefaultImageId());
51   - }
52   - return settings;
53   - }
54   -
55   - private void setDisplayImageSize(Context context) {
56   - Display display = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE))
57   - .getDefaultDisplay();
58   - settings.setImageHeight(display.getHeight());
59   - settings.setImageWidth(display.getWidth());
60   - }
61   -
62   -}
15 core/src/main/java/com/novoda/imageloader/core/cache/BitmapCache.java
... ... @@ -0,0 +1,15 @@
  1 +package com.novoda.imageloader.core.cache;
  2 +
  3 +import android.graphics.Bitmap;
  4 +
  5 +public interface BitmapCache {
  6 +
  7 + boolean hasBitmap(String url);
  8 +
  9 + Bitmap get(String url);
  10 +
  11 + void put(String url, Bitmap bmp);
  12 +
  13 + void clean();
  14 +
  15 +}
23 core/src/main/java/com/novoda/imageloader/core/cache/ImageCache.java
... ... @@ -1,23 +0,0 @@
1   -package com.novoda.imageloader.core.cache;
2   -
3   -import android.graphics.Bitmap;
4   -
5   -public interface ImageCache {
6   -
7   - boolean hasImage(String url);
8   -
9   - Bitmap get(String url);
10   -
11   - void put(String url, Bitmap bmp);
12   -
13   - void clean();
14   -
15   - Bitmap getDefaultImage();
16   -
17   - void setDefaultImage(Bitmap defaultImage);
18   -
19   - Bitmap getNotFoundImage();
20   -
21   - void setNotFoundImage(Bitmap defaultImage);
22   -
23   -}
35 core/src/main/java/com/novoda/imageloader/core/cache/LruBitmapCache.java
... ... @@ -0,0 +1,35 @@
  1 +package com.novoda.imageloader.core.cache;
  2 +
  3 +import android.graphics.Bitmap;
  4 +
  5 +import com.android.camera.gallery.LruCache;
  6 +
  7 +public class LruBitmapCache implements BitmapCache {
  8 +
  9 + private LruCache<String, Bitmap> cache;
  10 +
  11 + public LruBitmapCache(int capacity) {
  12 + cache = new LruCache<String, Bitmap>(capacity);
  13 + }
  14 +
  15 + @Override
  16 + public boolean hasBitmap(String url) {
  17 + return cache.hasKey(url);
  18 + }
  19 +
  20 + @Override
  21 + public Bitmap get(String url) {
  22 + return cache.get(url);
  23 + }
  24 +
  25 + @Override
  26 + public void put(String url, Bitmap bmp) {
  27 + cache.put(url, bmp);
  28 + }
  29 +
  30 + @Override
  31 + public void clean() {
  32 + cache.clear();
  33 + }
  34 +
  35 +}
24 core/src/main/java/com/novoda/imageloader/core/cache/MapCache.java
@@ -4,14 +4,12 @@
4 4
5 5 import android.graphics.Bitmap;
6 6
7   -public class MapCache implements ImageCache {
  7 +public class MapCache implements BitmapCache {
8 8
9 9 private HashMap<String, Bitmap> cache = new HashMap<String, Bitmap>();
10   - private Bitmap defaultImage;
11   - private Bitmap notFoundImage;
12 10
13 11 @Override
14   - public boolean hasImage(String url) {
  12 + public boolean hasBitmap(String url) {
15 13 return cache.containsKey(url);
16 14 }
17 15
@@ -29,23 +27,5 @@ public void put(String url, Bitmap bmp) {
29 27 public void clean() {
30 28 cache.clear();
31 29 }
32   -
33   - @Override
34   - public Bitmap getDefaultImage() {
35   - return defaultImage;
36   - }
37   -
38   - @Override
39   - public void setDefaultImage(Bitmap defaultImage) {
40   - this.defaultImage = defaultImage;
41   - }
42   -
43   - public Bitmap getNotFoundImage() {
44   - return notFoundImage;
45   - }
46   -
47   - public void setNotFoundImage(Bitmap notFoundImage) {
48   - this.notFoundImage = notFoundImage;
49   - }
50 30
51 31 }
24 core/src/main/java/com/novoda/imageloader/core/cache/NoCache.java
@@ -2,10 +2,10 @@
2 2
3 3 import android.graphics.Bitmap;
4 4
5   -public class NoCache implements ImageCache {
  5 +public class NoCache implements BitmapCache {
6 6
7 7 @Override
8   - public boolean hasImage(String url) {
  8 + public boolean hasBitmap(String url) {
9 9 return false;
10 10 }
11 11
@@ -22,24 +22,4 @@ public void put(String url, Bitmap bmp) {
22 22 public void clean() {
23 23 }
24 24
25   - @Override
26   - public Bitmap getDefaultImage() {
27   - return null;
28   - }
29   -
30   - @Override
31   - public void setDefaultImage(Bitmap defaultImage) {
32   -
33   - }
34   -
35   - @Override
36   - public Bitmap getNotFoundImage() {
37   - return null;
38   - }
39   -
40   - @Override
41   - public void setNotFoundImage(Bitmap defaultImage) {
42   -
43   - }
44   -
45 25 }
26 core/src/main/java/com/novoda/imageloader/core/cache/SoftMapCache.java
@@ -5,14 +5,12 @@
5 5
6 6 import android.graphics.Bitmap;
7 7
8   -public class SoftMapCache implements ImageCache {
  8 +public class SoftMapCache implements BitmapCache {
9 9
10 10 private HashMap<String, SoftReference<Bitmap>> cache = new HashMap<String, SoftReference<Bitmap>>();
11   - private Bitmap defaultImage;
12   - private Bitmap notFoundImage;
13 11
14 12 @Override
15   - public boolean hasImage(String url) {
  13 + public boolean hasBitmap(String url) {
16 14 if (!cache.containsKey(url)) {
17 15 return false;
18 16 }
@@ -41,24 +39,4 @@ public void clean() {
41 39 cache.clear();
42 40 }
43 41
44   - @Override
45   - public Bitmap getDefaultImage() {
46   - return defaultImage;
47   - }
48   -
49   - @Override
50   - public void setDefaultImage(Bitmap defaultImage) {
51   - this.defaultImage = defaultImage;
52   - }
53   -
54   - @Override
55   - public Bitmap getNotFoundImage() {
56   - return notFoundImage;
57   - }
58   -
59   - @Override
60   - public void setNotFoundImage(Bitmap notFoundImage) {
61   - this.notFoundImage = notFoundImage;
62   - }
63   -
64 42 }
15 core/src/main/java/com/novoda/imageloader/core/exception/ImageCopyException.java
... ... @@ -0,0 +1,15 @@
  1 +package com.novoda.imageloader.core.exception;
  2 +
  3 +public class ImageCopyException extends Exception {
  4 +
  5 + private static final long serialVersionUID = 1L;
  6 +
  7 + public ImageCopyException(String message) {
  8 + super(message);
  9 + }
  10 +
  11 + public ImageCopyException(Throwable cause) {
  12 + super(cause);
  13 + }
  14 +
  15 +}
12 core/src/main/java/com/novoda/imageloader/core/file/FileUtil.java
@@ -10,6 +10,7 @@
10 10 import java.io.OutputStream;
11 11 import java.net.URL;
12 12
  13 +import com.novoda.imageloader.core.exception.ImageCopyException;
13 14 import com.novoda.imageloader.core.exception.ImageNotFoundException;
14 15
15 16 import android.content.Context;
@@ -21,6 +22,8 @@
21 22 private static final String DEFAULT_IMAGE_FOLDER_NAME = "/imagedata";
22 23 private static final String TAG = "ImageLoader";
23 24
  25 + private static final int BUFFER_SIZE = 6*1024;
  26 +
24 27 public void retrieveImage(String url, File f) {
25 28 InputStream is = null;
26 29 OutputStream os = null;
@@ -50,11 +53,10 @@ public void closeSilently(Closeable c) {
50 53 }
51 54
52 55 public void copyStream(InputStream is, OutputStream os) {
53   - final int bufferSize = 1024;
54 56 try {
55   - byte[] bytes = new byte[bufferSize];
  57 + byte[] bytes = new byte[BUFFER_SIZE];
56 58 for (;;) {
57   - int count = is.read(bytes, 0, bufferSize);
  59 + int count = is.read(bytes, 0, BUFFER_SIZE);
58 60 if (count == -1) {
59 61 break;
60 62 }
@@ -83,7 +85,7 @@ public boolean reduceFileCache(String cacheDirFullPath, long expirationPeriod) {
83 85 return true;
84 86 }
85 87
86   - public void copy(File src, File dst) {
  88 + public void copy(File src, File dst) throws ImageCopyException {
87 89 InputStream in = null;
88 90 OutputStream out = null;
89 91 try {
@@ -95,7 +97,7 @@ public void copy(File src, File dst) {
95 97 out.write(buf, 0, len);
96 98 }
97 99 } catch (IOException e) {
98   - throw new RuntimeException(e.getMessage());
  100 + throw new ImageCopyException(e);
99 101 } finally {
100 102 closeSilently(out);
101 103 closeSilently(in);
179 core/src/main/java/com/novoda/imageloader/core/loader/SingleThreadedImageLoader.java
... ... @@ -0,0 +1,179 @@
  1 +package com.novoda.imageloader.core.loader;
  2 +
  3 +import java.io.File;
  4 +
  5 +import android.Manifest;
  6 +import android.content.Context;
  7 +import android.content.Intent;
  8 +import android.content.pm.PackageManager;
  9 +import android.graphics.Bitmap;
  10 +import android.util.Log;
  11 +import android.widget.ImageView;
  12 +
  13 +import com.novoda.imageloader.core.ImageLoader;
  14 +import com.novoda.imageloader.core.cache.BitmapCache;
  15 +import com.novoda.imageloader.core.cache.SoftMapCache;
  16 +import com.novoda.imageloader.core.exception.ImageNotFoundException;
  17 +import com.novoda.imageloader.core.file.FileUtil;
  18 +import com.novoda.imageloader.core.model.ImageWrapper;
  19 +import com.novoda.imageloader.core.service.CacheCleaner;
  20 +import com.novoda.imageloader.core.util.BitmapDisplayer;
  21 +import com.novoda.imageloader.core.util.BitmapUtil;
  22 +import com.novoda.imageloader.core.util.Settings;
  23 +import com.novoda.imageloader.core.util.UrlUtil;
  24 +
  25 +public class SingleThreadedImageLoader implements ImageLoader {
  26 +
  27 + private static final String TAG = "ImageLoader";
  28 +
  29 + private UrlUtil urlUtil = new UrlUtil();
  30 + private BitmapUtil bitmapUtil = new BitmapUtil();
  31 + private SingleThreadedLoader singleThreadedLoader;
  32 + private Settings settings;
  33 + private BitmapCache cache;
  34 + private BitmapCache resBitmapCache = new SoftMapCache();
  35 +
  36 + public SingleThreadedImageLoader(Context c, Settings settings) {
  37 + this.settings = settings;
  38 + this.cache = createCache();
  39 + this.singleThreadedLoader = new SingleThreadedLoader() {
  40 + @Override
  41 + protected Bitmap loadMissingBitmap(ImageWrapper iw) {
  42 + return getBitmap(iw.getUrl(), iw.getWidth(), iw.getHeight());
  43 + }
  44 + @Override
  45 + protected void onBitmapLoaded(ImageWrapper iw, Bitmap bmp) {
  46 + new BitmapDisplayer(bmp, iw).runOnUiThread();
  47 + cache.put(iw.getUrl(), bmp);
  48 + }
  49 + };
  50 + verifyPermission(c, Manifest.permission.WRITE_EXTERNAL_STORAGE);
  51 + verifyPermission(c, Manifest.permission.INTERNET);
  52 + }
  53 +
  54 + private void verifyPermission(Context c, String permission) {
  55 + int p = c.getPackageManager().checkPermission(permission, c.getPackageName());
  56 + if(p == PackageManager.PERMISSION_DENIED) {
  57 + throw new RuntimeException("ImageLoader : please add the permission "
  58 + + permission + " to the manifest");
  59 + }
  60 + }
  61 +
  62 + @Override
  63 + public void load(ImageView imageView) {
  64 + ImageWrapper w = new ImageWrapper(imageView);
  65 + try {
  66 + if (cache.hasBitmap(w.getUrl())) {
  67 + Bitmap b = cache.get(w.getUrl());
  68 + if (b != null) {
  69 + imageView.setImageBitmap(b);
  70 + return;
  71 + }
  72 + }
  73 + setResource(w, w.getLoadingResourceId());
  74 + singleThreadedLoader.push(w);
  75 + } catch (ImageNotFoundException inf) {
  76 + setResource(w, w.getNotFoundResourceId());
  77 + } catch (Throwable t) {
  78 + setResource(w, w.getNotFoundResourceId());
  79 + }
  80 + }
  81 +
  82 + private void setResource(ImageWrapper w, int resId) {
  83 + String key = "resource" + resId + w.getHeight() + w.getHeight();
  84 + if(resBitmapCache.hasBitmap(key)) {
  85 + Bitmap b = resBitmapCache.get(key);
  86 + if(b != null) {
  87 + w.setBitmap(b);
  88 + return;
  89 + }
  90 + }
  91 + Bitmap b = bitmapUtil.scaleResourceBitmap(w, resId);
  92 + resBitmapCache.put(key, b);
  93 + w.setBitmap(b);
  94 + }
  95 +
  96 + @Override
  97 + public void loadFromCacheOnly(ImageView imageView) {
  98 + ImageWrapper w = new ImageWrapper(imageView);
  99 + try {
  100 + if (cache.hasBitmap(w.getUrl())) {
  101 + Bitmap b = cache.get(w.getUrl());
  102 + if (b != null) {
  103 + imageView.setImageBitmap(b);
  104 + return;
  105 + }
  106 + return;
  107 + }
  108 + setResource(w, w.getLoadingResourceId());
  109 + } catch (Throwable t) {
  110 + Log.e(TAG, t.getMessage(), t);
  111 + }
  112 + }
  113 +
  114 + @Override
  115 + public String getFilePath(String imageUrl) {
  116 + File f = getFile(imageUrl);
  117 + if (f.exists()) {
  118 + return f.getAbsolutePath();
  119 + }
  120 + return null;
  121 + }
  122 +
  123 + @Override
  124 + public Bitmap getBitmap(String url, int width, int height) {
  125 + if (url != null && url.length() >= 0) {
  126 + File f = getFile(url);
  127 + if (f.exists()) {
  128 + Bitmap b = bitmapUtil.decodeFileAndScale(f, width, height);
  129 + if (b != null) {
  130 + return b;
  131 + }
  132 + }
  133 + new FileUtil().retrieveImage(url, f);
  134 + return bitmapUtil.decodeFileAndScale(f, width, height);
  135 + }
  136 + return null;
  137 + }
  138 +
  139 + @Override
  140 + public void deleteFileCache(Context context) {
  141 + sendCacheCleanUpBroadcast(context, 0);
  142 + }
  143 +
  144 + @Override
  145 + public void reduceFileCache(Context context) {
  146 + long expirationPeriod = settings.getExpirationPeriod();
  147 + sendCacheCleanUpBroadcast(context, expirationPeriod);
  148 + }
  149 +
  150 + @Override
  151 + public void cleanCache() {
  152 + cache.clean();
  153 + }
  154 +
  155 + protected String processUrl(String url) {
  156 + if (!settings.isQueryIncludedInHash()) {
  157 + return url;
  158 + }
  159 + return urlUtil.removeQuery(url);
  160 + }
  161 +
  162 + protected BitmapCache createCache() {
  163 + return new SoftMapCache();
  164 + }
  165 +
  166 + private void sendCacheCleanUpBroadcast(Context context, long expirationPeriod) {
  167 + String path = settings.getCacheDir().getAbsolutePath();
  168 + Intent i = CacheCleaner.getCleanCacheIntent(path, expirationPeriod);
  169 + i.setPackage(context.getPackageName());
  170 + context.startService(i);
  171 + }
  172 +
  173 + private File getFile(String url) {
  174 + url = processUrl(url);
  175 + String filename = String.valueOf(url.hashCode());
  176 + return new File(settings.getCacheDir(), filename);
  177 + }
  178 +
  179 +}
131 core/src/main/java/com/novoda/imageloader/core/loader/SingleThreadedLoader.java
... ... @@ -0,0 +1,131 @@
  1 +package com.novoda.imageloader.core.loader;
  2 +
  3 +import java.util.ArrayList;
  4 +import java.util.List;
  5 +import java.util.Stack;
  6 +
  7 +import android.graphics.Bitmap;
  8 +import android.text.TextUtils;
  9 +import android.util.Log;
  10 +
  11 +import com.novoda.imageloader.core.exception.ImageNotFoundException;
  12 +import com.novoda.imageloader.core.model.ImageWrapper;
  13 +
  14 +public abstract class SingleThreadedLoader {
  15 +
  16 + private static final String TAG = "ImageLoader";
  17 +
  18 + private BitmapLoader thread = new BitmapLoader();
  19 + private Stack<ImageWrapper> stack = new Stack<ImageWrapper>();
  20 + private List<String> notFoundImages = new ArrayList<String>();
  21 +
  22 + public void push(ImageWrapper image) {
  23 + if (TextUtils.isEmpty(image.getUrl())) {
  24 + return;
  25 + }
  26 + pushOnStack(image);
  27 + startThread();
  28 + }
  29 +
  30 + public int size() {
  31 + synchronized (stack) {
  32 + return stack.size();
  33 + }
  34 + }
  35 +
  36 + public ImageWrapper pop() {
  37 + synchronized (stack) {
  38 + return stack.pop();
  39 + }
  40 + }
  41 +
  42 + protected abstract Bitmap loadMissingBitmap(ImageWrapper iw);
  43 +
  44 + protected abstract void onBitmapLoaded(ImageWrapper iw, Bitmap bmp);
  45 +
  46 + private void clean(ImageWrapper p) {
  47 + synchronized (stack) {
  48 + for (int j = 0; j < stack.size(); j++) {
  49 + if (stack.get(j).getUrl() != null && stack.get(j).getUrl().equals(p.getUrl())) {
  50 + stack.remove(j);
  51 + j--;
  52 + }
  53 + }
  54 + }
  55 + }
  56 +
  57 + private void pushOnStack(ImageWrapper p) {
  58 + synchronized (stack) {
  59 + stack.push(p);
  60 + }
  61 + }
  62 +
  63 + private class BitmapLoader extends Thread {