Android异步下载图片的类和缓存图片到SD卡的类。

最近因为以前写应用的网络图片读取都是另外启动一个线程去下载,现在感觉下载速度太慢了,而且也不方便管理。就打算写一个异步下载的,在网上找了一些大神们的博客看了。

开始自己动手写了。写了一个异步下载图片的类和一个缓存图片到SD卡的类。

首先是异步下载图片的类,用Android提供的LruCache来缓存图片非常方便,当使用的缓存接近最大时LruCache会自动释放掉使用次数最少的图片。需要了解LruCache更多的信息可以点这里

  1 /**
  2  * 
  3  * @ClassName ImageDownload
  4  * @Description 异步图片下载类,能异步多线程下载图片,最大为并发5线程。能将下载好的图片缓存在内存和SD卡中,
  5  *              等下次需要的时候就不用再下载,节省流量。
  6  * @author lan4627@gmail.com
  7  * @date 2014-3-27
  8  * @version V1.2
  9  */
 10 public class ImageDownload {
 11     private static final String TAG = ImageDownload.class.getSimpleName();
 12     private Context mContext;
 13     private LruCache<String, Bitmap> mLruCache;
 14     private ImageLoader imageLoader;
 15     private ImageCache imageCache;
 16 
 17     public ImageDownload(Context context) {
 18         mContext = context.getApplicationContext();
 19         // 用最大内存的八分之一来存图片
 20         int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
 21         int cache = maxMemory / 8;
 22         mLruCache = new LruCache<String, Bitmap>(cache) {
 23 
 24             protected int sizeOf(String key, Bitmap value) {
 25                 return (value.getRowBytes() * value.getHeight()) / 1024;
 26             }
 27 
 28         };
 29         // 初始化图片缓存类,用来把图片缓存到SD卡的文件夹里
 30         imageCache = new ImageCache(mContext);
 31     }
 32     
 33     private void addImageCache(String key, Bitmap bitmap) {
 34         if (getImage(key) == null) {
 35             mLruCache.put(key, bitmap);
 36         }
 37     }
 38 
 39     private Bitmap getImage(String key) {
 40         return mLruCache.get(key);
 41     }
 42 
 43     /**
 44      * 
 45      * @Method: loadBitmap
 46      * @Description: 读取图片的方法,先找缓存中看是否有,没有再找SD卡的缓存目录中是否有,没有再启动一个线程去下载图片。
 47      *               下载完成后返回图片,同时把下载好的图片储存在缓存和SD卡中,下次要用就不用再下载了。
 48      * @throws 49      * @param key
 50      *            需要下载的图片的URL
 51      * @param imageLoader
 52      *            回调接口
 53      * @return 返回一个Bitmap类型的图片
 54      */
 55     @TargetApi(Build.VERSION_CODES.HONEYCOMB)
 56     public Bitmap loadBitmap(String key, ImageLoader imageLoader) {
 57         Bitmap bitmap = getImage(key);
 58         if (bitmap != null) {
 59             return bitmap;
 60         }
 61         // 读取内置SD卡的缓存文件夹看有没有这张图片
 62         bitmap = imageCache.getCacheImage(key);
 63         if (bitmap != null) {
 64             addImageCache(key, bitmap);
 65             return getImage(key);
 66         }
 67         // 如果都没有,则启动线程去联网读取。
 68         this.imageLoader = imageLoader;
 69         if (android.os.Build.VERSION.SDK_INT > 10) {
 70             new ImageDownloadTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, key); // 当SDK版本大于10时,建立一个线程池为5的AsyncTask
 71         } else {
 72             new ImageDownloadTask().execute(key);
 73         }
 74         // 返回一个默认的占位图。
 75         return BitmapFactory.decodeResource(mContext.getResources(), R.drawable.bit_null);
 76     }
 77 
 78     public interface ImageLoader {
 79         public void onImage(String key, Bitmap bitmap);
 80     }
 81 
 82     class ImageDownloadTask extends AsyncTask<String, Integer, Bitmap> {
 83         private String key;
 84 
 85         protected Bitmap doInBackground(String... params) {
 86             Bitmap bitmap = null;
 87             InputStream is = null;
 88             try {
 89                 key = params[0];
 90                 URL url = new URL(key);
 91                 HttpURLConnection huc = (HttpURLConnection) url.openConnection();
 92                 // 设置请求方式
 93                 huc.setRequestMethod("GET");
 94                 // 设置读取的超时时间
 95                 huc.setReadTimeout(11000);
 96                 // 设置网络连接超时时间
 97                 huc.setConnectTimeout(4000);
 98                 // 拿到服务器返回的响应码
 99                 int hand = huc.getResponseCode();
100                 if (hand == 200) {
101                     // 读取图片
102                     is = huc.getInputStream();
103                     bitmap = BitmapFactory.decodeStream(is);
104                 } else {
105                     Log.e(TAG, "错误的服务器返回码" + hand);
106                 }
107             } catch (ProtocolException e) {
108                 Log.e(TAG, "不支持的连接方式");
109                 e.printStackTrace();
110             } catch (MalformedURLException e) {
111                 Log.e(TAG, "URL解析错误");
112                 e.printStackTrace();
113             } catch (IOException e) {
114                 Log.e(TAG, "连接打开错误或创建输入流错误");
115                 e.printStackTrace();
116             } finally {
117                 if (is != null) {
118                     try {
119                         is.close();
120                     } catch (IOException e) {
121                         Log.e(TAG, "InputStream关闭时发生错误");
122                         e.printStackTrace();
123                     }
124                 }
125             }
126             if (bitmap != null) {
127                 // 如果不等于空,则加入缓存中以及保存到SD卡。
128                 addImageCache(key, bitmap);
129                 imageCache.saveImage(key, bitmap);
130             }
131             return bitmap;
132         }
133         // 下载完成后通过实现好的接口设置图片
134         protected void onPostExecute(Bitmap bitmap) {
135             if (bitmap != null) {
136                 imageLoader.onImage(key, bitmap);
137             }
138         }
139 
140     }
141 }

一般都是在GridView和ListView的适配器里使用这个异步下载的工具。

 1 public class Test extends BaseAdapter {
 2     private ArrayList<HashMap<String, Object>> list;
 3     private LayoutInflater lInflater;
 4     private ListView listView;
 5     private ImageDownload imageDownload;
 6 
 7     public final class ListItemView {
 8         public ImageView icon;
 9     }
10 
11     public Test(Context context, ArrayList<HashMap<String, Object>> list, ListView listView) {
12         lInflater = LayoutInflater.from(context);
13         this.list = list;
14         this.listView = listView;
15         // 初始化图片下载类
16         imageDownload = new ImageDownload(context);
17     }
18 
19     @Override
20     public int getCount() {
21         return list.size();
22     }
23 
24     @Override
25     public Object getItem(int position) {
26         return list.get(position);
27     }
28 
29     @Override
30     public long getItemId(int position) {
31         return position;
32     }
33 
34     @Override
35     public View getView(int position, View convertView, ViewGroup parent) {
36         ListItemView listItem = null;
37         if (convertView == null) {
38             listItem = new ListItemView();
39             // 获取listItem视图
40             convertView = lInflater.inflate(R.layout.list_warinfo, null);
41             listItem.icon = (ImageView) convertView.findViewById(R.id.get_icon);
42             convertView.setTag(listItem);
43         } else {
44             listItem = (ListItemView) convertView.getTag();
45         }
46         // 获得要下载的图片网址
47         String url = (String)list.get(position).get("url");
48         // 设置标记
49         listItem.icon.setTag(url);
50         // 实现回调接口
51         ImageLoader imageLoader = new ImageLoader() {
52             
53             public void onImage(String key, Bitmap bitmap) {
54                 ImageView image = (ImageView) listView.findViewWithTag(key);
55                 if (image != null) {
56                     image.setImageBitmap(bitmap);
57                 }
58             }
59         };
60         Bitmap bitmap = imageDownload.loadBitmap(url, imageLoader);
61         listItem.icon.setImageBitmap(bitmap);
62         return convertView;
63     }
64 
65 }

下面是图片缓存类,会如果缓存文件夹过大了会自动清除,而且对外也提供了两个清除缓存文件夹的方法。

  1 /**
  2  * 
  3  * @ClassName ImageCache
  4  * @Description 图片缓存
  5  * @author lan4627@gmail.com
  6  * @date 2014-3-29
  7  * @version V1.0
  8  */
  9 public class ImageCache {
 10     private static final String TAG = ImageCache.class.getSimpleName();
 11     private Context context;
 12 
 13     /**
 14      * 
 15      * @Description: ImageCache初始化的时候会启动一个线程检查两个缓存文件夹的大小。
 16      *               如果SD卡内的缓存文件大于5MB,则自动删除里面的文件。 如果内部的缓存文件大于1MB,则自动删除里面的文件。
 17      * @param context
 18      *            上下文
 19      */
 20     public ImageCache(final Context context) {
 21         this.context = context.getApplicationContext();
 22         new Thread() {
 23             public void run() {
 24                 long externalSize = cacheDirSize(getExternalCacheDir());
 25                 long cacheSize = cacheDirSize(context.getCacheDir());
 26                 float size = Float.valueOf(externalSize) / (1024 * 1024);
 27                 if (size > 5) {
 28                     Log.v(TAG, "开始删除文件");
 29                     delete(getExternalCacheDir());
 30                 }
 31                 size = cacheSize / (1024 * 1024);
 32                 if (size > 1) {
 33                     Log.v(TAG, "开始删除文件");
 34                     delete(context.getCacheDir());
 35                 }
 36             }
 37 
 38         }.start();
 39     }
 40 
 41     /**
 42      * 
 43      * @Method: getCacheFile
 44      * @Description: 此方法用来获取缓存文件夹的路径。
 45      *                   如果SD卡是可用的状态,返回SD卡里的缓存文件夹。如果不可以则返回程序安装目录下的缓存文件夹。
 46      * @throws 47      * @return 返回缓存文件夹的路径。
 48      */
 49     private File getCacheFile() {
 50         File file = null;
 51         if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
 52             file = getExternalCacheDir();
 53         } else {
 54             file = context.getCacheDir();
 55         }
 56         return file;
 57     }
 58     /**
 59      * 
 60      * @Method: getExternalCacheDir 
 61      * @Description: 此方法根据不同的SDK版本来返回SD卡内的缓存文件夹路径。
 62      *                  Android2.1以上的版本返回/Android/data/包名/cache。
 63      *                  Android2.1及以下版本在SD卡根目录创建一个ImageCache文件夹。
 64      * @throws 65      * @return 返回SD卡里的缓存文件夹路径。
 66      */
 67     @SuppressLint("NewApi")
 68     private File getExternalCacheDir() {
 69         File file = null;
 70         if (android.os.Build.VERSION.SDK_INT > 7) {
 71             file = context.getExternalCacheDir();
 72         } else {
 73             file = new File(Environment.getExternalStorageDirectory() + "/ImageCache");
 74             // 如果目录不存在就创建一个
 75             if (!file.exists()) {
 76                 file.mkdir();
 77             }
 78         }
 79         return file;
 80     }
 81 
 82     /**
 83      * 
 84      * @Method: saveImage
 85      * @Description: 把下载的图片保存在内置SD卡的缓存文件夹中,下次要用的时候可以先检查缓存的文件夹里有没有,有的话就不用下载
 86      * @throws 87      * @param key
 88      *            图片的URL
 89      * @param bitmap
 90      *            图片
 91      */
 92     public boolean saveImage(String path, Bitmap bitmap) {
 93         // 把图片的下载地址作为保存的名字,需要把/和:替换掉,不然程序会认为这是一个目录。
 94         String fileName = path.replace("/", "_").replace(":", "&");
 95         File saveFile = new File(getCacheFile(), fileName);
 96         FileOutputStream fos = null;
 97         try {
 98             fos = new FileOutputStream(saveFile);
 99             return bitmap.compress(CompressFormat.JPEG, 70, fos);
100         } catch (FileNotFoundException e) {
101             Log.e(TAG, "缓存图片的文件夹路径不存在");
102             return false;
103         } finally {
104             if (fos != null) {
105                 try {
106                     fos.flush();
107                     fos.close();
108                 } catch (IOException e) {
109                     Log.e(TAG, "写入缓存图片的文件输出流关闭错误");
110                     e.printStackTrace();
111                 }
112             }
113         }
114     }
115 
116     /**
117      * 
118      * @Method: getCacheImage
119      * @Description: 从SD卡的缓存目录中读取图片
120      * @throws121      * @param key
122      *            图片的URL
123      * @return 返回一个Bitmap类型的图片, 如果缓存文件夹里无此图片则返回null。
124      */
125     @SuppressLint("NewApi")
126     public Bitmap getCacheImage(String path) {
127         String fileName = path.replace("/", "_").replace(":", "&");
128         Bitmap bitmap = null;
129         File cacheFile = new File(getCacheFile(), fileName);
130         FileInputStream fis = null;
131         try {
132             fis = new FileInputStream(cacheFile);
133             bitmap = BitmapFactory.decodeStream(fis);
134         } catch (FileNotFoundException e) {
135             Log.i(TAG, "缓存图片不存在");
136         } finally {
137             if (fis != null) {
138                 try {
139                     fis.close();
140                 } catch (IOException e) {
141                     Log.e(TAG, "读取缓存图片的文件输入流关闭错误");
142                     e.printStackTrace();
143                 }
144             }
145         }
146         return bitmap;
147     }
148 
149     /**
150      * 
151      * @Method: cacheDirSize
152      * @Description: 此方法用来计算文件夹的大小
153      * @throws154      * @param file
155      *            需要计算大小的文件夹
156      * @return 返回文件夹大小。一个long类型的数,单位为字节。
157      */
158     private long cacheDirSize(File file) {
159         long size = 0;
160         String[] files = file.list();
161         for (int i = 0; i < files.length; i++) {
162             size += new File(file, files[i]).length();
163         }
164         return size;
165     }
166 
167     /**
168      * 
169      * @Method: delete
170      * @Description: 此方法用来删除文件夹内的所有文件。
171      * @throws172      * @param file
173      *            需要删除内部所有文件的文件夹
174      */
175     private static synchronized void delete(File file) {
176         if (file != null && file.exists() && file.isDirectory()) {
177             String[] files = file.list();
178             for (int i = 0; i < files.length; i++) {
179                 new File(file, files[i]).delete();
180             }
181             Log.v(TAG, "删除完成");
182         }
183     }
184 
185     /**
186      * 
187      * @Method: deleteExternalCacheFile
188      * @Description: 删除SD卡缓存文件夹里的所有文件。
189      * @throws190      */
191     public void deleteExternalCacheFile() {
192         if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
193             delete(getExternalCacheDir());
194         }
195     }
196 
197     /**
198      * 
199      * @Method: deleteCacheFile
200      * @Description: 删除程序安装目录下的缓存文件夹里所有文件。
201      * @throws202      */
203     public void deleteCacheFile() {
204         File cacheFile = context.getCacheDir();
205         delete(cacheFile);
206     }
207 }
原文地址:https://www.cnblogs.com/zhengxt/p/3634979.html