首先简介一下:
首先你第一次进一个App时,一定是先在网络请求图片的,之后我们可以将图片保存至本地SD卡和内存,再次进入时优先访问内存中的图片缓存,若内存中没有,则加载本地SD卡中的图片。
其中,内存缓存应优先加载,它速度最快;本地缓存次优先加载,它速度也快;网络缓存不应该优先加载,它走网络,速度慢且耗流量。(总的来说由快到慢)。
当然有很多小伙伴问为什么要使用三级缓存,因为android默认给每一个应用分配16M的内存,如果加载过多的图片的话,为了防止内存溢出(其实要区分一下内存泄漏的概念)。
为了搞这个三级缓存,搞得我头皮发麻,气得哦,一万只mmp飘过......
三级缓存有哪三级:
1、网络缓存 从网络获取资源(异步加载)网络缓存, 不优先加载, 速度慢,浪费流量
2、本地缓存 从本地获取数据(File存储)本地缓存, 次优先加载, 速度快
3、内存缓存 从内存获取数据(LruCache) 内存缓存, 优先加载, 速度最快
接下来是我自己玩法:
首先在布局文件定义两个控件,又创建个 BitmapUtils 工具类,
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//控件id
img1 = findViewById(R.id.img1);
img2 = findViewById(R.id.img2);
//调用工具类
final BitmapUtils bitmapUtils = new BitmapUtils(this);
bitmapUtils.display(path, img1);
//点击事件
img1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
bitmapUtils.display(path, img2);
}
});
}
BitmapUtils工具类写法:
public class BitmapUtils {
//分配一个Sd卡缓存路径-------->Sd卡用
private static final String SD_CACHE_DIR = Environment.getExternalStorageDirectory() + "/photo";
//制定一个图片缓存的内存大小------>内存用
int maxSize = (int) (Runtime.getRuntime().maxMemory() / 8);
//定义一个强引类型来缓存图片
private LruCache<String, Bitmap> images = new LruCache<String, Bitmap>(maxSize) {
@Override
protected int sizeOf(String key, Bitmap value) {
//返回缓存字节
return value.getByteCount();
}
};
Handler handler = new Handler() {
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what == 0) {
ImageViewBitmap imageViewBitmap = (ImageViewBitmap) msg.obj;
Bitmap bitmap = imageViewBitmap.bitmap;
ImageView img1 = imageViewBitmap.img1;
//修改控件上的图片
img1.setImageBitmap(bitmap);
}
}
};
//判断SD卡文件夹是否存在--->不存在创建
public BitmapUtils(MainActivity mainActivity) {
File file = new File(SD_CACHE_DIR);
if (!file.exists()) {
file.mkdir();
}
}
//加载图片做三级缓存
public void display(String path, ImageView img1) {
//先从内存取图片
Bitmap bitmap = getMemory(path);
if (bitmap != null) {
img1.setImageBitmap(bitmap);
Log.i("xxx", "走内存了");
} else {
//从sd取图片
bitmap = getSDcard(path);
if (bitmap != null) {
img1.setImageBitmap(bitmap);
Log.i("xxx", "走sd了");
} else {
//从网络取图片
getInternet(path, img1);
Log.i("xxx", "走网络了");
}
}
}
//内存获取图片
private Bitmap getMemory(String path) {
Bitmap bitmap = images.get(path);
if (bitmap != null) {
return bitmap;
}
return null;
}
//从SD卡获取图片
private Bitmap getSDcard(String path) {
String fileName = getFileName(path);
File file = new File(SD_CACHE_DIR, fileName);
//从sd取出来再缓存到内存
Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath());
if (bitmap != null) {
images.put(path, bitmap);
return bitmap;
}
return null;
}
//截取文件名存入Sd卡
private String getFileName(String path) {
//截取最后一个“/”后的一部分作为名字返回
return path.substring(path.lastIndexOf("/") + 1);
}
//网络获取图片
private void getInternet(String path, ImageView img1) {
//开启子线程
new Thread(new MyRunnabale(path, img1)).start();
}
//Runnabale内部类
private class MyRunnabale implements Runnable {
String path;
ImageView img1;
public MyRunnabale(String path, ImageView img1) {
this.path = path;
this.img1 = img1;
}
@Override
public void run() {
try {
//创建url
URL url = new URL(path);
//打开链接
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
//设置请求方式
conn.setRequestMethod("GET");
//设置延时
conn.setConnectTimeout(5000);
//判断状态码
if (conn.getResponseCode() == 200) {
//获取网络资源
InputStream inputStream = conn.getInputStream();
//创建字节数组输出流
Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
//缓存到SD卡--------------------------------------------------
String fileName = getFileName(path);
File file = new File(SD_CACHE_DIR, fileName);
FileOutputStream outputStream = new FileOutputStream(file);
BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(outputStream);
//Bitmap质量压缩---->jpeg格式---->压缩30%--->缓冲输出流
bitmap.compress(Bitmap.CompressFormat.JPEG, 70, bufferedOutputStream);
//关流
bufferedOutputStream.close();
outputStream.close();
inputStream.close();
//只要sd里面有图片,就不会走网络了,所以内存当中就没有图片
images.put(path, bitmap);
//把img控件,bitmap封装成一个对象
ImageViewBitmap imageViewBitmap = new ImageViewBitmap(bitmap, img1);
Message message = handler.obtainMessage(0, imageViewBitmap);
//发送消息
handler.sendMessage(message);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
private class ImageViewBitmap {
Bitmap bitmap;
ImageView img1;
public ImageViewBitmap(Bitmap bitmap, ImageView img1) {
this.bitmap = bitmap;
this.img1 = img1;
}
}
}