Android-单线程轮循机制批量下载图片、图片压缩、内存泄漏、图片缓存、文件缓存
2016-09-17 10:32
375 查看
ListView异步批量加载图片
单线程轮循机制批量下载图片
例如:imageloader框架底层也是这么实现的!思路:主线程向任务集合添加一个任务,工作线程从任务集合移除第一个任务,如果有任务一直移除,一直轮循,拿到图片之后解析成Bitmap,呈现的imageView当中。
package com.tarena.musicclient.adapter; import java.io.IOException; import java.lang.ref.SoftReference; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import org.apache.http.HttpEntity; import org.apache.http.util.EntityUtils; import com.tarena.musicclient.activity.R; import com.tarena.musicclient.entity.Music; import com.tarena.musicclient.util.BitmapUtils; import com.tarena.musicclient.util.GlobalConsts; import com.tarena.musicclient.util.HttpUtils; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Handler; import android.os.Message; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.ListView; import android.widget.TextView; public class MusicAdapter extends BaseAdapter{ private Context context; private List<Music> musics; private LayoutInflater inflater; //(2)声明任务集合 private List<ImageLoadTask> tasks=new ArrayList<ImageLoadTask>(); private ListView listView; //声明用于轮循任务集合的工作线程 private Thread workThread; //声明变量控制while循环 private boolean isLoop=true; //声明缓存集合 private HashMap<String, SoftReference<Bitmap>> cache =new HashMap<String, SoftReference<Bitmap>>(); //声明Handler 更新ImageView private Handler handler=new Handler(){ public void handleMessage(android.os.Message msg) { switch (msg.what) { case HANDLER_LOAD_IMAGE_SUCCESS: //给ImageView设置Bitmap ImageLoadTask task=(ImageLoadTask)msg.obj; //通过tag寻找到相应的ImageView ImageView ivAlum=(ImageView)listView.findViewWithTag(task.position); if(ivAlum!=null){ if(task.bitmap!=null){ ivAlum.setImageBitmap(task.bitmap); }else{ ivAlum.setImageResource(R.drawable.ic_launcher); } } break; } } }; public static final int HANDLER_LOAD_IMAGE_SUCCESS=0; public MusicAdapter(Context context, List<Music> musics, ListView listView) { this.context=context; this.musics=musics; this.listView=listView; inflater=LayoutInflater.from(context); //(5)MusicAdapter有几个,工作线程就有几个 //对workThread进行初始化 workThread=new Thread(){ public void run() { //(6)不断轮循任务集合 while(isLoop){ //如果任务集合不是空集 if(!tasks.isEmpty()){ //获取任务对象并且发送请求 ImageLoadTask task = tasks.remove(0); //通过路径返回Bitmap对象 Bitmap bitmap=loadBitmap(task.path); task.bitmap=bitmap; //给相应的ImageView设置Bitmap handler Message msg=new Message(); msg.what=HANDLER_LOAD_IMAGE_SUCCESS; msg.obj=task; handler.sendMessage(msg); }else{ //同步代码块:一但调用了某个对象的wait()/notify()方法后,外面一定套一个以这个对象为锁的同步代码块 synchronized (workThread) { //如果任务集合是空集 wait等待 try { workThread.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } } }; workThread.start(); } /** * 通过path 获取Bitmap * @param path * @return */ public Bitmap loadBitmap(String path){ Bitmap bitmap=null; String uri=GlobalConsts.BASEURL+path; try { //实体转成字节数组,字节数组转成图片 HttpEntity entity=HttpUtils.send(HttpUtils.METHOD_GET, uri, null); byte[] bytes=EntityUtils.toByteArray(entity); bitmap=BitmapUtils.loadBitmap(bytes, 50, 50); //向缓存集合中存储缓存图片 cache.put(path, new SoftReference<Bitmap>(bitmap)); } catch (IOException e) { e.printStackTrace(); } return bitmap; } @Override public int getCount() { return musics.size(); } public Object getItem(int position) { return musics.get(position); } @Override public long getItemId(int position) { return position; } public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder=null; if(convertView==null){ convertView=inflater.inflate(R.layout.item_lv_music, null); holder=new ViewHolder(); holder.ivAlbum=(ImageView)convertView.findViewById(R.id.imageView1); holder.tvName=(TextView)convertView.findViewById(R.id.tvName); holder.tvDuration=(TextView)convertView.findViewById(R.id.tvDuration); holder.tvSinger=(TextView)convertView.findViewById(R.id.tvSinger); holder.tvAuthor=(TextView)convertView.findViewById(R.id.tvAuthor); convertView.setTag(holder); } holder=(ViewHolder)convertView.getTag(); //给控件赋值 Music music=musics.get(position); holder.tvName.setText(music.getName()); holder.tvSinger.setText(music.getSinger()); holder.tvAuthor.setText(music.getAuthor()); holder.tvDuration.setText(music.getDurationtime()); //给当前item中的ImageView设置一个Tag //用于图片下载完成后根据该tag //找到相应的ImageView holder.ivAlbum.setTag(position); //查询缓存中是否有图片 如果有 则直接获取 SoftReference<Bitmap> ref=cache.get(music.getAlbumpic()); if(ref!=null && ref.get()!=null){ Bitmap bitmap=ref.get(); holder.ivAlbum.setImageBitmap(bitmap); System.out.println("从缓存中读取图片.."); return convertView; } //(3)下载一张图片 //向任务队列中添加一个图片任务 ImageLoadTask task=new ImageLoadTask(); //albumPic: images/xxxxx.jpg task.path=music.getAlbumpic(); task.position=position; tasks.add(task); //(4)唤醒工作线程 起来干活 notifyWorkThread(); return convertView; } /** * 唤醒工作线程 */ public void notifyWorkThread(){ //注意:一但调用wait活notify方法的时候,就要在这个同步代码块对象上加锁 synchronized (workThread) { workThread.notify(); } } //(1)创建图片下载任务类型的类 class ImageLoadTask{ String path;//图片路径 Bitmap bitmap;//图片对象 int position; } class ViewHolder{ ImageView ivAlbum; TextView tvName; TextView tvDuration; TextView tvSinger; TextView tvAuthor; } public void stopWorkThread() { isLoop=false; notifyWorkThread(); } }
图片压缩
图片压缩的意义:(像一些小图标,不需要很高清)例如我们写的上面的程序图片都是从服务器端下载下来的,一个图片大小是5k,但是这个5k的图片加载到内存里就不止是5k了,一个Bitmap肯定比5k要多(5k的十倍)50k,10张就500k(半兆),这样图片多了的话就非常浪费内存,占用内存!当占用内存十分大的时候,android 手机就会出现GC(垃圾回收机制),自动销毁堆内存中的垃圾。如果java管理的内存越来越满,快溢出了,GC执行频率会上升,导致效果:在GC执行垃圾回收的时候,所有的线程暂停,主线程也不准绘制UI!如果GC执行频率非常频繁,肯定会出现吊帧,卡顿的现象!
Bitmap图片(位图对象):
BitmapFactory 图片工厂:
Options封装了创建Bitmap对象时使用的参数
package com.tarena.musicclient.adapter; import java.io.IOException; import java.util.ArrayList; import java.util.List; import org.apache.http.HttpEntity; import org.apache.http.util.EntityUtils; import com.tarena.musicclient.activity.R; import com.tarena.musicclient.entity.Music; import com.tarena.musicclient.util.BitmapUtils; import com.tarena.musicclient.util.GlobalConsts; import com.tarena.musicclient.util.HttpUtils; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Handler; import android.os.Message; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.ListView; import android.widget.TextView; public class MusicAdapter extends BaseAdapter{ private Context context; private List<Music> musics; private LayoutInflater inflater; //声明任务集合 private List<ImageLoadTask> tasks=new ArrayList<ImageLoadTask>(); private ListView listView; //声明用于轮循任务集合的工作线程 private Thread workThread; //声明Handler 更新ImageView private Handler handler=new Handler(){ public void handleMessage(android.os.Message msg) { switch (msg.what) { case HANDLER_LOAD_IMAGE_SUCCESS: //给ImageView设置Bitmap ImageLoadTask task=(ImageLoadTask)msg.obj; //通过tag寻找到相应的ImageView ImageView ivAlum=(ImageView)listView.findViewWithTag(task.position); if(ivAlum!=null){ if(task.bitmap!=null){ ivAlum.setImageBitmap(task.bitmap); }else{ ivAlum.setImageResource(R.drawable.ic_launcher); } } break; } } }; public static final int HANDLER_LOAD_IMAGE_SUCCESS=0; public MusicAdapter(Context context, List<Music> musics, ListView listView) { this.context=context; this.musics=musics; this.listView=listView; inflater=LayoutInflater.from(context); //对workThread进行初始化 workThread=new Thread(){ public void run() { //不断轮循任务集合 while(true){ //如果任务集合不是空集 if(!tasks.isEmpty()){ //获取任务对象并且发送请求 ImageLoadTask task = tasks.remove(0); Bitmap bitmap=loadBitmap(task.path); task.bitmap=bitmap; //给相应的ImageView设置Bitmap handler Message msg=new Message(); msg.what=HANDLER_LOAD_IMAGE_SUCCESS; msg.obj=task; handler.sendMessage(msg); }else{ synchronized (workThread) { //如果任务集合是空集 wait等待 try { workThread.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } } }; workThread.start(); } /** * 通过path 获取Bitmap * @param path * @return */ public Bitmap loadBitmap(String path){ Bitmap bitmap=null; String uri=GlobalConsts.BASEURL+path; try { HttpEntity entity=HttpUtils.send(HttpUtils.METHOD_GET, uri, null); byte[] bytes=EntityUtils.toByteArray(entity); /** *图片压缩处理,工具类工具方法(传个来字节数字,和宽度、高度) **/ bitmap=BitmapUtils.loadBitmap(bytes, 50, 50); } catch (IOException e) { e.printStackTrace(); } return bitmap; } @Override public int getCount() { return musics.size(); } public Object getItem(int position) { return musics.get(position); } @Override public long getItemId(int position) { return position; } public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder=null; if(convertView==null){ convertView=inflater.inflate(R.layout.item_lv_music, null); holder=new ViewHolder(); holder.ivAlbum=(ImageView)convertView.findViewById(R.id.imageView1); holder.tvName=(TextView)convertView.findViewById(R.id.tvName); holder.tvDuration=(TextView)convertView.findViewById(R.id.tvDuration); holder.tvSinger=(TextView)convertView.findViewById(R.id.tvSinger); holder.tvAuthor=(TextView)convertView.findViewById(R.id.tvAuthor); convertView.setTag(holder); } holder=(ViewHolder)convertView.getTag(); //给控件赋值 Music music=musics.get(position); holder.tvName.setText(music.getName()); holder.tvSinger.setText(music.getSinger()); holder.tvAuthor.setText(music.getAuthor()); holder.tvDuration.setText(music.getDurationtime()); //给当前item中的ImageView设置一个Tag //用于图片下载完成后根据该tag //找到相应的ImageView holder.ivAlbum.setTag(position); //下载一张图片 //向任务队列中添加一个图片任务 ImageLoadTask task=new ImageLoadTask(); //albumPic: images/xxxxx.jpg task.path=music.getAlbumpic(); task.position=position; tasks.add(task); //唤醒工作线程 起来干活 notifyWorkThread(); return convertView; } /** * 唤醒工作线程,起来运行跳出循环 */ public void notifyWorkThread(){ synchronized (workThread) { workThread.notify(); } } class ImageLoadTask{ String path; Bitmap bitmap; int position; } class ViewHolder{ ImageView ivAlbum; TextView tvName; TextView tvDuration; TextView tvSinger; TextView tvAuthor; } }
图片压缩工具类
package com.tarena.musicclient.util; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.BitmapFactory.Options; public class BitmapUtils { /** * 通过字节数组解析出Bitmap对象 * 需要按照一定的宽高的规格进行压缩 * @param bytes 数据源 * @param width 图片的目标宽度 * @param height 图片的目标高度 * @return */ public static Bitmap loadBitmap(byte[] bytes, int width, int height) { Options opt=new Options(); //设置是否仅仅加载边界属性 opt.inJustDecodeBounds=true; BitmapFactory.decodeByteArray(bytes, 0, bytes.length, opt); //获取图片的原始的高度与宽度 int w=opt.outWidth/width; int h=opt.outHeight/height; int scale=w>h ? w : h; //设置压缩比例 opt.inSampleSize=scale; //再次进行解析Bitmap opt.inJustDecodeBounds=false; return BitmapFactory.decodeByteArray(bytes, 0, bytes.length, opt); } }
Android 内存泄漏
Android内存当中有垃圾对象,这些垃圾对象被占用了之后还不能被GC(垃圾回收机制回收)内存也不能重复利用,导致内存泄漏。GC垃圾回收机制:
项目中内存泄漏中的地方:
解决方法:
在Activity生命周期方法中销毁的时候关闭当前线程,详情看上方代码
内存泄漏最常见的情况:
Thread
new Thread (){}.start()必须给出run方法执行完毕的条件,在应用程序退出的时候销毁工作线程。
查看本程序中运行的线程
Java中的引用
强引用: StrongReferencee=new Emp();
就算出OOM,也不会销毁强引用
软引用: SoftReference
弱java管理的内存接近阈值,将会销毁一些软引用。释放内存
弱引用: WeakReference
就算java管理的内存没有接近阈值,就会销毁一些弱引用。
虚引用: …………
由GC管理的。
软引用的使用:
SoftReference s=new SoftReference(bitmap);
Bitmap bitmap=s.get();
Android图片的内存缓存
第一次把图片下载到手机后,存入内存中,再次需要下载图片时,先去内存中查询,如果已经下载过,那么直接获取,如果没有获取过,再去下载。存:
注意:
一直存会容易c内存溢出OOM
HashMap < String,Bitmap > cache ;
cache.put(“images/jsjs.jpg”,bitmap);
cache.put(“images/jsjs.jpg”,bitmap);
cache.put(“images/jsjs.jpg”,bitmap);
cache.put(“images/jsjs.jpg”,bitmap);
cache.put(“images/jsjs.jpg”,bitmap);
WeakHashMap:
弱引用,当java的内存达到阈值的时候,会随机删除键值对。
取:
Bitmap bitmap=cache.get(“images/jsjs.jpg”);
改进后:
HashMap < String,SoftReference> map;
map.put(path,new SoftReference(bitmap));
map.get(path).get();
代码参考上方例子
图片的文件缓存
(1)下载图片后,把图片数据保存到应用的缓存目录中。public void save(File targetFile,Bitmap bitmap)//工具类存的方法
Bitmap bitmap;
FileOutputStream fos;//写出到文件
file.write();
//缓存目录都存到cach文件中
bimap.compress();//压缩方法
(2)再次获取图片时,先去内存缓存中去查询,如果没有则去缓存目录中查询。有则直接读取。没有的话再次发送请求。
public Bitmap loadBitmap(path)//工具类方法:通过路径加载bitmap,也可以出入压缩的比例
String path;
BitmapFactory.decodeFile(path,Options);
文件缓存工具类方法:
package com.tarena.musicclient.util; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import android.graphics.Bitmap; import android.graphics.Bitmap.CompressFormat; import android.graphics.BitmapFactory; import android.graphics.BitmapFactory.Options; public class BitmapUtils { /** * 通过一个文件路径 查找到Bitmap * @param path * @return * @throws IOException */ public static Bitmap loadBitmap(String path)throws IOException{ File file=new File(path); if(file.exists()){ return BitmapFactory.decodeFile(path); } return null; } /*** * 保存图片 * @param targetFile 目标文件 * @param bitmap 位图对象 * @throws IOException */ public static void save(File targetFile, Bitmap bitmap)throws IOException{ if(!targetFile.getParentFile().exists()){ //父目录不存在 则创建 targetFile.getParentFile().mkdirs(); } if(!targetFile.exists()){ targetFile.createNewFile(); } //写入 FileOutputStream fos=new FileOutputStream(targetFile); bitmap.compress(CompressFormat.JPEG, 100, fos); } /** * 通过字节数组解析出Bitmap对象 * 需要按照一定的宽高的规格进行压缩 * @param bytes 数据源 * @param width 图片的目标宽度 * @param height 图片的目标高度 * @return */ public static Bitmap loadBitmap(byte[] bytes, int width, int height) { Options opt=new Options(); //设置是否仅仅加载边界属性 opt.inJustDecodeBounds=true; BitmapFactory.decodeByteArray(bytes, 0, bytes.length, opt); //获取图片的原始的高度与宽度 int w=opt.outWidth/width; int h=opt.outHeight/height; int scale=w>h ? w : h; //设置压缩比例 opt.inSampleSize=scale; //再次进行解析Bitmap opt.inJustDecodeBounds=false; return BitmapFactory.decodeByteArray(bytes, 0, bytes.length, opt); } }
使用:
package com.tarena.musicclient.adapter; import java.io.File; import java.io.IOException; import java.lang.ref.SoftReference; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import org.apache.http.HttpEntity; import org.apache.http.util.EntityUtils; import com.tarena.musicclient.activity.R; import com.tarena.musicclient.entity.Music; import com.tarena.musicclient.util.BitmapUtils; import com.tarena.musicclient.util.GlobalConsts; import com.tarena.musicclient.util.HttpUtils; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.Environment; import android.os.Handler; import android.os.Message; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.ListView; import android.widget.TextView; public class MusicAdapter extends BaseAdapter{ private Context context; private List<Music> musics; private LayoutInflater inflater; //声明任务集合 private List<ImageLoadTask> tasks=new ArrayList<ImageLoadTask>(); private ListView listView; //声明用于轮循任务集合的工作线程 private Thread workThread; //声明变量控制while循环 private boolean isLoop=true; //声明缓存集合 private HashMap<String, SoftReference<Bitmap>> cache =new HashMap<String, SoftReference<Bitmap>>(); //声明Handler 更新ImageView private Handler handler=new Handler(){ public void handleMessage(android.os.Message msg) { switch (msg.what) { case HANDLER_LOAD_IMAGE_SUCCESS: //给ImageView设置Bitmap ImageLoadTask task=(ImageLoadTask)msg.obj; //通过tag寻找到相应的ImageView ImageView ivAlum=(ImageView)listView.findViewWithTag(task.position); if(ivAlum!=null){ if(task.bitmap!=null){ ivAlum.setImageBitmap(task.bitmap); }else{ ivAlum.setImageResource(R.drawable.ic_launcher); } } break; } } }; public static final int HANDLER_LOAD_IMAGE_SUCCESS=0; public MusicAdapter(Context context, List<Music> musics, ListView listView) { this.context=context; this.musics=musics; this.listView=listView; inflater=LayoutInflater.from(context); //对workThread进行初始化 workThread=new Thread(){ public void run() { //不断轮循任务集合 while(isLoop){ //如果任务集合不是空集 if(!tasks.isEmpty()){ //获取任务对象并且发送请求 ImageLoadTask task = tasks.remove(0); Bitmap bitmap=loadBitmap(task.path); task.bitmap=bitmap; //给相应的ImageView设置Bitmap handler Message msg=new Message(); msg.what=HANDLER_LOAD_IMAGE_SUCCESS; msg.obj=task; handler.sendMessage(msg); }else{ synchronized (workThread) { //如果任务集合是空集 wait等待 try { workThread.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } } }; workThread.start(); } /** * 通过path 获取Bitmap * @param path * @return */ public Bitmap loadBitmap(String path){ Bitmap bitmap=null; String uri=GlobalConsts.BASEURL+path; try { HttpEntity entity=HttpUtils.send(HttpUtils.METHOD_GET, uri, null); byte[] bytes=EntityUtils.toByteArray(entity); bitmap=BitmapUtils.loadBitmap(bytes, 50, 50); //向缓存集合中存储缓存图片 cache.put(path, new SoftReference<Bitmap>(bitmap)); //向文件缓存中存储图片 //cacheDir: /data/data/com.xxx.client/cache/ //path: images/xxxx.jpg File targetFile=new File(context.getCacheDir(), path); BitmapUtils.save(targetFile, bitmap); } catch (IOException e) { e.printStackTrace(); } return bitmap; } @Override public int getCount() { return musics.size(); } public Object getItem(int position) { return musics.get(position); } @Override public long getItemId(int position) { return position; } public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder=null; if(convertView==null){ convertView=inflater.inflate(R.layout.item_lv_music, null); holder=new ViewHolder(); holder.ivAlbum=(ImageView)convertView.findViewById(R.id.imageView1); holder.tvName=(TextView)convertView.findViewById(R.id.tvName); holder.tvDuration=(TextView)convertView.findViewById(R.id.tvDuration); holder.tvSinger=(TextView)convertView.findViewById(R.id.tvSinger); holder.tvAuthor=(TextView)convertView.findViewById(R.id.tvAuthor); convertView.setTag(holder); } holder=(ViewHolder)convertView.getTag(); //给控件赋值 Music music=musics.get(position); holder.tvName.setText(music.getName()); holder.tvSinger.setText(music.getSinger()); holder.tvAuthor.setText(music.getAuthor()); holder.tvDuration.setText(music.getDurationtime()); //给当前item中的ImageView设置一个Tag //用于图片下载完成后根据该tag //找到相应的ImageView holder.ivAlbum.setTag(position); Bitmap bitmap=getCacheBitmap(music.getAlbumpic()); if(bitmap!=null){ holder.ivAlbum.setImageBitmap(bitmap); return convertView; } //下载一张图片 //向任务队列中添加一个图片任务 ImageLoadTask task=new ImageLoadTask(); //albumPic: images/xxxxx.jpg task.path=music.getAlbumpic(); task.position=position; tasks.add(task); //唤醒工作线程 起来干活 notifyWorkThread(); return convertView; } private Bitmap getCacheBitmap(String path){ //查询缓存中是否有图片 如果有 则直接获取 SoftReference<Bitmap> ref=cache.get(path); if(ref!=null && ref.get()!=null){ Bitmap bitmap=ref.get(); System.out.println("从缓存中读取图片.."); return bitmap; } //查询文件缓存 File targetFile=new File(context.getCacheDir(), path); Bitmap bitmap; try { bitmap = BitmapUtils.loadBitmap(targetFile.getAbsolutePath()); //如果文件中查到了bitmap //那么存入内存缓存 if(bitmap!=null){ System.out.println("从文件缓存中读取图片.."); cache.put(path, new SoftReference<Bitmap>(bitmap)); } return bitmap; } catch (IOException e) { e.printStackTrace(); } return null; } /** * 唤醒工作线程 */ public void notifyWorkThread(){ synchronized (workThread) { workThread.notify(); } } class ImageLoadTask{ String path; Bitmap bitmap; int position; } class ViewHolder{ ImageView ivAlbum; TextView tvName; TextView tvDuration; TextView tvSinger; TextView tvAuthor; } public void stopWorkThread() { isLoop=false; notifyWorkThread(); } }
相关文章推荐
- 技术篇-android ListView网络图片下载文件缓存
- 技术篇-android ListView网络图片下载文件缓存
- Android使用缓存机制实现文件下载及异步请求图片加三级缓存
- Android:ListView异步加载图片(实现网络下载、存储本地、缓存内存、压缩显示)
- android中图片加载使用LruCache缓存到内存或外部文件的功能
- Android 开发中的网络下载策略 与 文件缓存策略
- android异步下载图片缓存到sdcard
- Android异步从网络下载图片并且缓存图片到本地的demo
- Android图片批量压缩下载方法
- android下载网络图片并缓存
- Android 图片下载本地内存的缓存方式
- android的system.img文件的解压和压缩工具的下载地址
- Android 利用缓存机制实现文件下载
- Android异步从网络下载图片并且缓存图片到本地的demo
- Android 开发中的网络下载策略 与 文件缓存策略
- Android异步下载图片并且缓存图片到本地
- Android异步从网络下载图片并且缓存图片到本地的demo
- Android 图片异步下载及缓存--Multithreading For Performance
- Android项目实战-ListView异步图片加载及压缩缓存
- 提供下载:批量图片压缩(生成缩略图或指定大小图片)及文件批量改名器