三级缓存机制:
内存缓存:优先加载,速度快,内存缓存中android给每一个app分配16m的内存。java中的引用是强引用,垃圾回收器不会回收。java中的引用默认都是强引用。软引用(SoftReference):java的垃圾回收机制会回收,内存不够的时候,垃圾回收器会考虑回收;弱引用(weakReference)在内存不够的时候,垃圾回收器会优先回收,虚引用(PhantomRefence)内存不够的时候,垃圾回收器会最先回收。
现在谷歌不推荐使用软引用,使用软引用缓存的时候,经常不是从内存中读取,而是从本地读取了,现在谷歌推荐使用LruCache来缓存,最少使用算法,哪一个使用的最少据回收哪一个,其将内存控制在一定的赌大小,超过这个大小会自动回收,这个大小是由开发者来设置。
sd卡(本地)缓存:次优先加载
网络缓存:最后考虑加载,速度慢,耗费流量,
图片的压缩处理
//图片压缩处理
BitmapFactory.Options option = new BitmapFactory.Options();
//宽高都压缩为原来的二分之一, 此参数需要根据图片要展示的大小来确定
option.inSampleSize = 2;
//设置图片格式
option.inPreferredConfig = Bitmap.Config.RGB_565;
//获取到bitmap对象
Bitmap bitmap = BitmapFactory.decodeStream(inputStream, null, option);
软引用的示例MyBitmapUtils.java
package com.ldw.beijing.utils.bitmap;
import android.graphics.Bitmap;
import android.widget.ImageView;
import com.ldw.beijing.R;
/*
* 自定义图片加载工具
*/
public class MyBitmapUtils {
//网络缓存
NetCacheUtils mNetCacheUtils;
//本地缓存
LocalCacheUtils mLocalCacheUtils;
//内存缓存
MemoryCacheUtils mMemoryCacheUtils;
//构造函数,一开始是从网络获取数据
public MyBitmapUtils(){
mMemoryCacheUtils = new MemoryCacheUtils();
mLocalCacheUtils = new LocalCacheUtils();
mNetCacheUtils = new NetCacheUtils(mLocalCacheUtils, mMemoryCacheUtils);
}
public void display(ImageView iv_pic, String url) {
System.out.println("url===" + url);
System.out.println("iv_pic===" + iv_pic);
// 设置默认加载图片
iv_pic.setImageResource(R.drawable.news_pic_default);
Bitmap bitmap = null;
// 从内存读取
bitmap = mMemoryCacheUtils.getBitmapFromMemory(url);
//bitmap不是空,就从内存中读取图片
if(bitmap != null){
iv_pic.setImageBitmap(bitmap);
System.out.println("从内存读取图片...");
return;
}
//从本地读取
bitmap = mLocalCacheUtils.getBitmapFromLocal(url);
//bitmap存在
if (bitmap != null) {
iv_pic.setImageBitmap(bitmap);
System.out.println("从本地读取图片...");
//将图片保存在内存
mMemoryCacheUtils.setBitmapToMemory(url, bitmap);
return;
}
//从网络读取
mNetCacheUtils.getBitmapFromNet(iv_pic, url);
}
}
三级缓存的示例
在PhotoMenuDetailPager.java中需要缓存图片,之前使用的是xUtils的BitmapUtils方法进行缓存的现在使用三级缓存,使用MyBitmapUtils作为接口来实现
package com.ldw.beijing.base.menudetails;
import java.util.ArrayList;
import android.app.Activity;
import android.text.TextUtils;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.GridView;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import com.google.gson.Gson;
import com.ldw.beijing.R;
import com.ldw.beijing.base.BaseMenuDetailPager;
import com.ldw.beijing.domain.PhotosData;
import com.ldw.beijing.domain.PhotosData.PhotoInfo;
import com.ldw.beijing.global.ClobalContants;
import com.ldw.beijing.utils.CacheUtils;
import com.ldw.beijing.utils.bitmap.MyBitmapUtils;
import com.lidroid.xutils.BitmapUtils;
import com.lidroid.xutils.HttpUtils;
import com.lidroid.xutils.exception.HttpException;
import com.lidroid.xutils.http.ResponseInfo;
import com.lidroid.xutils.http.callback.RequestCallBack;
import com.lidroid.xutils.http.client.HttpRequest.HttpMethod;
/*
* 菜单详情页-组图
*/
public class PhotoMenuDetailPager extends BaseMenuDetailPager {
private ArrayList<PhotoInfo> mPhotoList;
private PhotoAdapter mAdapter;
private ImageButton btn_photo;
private ListView lv_photo;
private GridView gv_photo;
public PhotoMenuDetailPager(Activity activity, ImageButton btn_photo) {
super(activity);
//获取到切换的对象
this.btn_photo = btn_photo;
btn_photo.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v) {
// 按钮点击的时候切换展示方式
changeDisplay();
}
});
}
@Override
public View initView() {
View view = View.inflate(mActivity, R.layout.menu_photo_pager, null);
lv_photo = (ListView) view.findViewById(R.id.lv_photo);
gv_photo = (GridView) view.findViewById(R.id.gv_photo);
return view;
}
@Override
public void initData() {
//读取缓存
String cache = CacheUtils
.getCache(ClobalContants.PHOTOS_URL, mActivity);
if (!TextUtils.isEmpty(cache)) {
}
//服务器中读取数据
getDataFromServer();
}
private void getDataFromServer() {
HttpUtils utils = new HttpUtils();
utils.send(HttpMethod.GET, ClobalContants.PHOTOS_URL,
new RequestCallBack<String>() {
@Override
public void onSuccess(ResponseInfo<String> responseInfo) {
String result = (String) responseInfo.result;
//解析json数据
parseData(result);
// 设置缓存
CacheUtils.setCache(ClobalContants.PHOTOS_URL, result,
mActivity);
}
@Override
public void onFailure(HttpException error, String msg) {
Toast.makeText(mActivity, msg, Toast.LENGTH_SHORT)
.show();
error.printStackTrace();
}
});
}
//解析json数据
protected void parseData(String result) {
Gson gson = new Gson();
PhotosData data = gson.fromJson(result, PhotosData.class);
mPhotoList = data.data.news;// 获取组图列表集合
if (mPhotoList != null) {
mAdapter = new PhotoAdapter();
lv_photo.setAdapter(mAdapter);
gv_photo.setAdapter(mAdapter);
}
}
class PhotoAdapter extends BaseAdapter{
//private BitmapUtils utils;
private MyBitmapUtils utils;
//构造函数
public PhotoAdapter() {
utils = new MyBitmapUtils();
/*
utils = new BitmapUtils(mActivity);
//默认加载图片
utils.configDefaultLoadingImage(R.drawable.news_pic_default);
*/
}
@Override
public int getCount() {
// TODO Auto-generated method stub
return mPhotoList.size();
}
@Override
public PhotoInfo getItem(int position) {
// TODO Auto-generated method stub
return mPhotoList.get(position);
}
@Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = View.inflate(mActivity, R.layout.list_photo_item,
null);
holder = new ViewHolder();
holder.tv_title = (TextView) convertView
.findViewById(R.id.tv_title);
holder.iv_pic = (ImageView) convertView
.findViewById(R.id.iv_pic);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
PhotoInfo item = getItem(position);
//设置标题
holder.tv_title.setText(item.title);
//显示图片
utils.display(holder.iv_pic, item.listimage);
return convertView;
}
}
static class ViewHolder {
public TextView tv_title;
public ImageView iv_pic;
}
// 是否是列表展示
private boolean isListDisplay = true;
/*
* 切换展现方式
*/
private void changeDisplay() {
//当前是listView展示的时候需要切换到GridView展示
if (isListDisplay) {
isListDisplay = false;
lv_photo.setVisibility(View.GONE);
gv_photo.setVisibility(View.VISIBLE);
//修改图标的样式
btn_photo.setImageResource(R.drawable.icon_pic_list_type);
} else {
isListDisplay = true;
lv_photo.setVisibility(View.VISIBLE);
gv_photo.setVisibility(View.GONE);
//修改图标的样式为GridView
btn_photo.setImageResource(R.drawable.icon_pic_grid_type);
}
}
}
三级缓存的接口MyBitmapUtils.java,首先是考虑从内存中读取,然后考虑从本地,最后从网络
package com.ldw.beijing.utils.bitmap;
import android.graphics.Bitmap;
import android.widget.ImageView;
import com.ldw.beijing.R;
/*
* 自定义图片加载工具
*/
public class MyBitmapUtils {
//网络缓存
NetCacheUtils mNetCacheUtils;
//本地缓存
LocalCacheUtils mLocalCacheUtils;
//内存缓存
MemoryCacheUtils mMemoryCacheUtils;
//构造函数,一开始是从网络获取数据
public MyBitmapUtils(){
mMemoryCacheUtils = new MemoryCacheUtils();
mLocalCacheUtils = new LocalCacheUtils();
mNetCacheUtils = new NetCacheUtils(mLocalCacheUtils, mMemoryCacheUtils);
}
public void display(ImageView iv_pic, String url) {
System.out.println("url===" + url);
System.out.println("iv_pic===" + iv_pic);
// 设置默认加载图片
iv_pic.setImageResource(R.drawable.news_pic_default);
Bitmap bitmap = null;
// 从内存读取
bitmap = mMemoryCacheUtils.getBitmapFromMemory(url);
//bitmap不是空,就从内存中读取图片
if(bitmap != null){
iv_pic.setImageBitmap(bitmap);
System.out.println("从内存读取图片...");
return;
}
//从本地读取
bitmap = mLocalCacheUtils.getBitmapFromLocal(url);
//bitmap存在
if (bitmap != null) {
iv_pic.setImageBitmap(bitmap);
System.out.println("从本地读取图片...");
//将图片保存在内存
mMemoryCacheUtils.setBitmapToMemory(url, bitmap);
return;
}
//从网络读取
mNetCacheUtils.getBitmapFromNet(iv_pic, url);
}
}
网络缓存NetCacheUtils.java,图片加载的时候首先是从网络获取,然后再缓存在本地和内存中
package com.ldw.beijing.utils.bitmap;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.widget.ImageView;
/*
* 网络缓存数据
*/
public class NetCacheUtils {
private LocalCacheUtils mLocalCacheUtils;
private MemoryCacheUtils mMemoryCacheUtils;
public NetCacheUtils(LocalCacheUtils localCacheUtils, MemoryCacheUtils memoryCacheUtils) {
mLocalCacheUtils = localCacheUtils;
mMemoryCacheUtils = memoryCacheUtils;
}
/*
* 从网络加载图片,需要异步加载
*/
public void getBitmapFromNet(ImageView iv_pic, String url) {
//开启AsyncTask,参数会在doInBackground中获取
new BitmapTask().execute(iv_pic, url);
}
/*
* 异步加载,AsyncTask就是handler和线程池的封装
* 第一个泛型: 参数类型 第二个泛型: 更新进度的泛型, 第三个泛型是onPostExecute的返回结果
*/
class BitmapTask extends AsyncTask<Object, Void, Bitmap>{
/*
* 后台的耗时方法,子线程中运行
*/
private ImageView iv_pic;
private String url;
@Override
protected Bitmap doInBackground(Object... params) {
//获取到传递的参数
iv_pic = (ImageView) params[0];
url = (String)params[1];
// 将url和imageview绑定
iv_pic.setTag(url);
return downloadBitmap(url);
}
/*
* 更新进度,需要更新进度条, 主线程
*/
@Override
protected void onProgressUpdate(Void... values) {
super.onProgressUpdate(values);
}
/*
* 耗时方法结束以后,执行该方法,主线程中运行
*/
@Override
protected void onPostExecute(Bitmap result){
if (result != null) {
//获取到和imageview绑定的url
String bindUrl = (String) iv_pic.getTag();
//判断绑定的url和当前的url是不是一致的
if (url.equals(bindUrl)) {
// 确保图片设定给了正确的imageview
iv_pic.setImageBitmap(result);
//图片下载完成以后往本地保存
mLocalCacheUtils.setBitmapToLocal(url, result);// 将图片保存在本地
//将图片内存缓存
mMemoryCacheUtils.setBitmapToMemory(url, result);
System.out.println("从网络缓存读取图片...");
}
}
}
}
/*
* 下载图片
*/
private Bitmap downloadBitmap(String url) {
HttpURLConnection conn = null;
try {
conn = (HttpURLConnection) new URL(url).openConnection();
conn.setConnectTimeout(5000);
conn.setReadTimeout(5000);
conn.setRequestMethod("GET");
conn.connect();
int responseCode = conn.getResponseCode();
if (responseCode == 200) {
InputStream inputStream = conn.getInputStream();
//图片压缩处理
BitmapFactory.Options option = new BitmapFactory.Options();
//宽高都压缩为原来的二分之一, 此参数需要根据图片要展示的大小来确定
option.inSampleSize = 2;
//设置图片格式
option.inPreferredConfig = Bitmap.Config.RGB_565;
//获取到bitmap对象
Bitmap bitmap = BitmapFactory.decodeStream(inputStream, null, option);
return bitmap;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//关闭流
conn.disconnect();
}
return null;
}
}
本地缓存LocalCacheUtils.java,本地缓存首先需要缓存在
package com.ldw.beijing.utils.bitmap;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import com.ldw.beijing.utils.MD5Encoder;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.graphics.BitmapFactory;
import android.os.Environment;
/*
* 本地缓存
*/
public class LocalCacheUtils {
//本地缓存的地址
public static final String CACHE_PATH = Environment
.getExternalStorageDirectory().getAbsolutePath() + "/news_cache_data";
/*
* 从本地sdcard读图片
*/
public Bitmap getBitmapFromLocal(String url) {
try {
//得到图片url的md5值
String fileName = MD5Encoder.encode(url);
//创建一个文件夹
File file = new File(CACHE_PATH, fileName);
//判断文件夹是否存在
if (file.exists()) {
Bitmap bitmap = BitmapFactory.decodeStream(new FileInputStream(
file));
return bitmap;
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/*
* 向sdcard写图片,保存图片到sd卡
*/
public void setBitmapToLocal(String url, Bitmap bitmap) {
try {
//得到图片url的md5值
String fileName = MD5Encoder.encode(url);
//创建一个文件夹
File file = new File(CACHE_PATH, fileName);
//获取CACHE_PATH文件夹
File parentFile = file.getParentFile();
//判断CACHE_PATH是否存在 如果文件夹不存在, 创建文件夹
if (!parentFile.exists()) {
parentFile.mkdirs();
}
// 将图片保存在本地,第一个参数是格式,第二个是图片质量,第三个是图片路径
bitmap.compress(CompressFormat.JPEG, 100,
new FileOutputStream(file));
} catch (Exception e) {
e.printStackTrace();
}
}
}
内存中缓存MemoryCacheUtils.java使用LruCache实现
package com.ldw.beijing.utils.bitmap;
import java.lang.ref.SoftReference;
import java.util.HashMap;
import android.graphics.Bitmap;
import android.support.v4.util.LruCache;
/*
* 内存缓存
*/
public class MemoryCacheUtils {
private LruCache<String, Bitmap> mMemoryCache;
public MemoryCacheUtils() {
// 模拟器默认是16M,现在设定大小是2M
long maxMemory = Runtime.getRuntime().maxMemory() / 8;
mMemoryCache = new LruCache<String, Bitmap>((int) maxMemory) {
//sizeOf用来获取图片占用的内存大小,默认是1个字节
@Override
protected int sizeOf(String key, Bitmap value) {
// 获取图片占用内存大小
int byteCount = value.getRowBytes() * value.getHeight();
return byteCount;
}
};
}
/*
* 从内存读
*/
public Bitmap getBitmapFromMemory(String url) {
return mMemoryCache.get(url);
}
/*
* 写内存
* */
public void setBitmapToMemory(String url, Bitmap bitmap) {
mMemoryCache.put(url, bitmap);
}
/*软引用的方法缓存
//key-value的形式来保存图片缓存,使用SoftReference软引用来防止内存溢出
private HashMap<String, SoftReference<Bitmap>> mMemoryCache = new HashMap<String, SoftReference<Bitmap>>();
//private HashMap<String, Bitmap> mMemoryCache = new HashMap<String, Bitmap>();这种容易内存溢出,图片不断的缓存
//从内存中读取图片
public Bitmap getBitmapFromMemory(String url) {
//获取到软引用
SoftReference<Bitmap> softReference = mMemoryCache.get(url);
if (softReference != null) {
Bitmap bitmap = softReference.get();
return bitmap;
}
return null;
}
//网内存中写图片
public void setBitmapToMemory(String url, Bitmap bitmap) {
//使用软引用来写图片
SoftReference<Bitmap> softReference = new SoftReference<Bitmap>(bitmap);
mMemoryCache.put(url, softReference);
}
*/
}