Android缓存策略——打造最全的缓存

转载请注明出处:hao54216的博客

前言:

本篇介绍Android中的各种缓存机制和缓存框架,同样借鉴网上的一些知识总结分享给大家。


HR经常问到的缓存机制?

客户端缓存机制是android应用开发中非常重要的一项工作了,使用缓存机制不仅可以为客户节省流量,同时提高了用户体验,比如今日头条的离线模式,就是通过缓存机制实现的,那么缓存机制分为2种。文字缓存和多媒体文件缓存。


咱们先来说文字缓存:

1、将于服务器交互得到的json数据或者xml数据存入sd卡中,并在数据库添加该数据的记录。添加数据库记录时,可以提供2个字段,请求到的Url和本地保存后的文件地址,每次加载数据之前都会根据Url在数据库中检索。


ps:本段中提到存入sd卡,可能有的小伙伴不知道,这里说一下~


首先获取sdcard路径:

Environment.getExternalStorageDirectory()

判断sdcard状态:

Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)

保存到sd卡: filename 文件名 content内容

public void saveToSDCard(String filename,String content) throws Exception{  
        File file=new File(Environment.getExternalStorageDirectory(), filename);  
        OutputStream out=new FileOutputStream(file);  
        out.write(content.getBytes());  
        out.close();  
    }  
相关权限添加:
<!-- 在SDCard中创建于删除文件的权限 -->  
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>  
<!-- 往SDCard中写入数据的权限 -->  
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>  

2、将Json数据解析后装入List<Map>对象中,然后遍历List,将数据全部写入相应的数据库表结构中,以后每次想服务器发起请求之前可以现在数据库中检索,如果有直接返回。


ps:这里没有保存到文件,是先保存到list<Map<>>中,写入数据库,每次查询数据库获取数据;


之后就是多媒体_图片缓存了(本地缓存):

缓存图片可以根据当前日期、时间为名字缓存到sd卡中的指定图片缓存目录,同时数据库中左相应记录,记录办法可以采用俩个关键字段控制,一个字段是该图片的URL地址,另一个是该字段的图片本机地址,取图片时根据URL在数据中检索,如果没有则连接服务器下载,下载之后再服务器中做出相应记录。


ps:如何缓存到sd卡中呢?其实和文字缓存一样的思路;

/** 保存方法 */
 public void saveBitmap(Bitmap bm, String <span >picName</span>) {
  Log.e(TAG, "保存图片");
  File f = new File(<span >Environment.getExternalStorageDirectory()</span>, picName);//保存路径和图片名称(上文说的日期和时间可以作为)
  if (f.exists()) {
   f.delete();
  }
  try {
   FileOutputStream out = new FileOutputStream(f);
   bm.compress(Bitmap.CompressFormat.PNG, 90, out);
   out.flush();
   out.close();
   Log.i(TAG, "已经保存");
  } catch (FileNotFoundException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  } catch (IOException e) {
   // TODO Auto-generated catch block
   e.printStackTrace();
  }

 }
同样存入数据库,检索数据库。同时推荐我的另一篇文章: 点击打开链接


相关的图片框架:


1) Picasso

picasso-强大的Android图片下载缓存库

2) Glide

Google推荐的图片加载库Glide介绍

3) Fresco

Fresco使用教程

4) ImageLoader

Android UI-开源框架ImageLoader的完美例子


他们说的三级缓存:

内存缓存(从内存中获取图片显示)、本地缓存(内存中没有从sd卡获取)、网络缓存(从网络下载并保存入本地和内存);


ps:从sd卡获取图片是放在子线程里面执行的,否则快速滑屏的话不够流畅!



这里我想提一下Lrucache缓存:

核心的类是LruCache (此类在android-support-v4的包中提供) 。这个类非常适合用来缓存图片,

它的主要算法原理是把最近使用的对象用强引用存储在 LinkedHashMap 中,并且把最近最少

使用的对象在缓存值达到预设定值之前从内存中移除。


实现思路:

(1).要先设置缓存图片的内存大小,基本上设置为手机内存的1/8,
手机内存的获取方式:int MAXMEMONRY = (int) (Runtime.getRuntime() .maxMemory() / 1024);
(2).LruCache里面的键值对分别是URL和对应的图片;
(3).重写了一个叫做sizeOf的方法,返回的是图片数量。


它的使用:

1、初始化Lrucache

import android.app.ActivityManager;
import android.app.Application;
import android.content.Context;

/**
 * application
 * @author hao
 *
 */
public class MyApplication extends Application{
	
	@Override
	public void onCreate() {
		// TODO Auto-generated method stub
		super.onCreate();
	}
	/**
	 * @description 
	 *
	 * @param context
	 * @return 得到需要分配的缓存大小,这里用八分之一的大小来做
	 */
	public int getMemoryCacheSize() {
		// Get memory class of this device, exceeding this amount will throw an
		// OutOfMemory exception.
		final int memClass = ((ActivityManager)getSystemService(Context.ACTIVITY_SERVICE)).getMemoryClass();

		// Use 1/8th of the available memory for this memory cache.
		return 1024 * 1024 * memClass / 8;
	}

}
ps:这个方法可以说就是获取系统分配缓存的大小/8 说明我们用其中的八分之一来做缓存,建议配置在application里面,方便调用。

2、初始化类

final int memoryCache = ((KaleApplication) getApplication()).getMemoryCacheSize();
        Log.d(TAG, "cache size = " + memoryCache / 1024 / 1024 + "M");
        mMemoryCache = new LruCache<String, Bitmap>(memoryCache) {
            @Override
            protected int sizeOf(String key, Bitmap bitmap) {
                // 重写此方法来衡量每张图片的大小,默认返回图片数量。
                return bitmap.getByteCount() / 1024;
            }
        }; // 初始化


ps:获取全局配置的内存, 我通过缓存的值来初始化了cache对象,然后重写了sizeOf()方法。(返回数量)

3、添加删除和添加操作

    /**
     * @description 将bitmap添加到内存中去
     *
     * @param key
     * @param bitmap
     */
    public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
        if (getBitmapFromMemCache(key) == null) {
            mMemoryCache.put(key, bitmap);
        }
    }

    /**
     * @description 通过key来从内存缓存中获得bitmap对象
     *
     * @param key
     * @return
     */
    private Bitmap getBitmapFromMemCache(String key) {
        return mMemoryCache.get(key);
    }

4、从网络上缓存并添加入缓存

<span >	</span>/**
	 * @description 将bitmap加载到imageview中去
	 *
	 * @param resId
	 * @param imageView
	 */
	public void loadBitmapToImageView(String url, ImageView imageView) {
		final Bitmap bitmap = getBitmapFromMemCache("img"); // 先看这个资源在不在内存中,如果在直接读取为bitmap,否则返回null
		if (bitmap != null) {
			Log.d(TAG, "in memory");
			imageView.setImageBitmap(bitmap);
		} else {
			Log.d(TAG, "not in memory");
			imageView.setImageResource(R.drawable.ic_launcher); // 如果没有在内存中,先显示默认的图片,然后启动线程去下载图片
			BitmapWorkerTask task = new BitmapWorkerTask(imageView);
			task.execute(url); // 启动线程,从网络下载图片,下载后加入缓存
		}
	}

ps:判断图片是否已经进行了缓存操作。图片如果在内存中就直接赋值,没有启动线程重新获取。

import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.widget.ImageView;

public class BitmapWorkerTask extends AsyncTask<String, Void, Bitmap>{

	private MainActivity mActivity;
	private ImageView mImageView;
	
	public BitmapWorkerTask(ImageView imageView) {
		mImageView = imageView;
		mActivity = (MainActivity) imageView.getContext();
	}
	
	/**
	 * 下载图片
	 */
	@Override
	protected Bitmap doInBackground(String... params) {
		Bitmap bitmap = null;  
        HttpURLConnection con = null;  
        try {  
            URL url = new URL(params[0]);  
            con = (HttpURLConnection) url.openConnection();  
            con.setConnectTimeout(10 * 1000);  
            con.setReadTimeout(10 * 1000);  
            bitmap = BitmapFactory.decodeStream(con.getInputStream());  
            //添加到内存
            mActivity.addBitmapToMemoryCache("img", bitmap);
        } catch (MalformedURLException e) {  
            e.printStackTrace();  
        } catch (IOException e) {  
            e.printStackTrace();  
        } finally {  
            if (con != null) {  
                con.disconnect();  
            }  
        } 
		return bitmap;
	}
	
	@Override
	protected void onPostExecute(Bitmap result) {
		super.onPostExecute(result);
		if (result != null) {
			mImageView.setImageBitmap(result); 
		}
	}
}
ps: 得到后将bitmap放入缓存中,最后在imageview中展示。

让我们最后看一下效果吧!!!


点击下载图片,小机器人加载中,然后下载从网络下载成功!再次点击则不会加载,从内存中获取!

最后在这附上这个的源码下载 Lrucache内存缓存下载(这里1币打赏给小的我吧~)

结束语:

我所了解的缓存到这里就说完了,后续肯定还会有补充,也希望你多帮我提一些好的想法。