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
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;
}
}; // 初始化
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币打赏给小的我吧~)
结束语:
我所了解的缓存到这里就说完了,后续肯定还会有补充,也希望你多帮我提一些好的想法。