专栏首页Android开发指南13.缓存、三级缓存、内存溢出、AsyncTask

13.缓存、三级缓存、内存溢出、AsyncTask

SharePreference工具类

  1. /** * SharePreference封装 * */ public class PrefUtils { public static final String PREF_NAME = "config"; public static boolean getBoolean(Context ctx, String key, boolean defaultValue) { SharedPreferences sp = ctx.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE); return sp.getBoolean(key, defaultValue); } public static void setBoolean(Context ctx, String key, boolean value) { SharedPreferences sp = ctx.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE); sp.edit().putBoolean(key, value).commit(); } public static String getString(Context ctx, String key, String defaultValue) { SharedPreferences sp = ctx.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE); return sp.getString(key, defaultValue); } public static void setString(Context ctx, String key, String value) { SharedPreferences sp = ctx.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE); sp.edit().putString(key, value).commit(); } }

缓存工具类

  1. public class CacheUtils { /** * 缓存原理:设置缓存 key 是url, value是json(解析出来的) */ public static void setCache(String key, String value, Context ctx) { PrefUtils.setString(ctx, key, value); // 可以将缓存放在文件中, 文件名就是Md5(url), 文件内容是json } /** * 获取缓存 key 是url */ public static String getCache(String key, Context ctx) { return PrefUtils.getString(ctx, key, null); } }

用法:

1.在请求完网络,获取json数据后保存起来

  1. private void getDataFromServer() { HttpUtils utils = new HttpUtils(); utils.send(HttpMethod.GET, GlobalContants.PHOTOS_URL, new RequestCallBack<String>() { @Override public void onSuccess(ResponseInfo<String> responseInfo) { String result = (String) responseInfo.result; parseData(result); // 设置缓存 CacheUtils.setCache(GlobalContants.PHOTOS_URL, result, mActivity); } @Override public void onFailure(HttpException error, String msg) { Toast.makeText(mActivity, msg, Toast.LENGTH_SHORT) .show(); error.printStackTrace(); } }); }

2.在初始化数据的时候判断,可以直接解析数据,也可以什么都不做,然后获取网络数据看有没有最新的

  1. public void initData() { String cache = CacheUtils .getCache(GlobalContants.PHOTOS_URL, mActivity); if (!TextUtils.isEmpty(cache)) { // parseData(cache); } getDataFromServer(); }

解析数据

  1. 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(); lvPhoto.setAdapter(mAdapter); gvPhoto.setAdapter(mAdapter); } }

三级缓存

- 内存缓存, 优先加载, 速度最快 - 本地缓存, 次优先加载, 速度快 - 网络缓存, 不优先加载, 速度慢,浪费流量

服务器端下载的图片是使用 Http的缓存机制,每次执行将本地图片的时间发送给服务器,如果返回码是 304,说明服务端的图片和本地的图片是相同的,直接使用本地保存的图片,如果返回码是 200,则开始下载新的图片并实现缓存。在从服务器获取到图片后,需要再在本地和内存中分别存一份,这样下次直接就可以从内存中直接获取了,这样就加快了显示的速度,提高了用户的体验。

内存溢出OOM

导致内存泄漏主要的原因是,先前申请了内存空间而忘记了释放。如果程序中存在对无用对象的引用,那么这些对象就会驻留内存,消耗内存,因为无法让垃圾回收器GC验证这些对象是否不再需要。如果存在对象的引用,这个对象就被定义为"有效的活动",同时不会被释放。要确定对象所占内存将被回收,我们就要务必确认该对象不再会被使用。典型的做法就是把对象数据成员设为null或者从集合中移除该对象。但当局部变量不需要时,不需明显的设为null,因为一个方法执行完毕时,这些引用会自动被清理

内存溢出(oom) out of memory,是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory;比如申请了一个integer,但给它存了long才能存下的数,那就是内存溢出。

内存泄露 memory leak,是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光。memory leak会最终会导致out of memory!

解决方法1:java中的引用(使用软引用)

    - 强引用 垃圾回收器不会回收, java默认引用都是强引用     - 软引用 SoftReference   在内存不够时,垃圾回收器会考虑回收     - 弱引用 WeakReference  在内存不够时,垃圾回收器会优先回收     - 虚引用 PhantomReference  在内存不够时,垃圾回收器最优先回收 注意: Android2.3+, 系统会优先将SoftReference的对象提前回收掉, 即使内存够用

解决方法2:LruCache 

    least recentlly use 最少最近使用算法     会将内存控制在一定的大小内, 超出最大值时会自动回收, 这个最大值开发者自己定 解决方法3:图片压缩

xutils就是这个原理,现在不用xutils,现在自定义几个工具类

1.自定义加载工具类

  1. 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 ivPic, String url) { ivPic.setImageResource(R.drawable.news_pic_default);// 设置默认加载图片 Bitmap bitmap = null; // 从内存读 bitmap = mMemoryCacheUtils.getBitmapFromMemory(url); if (bitmap != null) { ivPic.setImageBitmap(bitmap); System.out.println("从内存读取图片啦..."); return; } // 从本地读 bitmap = mLocalCacheUtils.getBitmapFromLocal(url); if (bitmap != null) { ivPic.setImageBitmap(bitmap); System.out.println("从本地读取图片啦..."); mMemoryCacheUtils.setBitmapToMemory(url, bitmap);// 将图片保存在内存 return; } // 从网络读 mNetCacheUtils.getBitmapFromNet(ivPic, url); } }

2.网络缓存、AsyncTask

  1. public class NetCacheUtils { private LocalCacheUtils mLocalCacheUtils; private MemoryCacheUtils mMemoryCacheUtils; public NetCacheUtils(LocalCacheUtils localCacheUtils, MemoryCacheUtils memoryCacheUtils) { mLocalCacheUtils = localCacheUtils; mMemoryCacheUtils = memoryCacheUtils; } /** * 从网络下载图片 * * @param ivPic * @param url */ public void getBitmapFromNet(ImageView ivPic, String url) { new BitmapTask().execute(ivPic, url);// 启动AsyncTask, // 参数会在doInbackground中获取 } /** * Handler和线程池的封装 * * 第一个泛型: 参数类型 第二个泛型: 更新进度的泛型, 第三个泛型是onPostExecute的返回结果 * * @author Kevin * */ class BitmapTask extends AsyncTask<Object, Void, Bitmap> { private ImageView ivPic; private String url; /** * 后台耗时方法在此执行, 子线程 */ @Override protected Bitmap doInBackground(Object... params) { ivPic = (ImageView) params[0]; url = (String) params[1]; ivPic.setTag(url);// 将url和imageview绑定 return downloadBitmap(url); } /** * 更新进度, 主线程 */ @Override protected void onProgressUpdate(Void... values) { super.onProgressUpdate(values); } /** * 耗时方法结束后,执行该方法, 主线程 */ @Override protected void onPostExecute(Bitmap result) { if (result != null) { String bindUrl = (String) ivPic.getTag(); if (url.equals(bindUrl)) {// 确保图片设定给了正确的imageview ivPic.setImageBitmap(result); mLocalCacheUtils.setBitmapToLocal(url, result);// 将图片保存在本地 mMemoryCacheUtils.setBitmapToMemory(url, result);// 将图片保存在内存 System.out.println("从网络缓存读取图片啦..."); } } } } /** * 下载图片 * * @param url * @return */ 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 = BitmapFactory.decodeStream(inputStream, null, option); return bitmap; } } catch (Exception e) { e.printStackTrace(); } finally { conn.disconnect(); } return null; } }

3.本地缓存(SD卡),一般会以MD5加密文件名

  1. public class LocalCacheUtils { public static final String CACHE_PATH = Environment .getExternalStorageDirectory().getAbsolutePath() + "/zhbj_cache_52"; /** * 从本地sdcard读图片 */ public Bitmap getBitmapFromLocal(String url) { try { String fileName = MD5Encoder.encode(url); File file = new File(CACHE_PATH, fileName); if (file.exists()) { Bitmap bitmap = BitmapFactory.decodeStream(new FileInputStream( file));//decodeStream放的是输入输出流 return bitmap; } } catch (Exception e) { e.printStackTrace(); } return null; } /** * 向sdcard写图片 * * @param url * @param bitmap */ public void setBitmapToLocal(String url, Bitmap bitmap) { try { String fileName = MD5Encoder.encode(url); File file = new File(CACHE_PATH, fileName); File parentFile = file.getParentFile(); if (!parentFile.exists()) {// 如果文件夹不存在, 创建文件夹 parentFile.mkdirs(); } // 将图片保存在本地 bitmap.compress(CompressFormat.JPEG, 100, new FileOutputStream(file));//100是质量 } catch (Exception e) { e.printStackTrace(); } }

3.内存缓存:

内存中使用LRUCache是最合适的。如果用HashMap来实现,不是不可以,但需要注意在合适的时候释放缓存。至于具体怎么释放,我没考虑过,但用软引用的问题在于,你很难控制缓存的大小,也就是说,只有等到你的内存快要撑爆,你的图片缓存才会被回收。是不是感觉傻傻的?

  1. public class MemoryCacheUtils { // private HashMap<String, SoftReference<Bitmap>> mMemoryCache = new // HashMap<String, SoftReference<Bitmap>>();//一开始使用map,后来使用软引用 private LruCache<String, Bitmap> mMemoryCache; public MemoryCacheUtils() { long maxMemory = Runtime.getRuntime().maxMemory() / 8;//主流都是分配16m的8/1 mMemoryCache = new LruCache<String, Bitmap>((int) maxMemory) { @Override protected int sizeOf(String key, Bitmap value) { int byteCount = value.getRowBytes() * value.getHeight();// 获取图片占用内存大小 return byteCount; } }; } /** * 从内存读 * * @param url */ public Bitmap getBitmapFromMemory(String url) { // SoftReference<Bitmap> softReference = mMemoryCache.get(url); // if (softReference != null) { // Bitmap bitmap = softReference.get(); // return bitmap; // } return mMemoryCache.get(url); } /** * 写内存 * * @param url * @param bitmap */ public void setBitmapToMemory(String url, Bitmap bitmap) { // SoftReference<Bitmap> softReference = new // SoftReference<Bitmap>(bitmap); // mMemoryCache.put(url, softReference); mMemoryCache.put(url, bitmap); } }

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 8:String类

    六月的雨
  • 5.AsyncHttp、post

    六月的雨
  • 3.接口文档

    六月的雨
  • Android 二次封装网络加载框架

    版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/gdutxiaoxu/article/de...

    用户2965908
  • PC 微信扫码登陆

    网站应用微信登录是基于OAuth2.0协议标准构建的微信OAuth2.0授权登录系统。进一步了解OAuth2.0-----理解OAuth2.0 官方介绍资料

    Javen
  • String 的不可变真的是因为 final 吗?

    String 为啥不可变?因为 String 中的 char 数组被 final 修饰。这套回答相信各位已经背烂了,But 这并不正确!

    飞天小牛肉
  • java版最新获取抖音无水印视频和背景音乐

    public static void main(String[] args) throws Exception {

    织梦有点东西
  • getParameterMap()返回参数需要对应实体类类型,否则收不到----打卡

  • 几张图轻松理解String.intern()

    转载自 https://blog.csdn.net/soonfly/article/details/70147205

    allsmallpig
  • 在java中String类为什么要设计成final?

    String很多实用的特性,比如说“不可变性”,是工程师精心设计的艺术品!艺术品易碎!用final就是拒绝继承,防止世界被熊孩子破坏,维护世界和平!

    用户1205080

扫码关注云+社区

领取腾讯云代金券