本篇介绍Android中的三级缓存机制
 
参考网址:https://www.cnblogs.com/huangjie123/p/6130665.html
 
三级缓存指的是:内存缓存、本地缓存、网络缓存。
 
其各自的特点是内存缓存速度快, 优先读取,本地缓存速度其次, 内存没有,读本地,网络缓存速度最慢, 本地也没有,才访问网络。
 
对于网络缓存理解起来较为容易直接从网络中获取资源,本地缓存可以存在SD卡中,内存缓存一般存在数组或集合中。
 
下面我们从三部分来透析缓存机制
 
一、内存缓存
 
内存中读数据需要用到最近最少引用的算法(lrucache)。Lrucache算法要求为new LruCache<>() 传入一个分配给软件的最大内存,同时重写sizeof()方法,计算每一张图片的大小。
 
这样就可以直接调用LruCache的put()和get()方法。
 
二、本地缓存
 
当发现内存中没有数据时,找到SD卡中的存储文件。
 
通过Bitmap的compress()方法向文件夹中写数据,通过位图工厂BitmapFactory的decodeStream()读取数据,同时可以为decodeStream()方法传入options参数,缩小图片。
 
三、网络缓存
 
本地仍然没有获取数据,再从网络获取。
 
网络获取数据可以用异步任务来执行(耗时操作不能再主线程中执行)。
 
异步任务需要重写onPostExecute()方法和doInBackground()方法。
 
doInBackground()方法中访问网路,这里用到的是Httpurlconnection,通过连接得到输入流,利用位图工厂转换成位图,返回。onPostExecute()方法在doInBackground()方法执行后执行,传入的参数数doInBackground()方法的返回值。
 
代码调用和缓存工具类如下:
 
public class NextActivity extends AppCompatActivity{
    private ImageView iv;
    
@Override
    protected void onCreate(Bundle savedInstanceState) {
        
super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_next);
        getBitmap();
    }
    
private void getBitmap() {
        iv = (ImageView) findViewById(R.id.iv_next);
        CacheUtils utils = 
new CacheUtils();
        utils.display(iv,
"http://a.hiphotos.baidu.com/image/pic/item/a9d3fd1f4134970a489111e59ccad1c8a6865d1d.jpg");
    }
} 
布局就是一个ImageView,此处省略布局了。
 
下面我们看缓存工具类:
 
1、总工具类
 
import android.graphics.Bitmap;
import android.widget.ImageView;
import com.dota.musicapp.utils.L;
/**
 * 三级缓存的管理工具类
 * Created by ${Dota.Wang} on 2017/11/25.
 */
public class CacheUtils {
    private MemoryCacheUtils mMemoryCacheUtils;
    
private LocalCacheUtils mLocalCacheUtils;
    
private NetCacheUtils mNetCacheUtils;
    
public CacheUtils() {
        mMemoryCacheUtils = 
new MemoryCacheUtils();
        mLocalCacheUtils = 
new LocalCacheUtils();
        mNetCacheUtils = 
new NetCacheUtils(mMemoryCacheUtils, mLocalCacheUtils);
    }
    
public void display(ImageView imageView, String url) {
        
        Bitmap bitmap = mMemoryCacheUtils.getBitmapToMemory(url);
        
if (bitmap != 
null) {
            imageView.setImageBitmap(bitmap);
            L.i( 
"diaplay: 221111111111");
            
return;
        }
        
        bitmap = LocalCacheUtils.getBitmapToLoacl(url);
        
if (bitmap != 
null) {
            L.i( 
"diaplay: 1111111");
            imageView.setImageBitmap(bitmap);
            mMemoryCacheUtils.putBitmapToMemory(bitmap, url);
            
return;
        }
        
        mNetCacheUtils.getBitmapFromNet(imageView, url);
    }
} 
2、内存缓存工具类
 
import android.graphics.Bitmap;
import android.util.LruCache;
import com.dota.musicapp.utils.L;
/**
 * 第一级缓存:内存缓存的工具类
 * Created by ${Dota.Wang} on 2017/11/25.
 */
public class MemoryCacheUtils {
    private LruCache<String, Bitmap> mMemoryCache;
    
public MemoryCacheUtils() {
        
int maxmemory = (
int) Runtime.getRuntime().maxMemory();
        L.i(
"MemoryCacheUtils: " + maxmemory);
        mMemoryCache = 
new LruCache<String, Bitmap>(maxmemory / 
8) {
            
@Override
            protected int sizeOf(String key, Bitmap value) {
                
return value.getRowBytes() * value.getHeight();
            }
        };
    }
    
public void putBitmapToMemory(Bitmap bitmap, String url) {
        L.i( 
"putBitmapToMemory: ");
        mMemoryCache.put(url, bitmap);
    }
    
public Bitmap 
getBitmapToMemory(String url) {
        L.i(
"getBitmapToMemory: ");
        Bitmap bitmap = mMemoryCache.get(url);
        
return bitmap;
    }
} 
3、本地缓存工具类
 
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Environment;
import com.dota.musicapp.utils.L;
import com.dota.musicapp.utils.Md5Utils;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
/**
 * 第二级缓存:本地缓存(SD卡)的工具类
 * Created by ${Dota.Wang} on 2017/11/25.
 */
public class LocalCacheUtils {
    public static void putBitmapToLoacl(Bitmap bitmap, String url) {
        String encode = Md5Utils.encode(url);
        File file = 
new File(Environment.getExternalStorageDirectory(), encode);
        L.i(
"putBitmapToLoacl: " + file.toString());
        File parent = file.getParentFile();
        
if (!parent.exists()) {
            parent.mkdirs();
        }
        
try {
            bitmap.compress(Bitmap.CompressFormat.JPEG, 
100, 
new FileOutputStream(file));
        } 
catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }
    
public static Bitmap 
getBitmapToLoacl(String url) {
        String encode = Md5Utils.encode(url);
        File file = 
new File(Environment.getExternalStorageDirectory(), encode);
        
if (file.exists()) {
            BitmapFactory.Options opts = 
new BitmapFactory.Options();
            opts.inSampleSize = 
3;
            
try {
                Bitmap bitmap = BitmapFactory.decodeStream(
new FileInputStream(file), 
null, opts);
                
return bitmap;
            } 
catch (FileNotFoundException e) {
                e.printStackTrace();
                
return null;
            }
        }
        
return null;
    }
} 
4、网络缓存工具类
 
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.widget.ImageView;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
/**
 * 第三级缓存:网络缓存的工具类
 * Created by ${Dota.Wang} on 2017/11/25.
 */
public class NetCacheUtils {
    private MemoryCacheUtils mMemoryCacheUtils;
    
private LocalCacheUtils mLocalCacheUtils;
    
public NetCacheUtils(MemoryCacheUtils mMemoryCacheUtils, LocalCacheUtils mLocalCacheUtils) {
        
this.mLocalCacheUtils = mLocalCacheUtils;
        
this.mMemoryCacheUtils = mMemoryCacheUtils;
    }
    
public void getBitmapFromNet(ImageView imageView, String url) {
        imageView.setTag(url);
        Bitmaptask task = 
new Bitmaptask();
        task.execute(imageView, url);
    }
    class Bitmaptask extends AsyncTask<Object, Void, Bitmap> {
        
private HttpURLConnection urlConnection;
        
private ImageView imageView;
        
private String url;
        
@Override
        protected void onPostExecute(Bitmap bitmap) {
            
if (bitmap != 
null) {
                
if (url.equals(imageView.getTag())) {
                    imageView.setImageBitmap(bitmap);
                    System.out.print(
"onPostExecute");
                    mLocalCacheUtils.putBitmapToLoacl(bitmap, url);
                    mMemoryCacheUtils.putBitmapToMemory(bitmap, url);
                }
            }
        }
        
@Override
        protected Bitmap 
doInBackground(Object[] params) {
            imageView = (ImageView) params[
0];
            url = (String) params[
1];
            Bitmap bitmap = downloadFromNet(url);
            
return bitmap;
        }
        
private Bitmap 
downloadFromNet(String url) {
            
try {
                urlConnection = (HttpURLConnection) 
new URL(url).openConnection();
                urlConnection.setConnectTimeout(
5000);
                urlConnection.setRequestMethod(
"GET");
                
int responseCode = urlConnection.getResponseCode();
                
if (responseCode == 
200) {
                    InputStream inputStream = urlConnection.getInputStream();
                    BitmapFactory.Options opts = 
new BitmapFactory.Options();
                    opts.inSampleSize = 
2;
                    Bitmap bitmap = BitmapFactory.decodeStream(inputStream, 
null, opts);
                    
return bitmap;
                }
            } 
catch (IOException e) {
                e.printStackTrace();
                
return null;
            } 
finally {
                urlConnection.disconnect();
            }
            
return null;
        }
    }
} 
5、本地缓存中用到的Md5Utils工具类
 
import java.security.MessageDigest;
/**
 * Created by ${Dota.Wang} on 2017/11/25.
 */
public class Md5Utils {
    public static String 
encode(String pwd) {
        
try {
            MessageDigest digest = MessageDigest.getInstance(
"md5");
            
byte[] bs = digest.digest(pwd.getBytes());
            StringBuilder sb = 
new StringBuilder();
            
for (
byte b : bs) {
                
int number = b & 
0xff;
                String str = Integer.toHexString(number);
                
if (str.length() == 
1) {
                    sb.append(
"0");
                }
                sb.append(number);
            }
            
return sb.toString();
        } 
catch (Exception e) {
            e.printStackTrace();
            
return null;
        }
    }
}
 
6、最后,注意注册表中对权限的添加
 
<uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />