• 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 }
  • 相关阅读:
    解决Ubuntu下pycharm无法输入中文的问题
    爬取www.mmjpg.com网站图片,你懂得哦!
    批量查询ip地址归属地
    Opencv源码编译
    使用阿里云安装python模块
    Ansible运维自动化
    Mha-Atlas-MySQL高可用
    SVN
    Tomcat
    DHCP
  • 原文地址:https://www.cnblogs.com/zhengxt/p/3634979.html
Copyright © 2020-2023  润新知