首先跟大家介绍一下三级缓存,当然缓存的好处就是可以让用户体验更好,通过首次访问时将数据存储起来,以后需要就直接在本地获取数据,减少不必要的网络访问次数,也减少了流量的开销。但我们大部分应用都运用了此技术,所以三级缓存运用面比较广的。
所谓三级缓存其实是一种加载图片文件的策略,简单的来说就是 “内存——文件——网络”,顾名思义,当你需要加载一张图片,首先去内存中去取,原因是内存的响应速度最快,其次是外存(即SD卡),如果两者都没有你需要的图片,那就只有发送网络请求获取图片,但需要注意的是当网络获取到图片时首先存入SD卡中,当需要此图片时,户首先去缓存中去取,但是缓存还没有存入数据,接着去sd卡中去获取数据此时sd卡中已经有数据,在此时将sd卡中的数据存入缓存中去的同时将数据取出来使用。当下一次需要使用该图片时,就可以在缓存中直接获取图片了。
三级缓存想必大家已经有了一定的了解了下面来说说二次采样
二次采样使为了避免程序oom,当我们需要加载一张获取多张图片时,由于程序分配的内存有限,当图片过大过多时,内存就就会溢出,程序就直接over了,所以二次采样时为了优化程序。
我们来说说二次采样的原理,首先我们获取一张高清图片获取此图片的宽(sourceWidth)和高(sourceHeight),然后自己把需要的图片的宽(width)和高(height)通过参数缩放比率来进行调整,与原图进行比较,通过BitmapFactory内部的解码器参数的设定, 通过参数缩放(simpleSize)来不断调整大小,当有一条边和规定的边相等时,将边框绘制成功,第一次采样完成,第二次采样就是通过得到的边框来设置彩色格式RGB_565(如果想了解采样格式,可以看我的下一篇博客,里面有彩色格式的介绍),然后就采样好的图片解码填充边框。二次采样就完成了!
说了这么多下面就给大家展示一个小案例,看完案例你应该就明白了。
下面是布局文件
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="demo.liuchen.com.android25_lrucache.MainActivity">
<Button
android:id="@+id/btn_getImg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="Click"
android:text="获取图片"/>
<ImageView
android:id="@+id/image_Show"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"/>
</LinearLayout>
下面是MainActivity:
package demo.liuchen.com.android25_lrucache;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
import android.os.Message;
import android.support.v4.util.LruCache;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
/**
* 图片的三级缓存:LruCache--SDCard——Internet
*/
public class MainActivity extends AppCompatActivity {
// private String url = "http://p8.qhimg.com/t0113125382f3c4141a.png";
private String url = "http://t2.27270.com/uploads/tu/201606/32/k5xnewfzvz0.jpg";
private ImageView imageView;
private LruCache<String,Bitmap> lruCache;
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what==1){
Bitmap bitmap = (Bitmap) msg.obj;
imageView.setImageBitmap(bitmap);
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageView = (ImageView) findViewById(R.id.image_Show);
//获取运行时内存
Long MaxSize = Runtime.getRuntime().maxMemory();
//初始化LruCache
lruCache = new LruCache<String, Bitmap>((int) (MaxSize/8)){
//key 从内存中取出或存入的对象的名字
//value 存取的对象
//return 返回对象的缓存
@Override
protected int sizeOf(String key, Bitmap value) {
return value.getByteCount();
}
};
}
public void Click(View view) {
//当用户需要图片是先到缓存中去取
Bitmap bitmap = lruCache.get(getName(url));
if (bitmap!= null){
Log.e("TAG","在缓存中获取");
imageView.setImageBitmap(bitmap);
}else {
//在文件中查找
Bitmap bitmap1 = getBitmapFromSDCard(getName(url));
if (bitmap1!= null){
Log.e("TAG","在文件中获取");
imageView.setImageBitmap(bitmap1);
//如果图片在SD卡中找到,将图片放入缓存
Log.e("TAG","文件中存在放入缓存");
lruCache.put(getName(url),bitmap1);
}else {
getBitmapFromNet();
}
}
}
//从网络获取
private void getBitmapFromNet() {
Log.e("TAG","在网络中获取");
new Thread(new Runnable() {
@Override
public void run() {
try {
URL path = new URL(url);
HttpURLConnection httpURLConn = (HttpURLConnection) path.openConnection();
if (httpURLConn.getResponseCode() == 200){
final Bitmap bitmap = BitmapFactory.decodeStream(httpURLConn.getInputStream());
//将图片放入缓存中
lruCache.put(getName(url),bitmap);
//将bitmap存入当前文件的文件中
SaveBitmapToSDCard(getName(url),bitmap);
Message message = Message.obtain();
message.obj=1;
message.obj=bitmap;
handler.sendMessage(message);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
//将Bitmap存入sd卡中
private void SaveBitmapToSDCard(String name, Bitmap bitmap) {
BufferedOutputStream bos = null;
try {
bos = new BufferedOutputStream(new FileOutputStream(new File(getExternalCacheDir(),name)));
if (name.endsWith("png")||name.endsWith("PNG")){
//把bitmap存入SdCard中
Log.e("TAG","把bitmap存入SdCard中");
bitmap.compress(Bitmap.CompressFormat.PNG,100,bos);
}else {
bitmap.compress(Bitmap.CompressFormat.JPEG,100,bos);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
//从sd卡中获取图片
private Bitmap getBitmapFromSDCard(String name) {
// Log.e("TAG","从SD卡中获取图片");
//如果需要原图就可以不调用二次采用方法
// Bitmap bitmap = BitmapFactory.decodeFile(getExternalCacheDir().getAbsolutePath()+ File.separator+name);
//在SD卡中取出的Bitmap进行二次采样,传出去后会将二次采样好的图片放入缓存中
Bitmap bitmap = BitmapUtils.getBitmap(getExternalCacheDir().getAbsolutePath()+File.separator+name,100,100);
return bitmap;
}
//获取Bitmap为Url的名字
public String getName(String url){
String name = url.substring(url.lastIndexOf("/")+1,url.length());
return name;
}
}
下面是二次采样的封装类,方便调用。
package demo.liuchen.com.android25_lrucache;
/**
* Created by Administrator on 2016/9/23.
*/
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
/**
* Bitmap二次采样工具类
*/
public class BitmapUtils {
/**
*
* @param filePath bitmap的路径
* @param destWidth 希望采样后Bitmap的宽
* @param destHeight 希望采样后Bitmap的高
*
* @return 只有边框的bitmap
*/
public static Bitmap getBitmap(String filePath, int destWidth, int destHeight) {
//第一采样
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(filePath, options);
int outWidth = options.outWidth;
int outHeight = options.outHeight;
int sampleSize = 1;
while ((outWidth / sampleSize > destWidth) || (outHeight / sampleSize > destHeight)) {
sampleSize *= 2;
}
//第二次采样
options.inJustDecodeBounds = false;
options.inSampleSize = sampleSize;
options.inPreferredConfig = Bitmap.Config.RGB_565;
return BitmapFactory.decodeFile(filePath, options);
}
}
}
//获取Bitmap为Url的名字
public String getName(String url){
String name = url.substring(url.lastIndexOf("/")+1,url.length());
return name;
}
}
//当然访问了网路和sd卡记得在Manifest清单文件加上权限:
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
//好了案例就完成了,如果有问题欢迎评论和指正,也可以加好友一起交流学习。
下面是以上案例的代码需要的可以自行下载:http://download.csdn.net/detail/mr_condingson/9765051