Android--图片的三级缓存策略
三级缓存
缓存(内存)--->本地磁盘---->网络
1、首先看一下图片存储到本地磁盘
1 public class FileUtils { 2 3 String path;//文件存储的地方 4 public FileUtils(Context context,String dirName){//文件夹的名称 5 6 if(Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED){//SD卡就绪 7 //文件可以放在SD卡中 8 path = Environment.getExternalStorageDirectory().getAbsolutePath()+"/"+dirName; 9 }else{ 10 //文件放在内部存储器里 11 path = context.getCacheDir().getAbsolutePath()+"/"+dirName; 12 } 13 14 new File(path).mkdirs(); 15 } 16 17 //读取和写入 18 public void saveToSDCard(String key, Bitmap bmp){ 19 FileOutputStream fos = null; 20 try { 21 fos = new FileOutputStream(new File(path,key)); 22 23 } catch (FileNotFoundException e) { 24 e.printStackTrace(); 25 } 26 //保存固定格式图片,不压缩 27 bmp.compress(Bitmap.CompressFormat.PNG,100,fos); 28 29 try { 30 fos.close(); 31 } catch (IOException e) { 32 e.printStackTrace(); 33 } 34 } 35 //读取 36 public Bitmap readFromSDCard(String key){ 37 return BitmapFactory.decodeFile(new File(path,key).getAbsolutePath()); 38 } 39 }
2、图片加载,先加载缓存图片,缓存没有再到本地加载,本地没有再到网络加载
1 public class ImageLoader { 2 /** 3 * 缓存类 4 * LruCache原理:Cache保存一个强引用来限制内容数量。每当Item被访问的时候,此Item就会移动到队列的头部。 5 * 当cache已满的时候加入新的item时,在队列尾部的item会被回收。 6 * 解释:当超出指定内存值则移除最近最少用的图片内存 7 */ 8 private static LruCache<String,Bitmap> cache; 9 private FileUtils fileUtils; 10 //使用线程池 11 ExecutorService threadLooper; 12 13 public ImageLoader(Context context,String dirName){ 14 //获取系统分配的最大内存 15 cache = new LruCache<String,Bitmap>((int) (Runtime.getRuntime().maxMemory()/8)){ 16 //判断每一个键对应得值得大小 17 //自动释放使用频率低的文件 18 @Override 19 protected int sizeOf(String key, Bitmap value) { 20 return value.getByteCount();//也可以value.getWidth()*value.getHeight() 21 } 22 }; 23 fileUtils = new FileUtils(context,dirName); 24 threadLooper = Executors.newFixedThreadPool(5); 25 26 } 27 28 //存储到缓存 29 private void saveToCache(String key, Bitmap bmp){ 30 cache.put(key,bmp); 31 } 32 33 //从缓存中读取 34 private Bitmap readFromCache(String key){ 35 return cache.get(key); 36 } 37 38 public void loadImage(final String url, @NonNull final ImageLoadListener listener){ 39 40 final String key = url.replaceAll("[\\W]","");//非单次类型用空替换 41 //第一级,缓存 42 if(readFromCache(key)!=null){ 43 //缓存已存在,直接拿出来,但是,不是直接返回,因为后面还有请求网络,并不能确定什么时候返回。这里使用回调 44 listener.loadImageFinish(readFromCache(key)); 45 Log.e("TAG","从缓存中加载"); 46 }else { 47 //第二级,本地磁盘 48 Bitmap bitmap = fileUtils.readFromSDCard(key); 49 if(bitmap!=null){ 50 //存储到缓存 51 saveToCache(key,bitmap); 52 //返回 53 listener.loadImageFinish(fileUtils.readFromSDCard(key)); 54 Log.e("TAG","从SDCard中加载"); 55 }else{ 56 57 Log.e("TAG","从网络下载"); 58 final Handler handler = new Handler(){ 59 @Override 60 public void handleMessage(Message msg) { 61 super.handleMessage(msg); 62 listener.loadImageFinish((Bitmap) msg.obj); 63 } 64 }; 65 66 //下载,使用线程池 67 threadLooper.execute(new Runnable() { 68 @Override 69 public void run() { 70 //开始下载,子线程 71 try { 72 73 Bitmap bitmap1 = BitmapFactory.decodeStream(new URL(url).openStream()); 74 //保存到本地,保存到缓存 75 fileUtils.saveToSDCard(key,bitmap1); 76 saveToCache(key,bitmap1); 77 78 //使用异步任务通知主线程修改UI 79 Message msg = handler.obtainMessage(); 80 msg.obj = bitmap1; 81 handler.sendMessage(msg); 82 83 } catch (IOException e) { 84 e.printStackTrace(); 85 } 86 87 } 88 }); 89 90 } 91 } 92 } 93 94 public void cancelDowload(){ 95 threadLooper.shutdown(); 96 } 97 98 public interface ImageLoadListener{ 99 void loadImageFinish(Bitmap bitmap); 100 } 101 }
3、NetworkImageView自定义View,继承自ImageView
res/values/创建network_attrs.xml
1 <resources> 2 <declare-styleable name="NetworkImageView"> 3 <attr name="url" format="string"/> 4 </declare-styleable> 5 </resources>
1 public class NetworkImageView extends ImageView{ 2 3 String url; 4 ImageLoader loader; 5 6 public NetworkImageView(Context context) { 7 super(context); 8 } 9 10 public NetworkImageView(Context context, AttributeSet attrs) { 11 super(context, attrs); 12 //获取自定义属性 13 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.NetworkImageView); 14 url = a.getString(R.styleable.NetworkImageView_url); 15 a.recycle(); 16 loader = new ImageLoader(context,"network"); 17 if(url!=null){ 18 setUrl(url); 19 } 20 } 21 22 public String getUrl() { 23 return url; 24 } 25 26 public void setUrl(String url) { 27 this.url = url; 28 //设置到图片上去 29 loader.loadImage(url, new ImageLoader.ImageLoadListener() { 30 @Override 31 public void loadImageFinish(Bitmap bitmap) { 32 setImageBitmap(bitmap); 33 } 34 }); 35 } 36 }
4、测试
1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 xmlns:tools="http://schemas.android.com/tools" 3 xmlns:app="http://schemas.android.com/apk/res-auto" 4 android:id="@+id/activity_main" 5 android:layout_width="match_parent" 6 android:layout_height="match_parent" 7 android:orientation="vertical" 8 tools:context="com.example.lesson19_threecache.MainActivity"> 9 10 <Button 11 android:layout_width="match_parent" 12 android:layout_height="wrap_content" 13 android:onClick="start" 14 android:text="加载图片" /> 15 16 <ImageView 17 android:id="@+id/iv" 18 android:layout_width="wrap_content" 19 android:layout_height="wrap_content" 20 android:src="@mipmap/ic_launcher" /> 21 22 <com.example.mylibrary.widget.NetworkImageView 23 android:layout_width="wrap_content" 24 android:layout_height="wrap_content" 25 app:url="http://ent.chinadaily.com.cn/img/site1/20161117/448a5bd66b8519976e9933.jpeg"/> 26 </LinearLayout>
1 public class MainActivity extends AppCompatActivity { 2 3 ImageView iv; 4 ImageLoader loader; 5 @Override 6 protected void onCreate(Bundle savedInstanceState) { 7 super.onCreate(savedInstanceState); 8 setContentView(R.layout.activity_main); 9 iv = (ImageView) findViewById(R.id.iv); 10 loader = new ImageLoader(this,"three_cache"); 11 } 12 13 public void start(View v){ 14 15 loader.loadImage("http://image.cqcb.com/d/file/personage/2016-11-17/9b05cf89592d3e3eb2027519fbb98214.jpg", new ImageLoader.ImageLoadListener() { 16 @Override 17 public void loadImageFinish(Bitmap bitmap) { 18 iv.setImageBitmap(bitmap); 19 } 20 }); 21 }