Android性能优化之缓存的代码实现
2016-02-04 21:22
537 查看
Hi,众猿们,昨天大概谈了一下三级缓存的原理,今天就说其代码实现吧,主要有: 1.随时获取全局Context(上下文对象)的方式 2.如何代码实现json的三级缓存 3.如何编写一个实现Lru算法的缓存类 4.编写一个用LruCache类实现图片的内存缓存的工具类 第一个问题:第一个问题貌似和后三个没啥关系,其实json缓存中获取SharedPreference对象是需要通过Context对象的,因此我们要先解决第一个问题。我们需要创建一个自定义的Application对象:MyApplication,该对象基础自系统的Application对象,其代码如下:
public class MyApplication extends Application { //创建一个静态全局变量用于保存上下文对象 private static Context mContext; @Override public void onCreate() { super.onCreate(); //创建应用时获取context对象并保存在全局变量中 mContext = getApplicationContext(); } /** * 静态方法,可直接由类名调用,用于获取上下文对象 * @return */ public static Context getContext(){ return mContext; } }
这样,当我们需要用到全局变量时,直接通过类名MyApplication调用getContext()方法即可,很简单吧,还不到20行代码,其实自定义Application对象有很多妙用,如在应用初始化时创建一些全局变量等,这个我看以后有木有机会再说吧。还要注意一点,我们自定义的Application对象需要在AndroidManifest文件中注册,记载application标签下添加下列属性即可:
android:name="lmz.threelevelcache.application.MyApplication"
第二个问题,不多说直接贴代码吧:
public class JsonCacheUtil { public static final int ERROR_NET_NUSED = 1; public static final int ERROR_NO_RESULT = 0; public static final String KEY_JSONCACHE = "JsonCache"; public static void loadJson( String url, SuccessCallback successCallback, FailCallback failCallback){ String result = null; //从本地缓存加载数据, result = loadFromLocalCache(url); if(TextUtils.isEmpty(result)){ loadFromNet(url, successCallback, failCallback); } } /** * 从网络加载json数据, * @param url 请求数据的url,它会以get方式向服务器请求数据,且 * 请求参数已经添加到的url尾部 * @param successCallback * @param failCallback */ private static void loadFromNet( final String url, final SuccessCallback successCallback, final FailCallback failCallback) { if(!isNetCanAccessable()){ if (failCallback != null) { failCallback.onFail(ERROR_NET_NUSED); } return; } new AsyncTask<Void, Void, String>(){ @Override protected String doInBackground(Void... params) { URLConnection connection = null; try { connection = new URL(url).openConnection(); connection.setConnectTimeout(3000); connection.setReadTimeout(3000); connection.connect(); BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream())); StringBuffer buffer = new StringBuffer(); String line = null; while((line = reader.readLine()) != null){ buffer.append(line); } return buffer.toString(); } catch (Exception e) {} return null; } @Override protected void onPostExecute(String result) { if (result != null) { //将json数据缓存在本地 cacheJsonToLocal(url, result); if (successCallback != null) { successCallback.onSuccess(result); } }else { if (failCallback != null) { failCallback.onFail(ERROR_NO_RESULT); } } super.onPostExecute(result); } }.execute(); } /** * 缓存json数据至本地的方法 * @param url * @param result */ protected static void cacheJsonToLocal(String url, String result) { MyApplication.getContext() .getSharedPreferences(KEY_JSONCACHE, Context.MODE_PRIVATE) .edit().putString(url, result).commit(); } /** * 判断网络是否连接 * @return 网络已经连接则返回true */ private static boolean isNetCanAccessable() { boolean flag = false; ConnectivityManager manager = (ConnectivityManager) MyApplication.getContext().getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo info = manager.getActiveNetworkInfo(); if (info != null) { flag = info.isAvailable(); } return false; } /** * 根据url从本地缓存中加载json数据 * @param url * @return 若本地未缓存url对应的json数据则直接返回空字符串 */ private static String loadFromLocalCache(String url) { return MyApplication.getContext() .getSharedPreferences(KEY_JSONCACHE, Context.MODE_PRIVATE) .getString(url, ""); } public interface SuccessCallback{ public void onSuccess(String result);} public interface FailCallback{public void onFail(int error);} }
代码上都有注释,本猿猿就不在多说了。 第三个问题,昨天的文章已经说过(戳这里看昨天文章),LRU算法有不同的实现方式,不同的方式在命中率、效率、时间空间复杂度等方面也不同,它们各有优缺点,这儿我就挑一个最简单的方式来实现吧(HashMap+链表,其实本猿猿也并不是什么老猿,算法功底也没那么好),其主要的思想为:建立一个链表,储存着HashMap中所有的键,当有值加入HashMap或从HashMap中取出时,将其对应的键移至表头,而每次移除对象时,总是优先移除链表尾的键所对应的值。其代码如下。其完整代码如下:
public abstract class MyLruCache<K, V> { private int mMaxSize = 0; private int currSize = 0; private Map<K, V> mValues = new LinkedHashMap<K, V>(); private List<K> mPositions = new LinkedList<K>(); public MyLruCache(int maxSize){ //设置该所管理的空间的最大值 this.mMaxSize = maxSize; } /** * 将指定的键和值放入集合中 * @param k * @param v */ public void put(K k, V v){ if (mPositions.contains(k)) { return; } int size = sizeof(v); //若加入了值后该对象所占的空间大于最大空间,则不断移除对象直到不大于最大空间 while(currSize + size > mMaxSize){ removeFromTail(); } mValues.put(k, v); mPositions.add(k); } /** * 根据指定的键得到值 * @param k * @return */ public V get(K k){ if ( !mPositions.contains(k)) { return null; } if (mPositions.remove(k)) { mPositions.add(k); }else{ return null; } return mValues.get(k); } /** * 根据指定的键移除值 * @param k */ public void remove(K k){ if ( !mPositions.contains(k)) { return; } mPositions.remove(k); mValues.remove(k); } /** * 从链表尾部移除值 */ private void removeFromTail() { K k = mPositions.remove(0); V v = mValues.remove(k); currSize =- sizeof(v); } /** * 用于计算加入该空间的每一个值得大小,子类必须实现该方法 * @param v * @return */ abstract protected int sizeof(V v); }
Android系统的LruCache对象也是采用这种方式(微信搜索公众号:brooklee123,关注本猿猿的微信公众号“猿聚于此”,回复关键字“LruCache”,即可得到LruCache的源码),LRU算法不仅仅局限于处理图片的缓存,只要我们需要维护一定大小的某一空间(如内存),都可以使用Android为我们提供的LruCache对象。 好了,最后一个问题,不多说了,直接上源码:
public class BitmapMemoryCacheUtils { private LruCache<String, Bitmap> mMemCache; public BitmapMemoryCacheUtils() { long maxMemory = Runtime.getRuntime().maxMemory();// 模拟器默认是16M内存 mMemCache = new LruCache<String, Bitmap>((int) (maxMemory / 8)) { @Override protected int sizeOf(String key, Bitmap value) { return value.getRowBytes() * value.getHeight();// 返回图片大小 } }; } /** * 从内存中取图片 * * @param url * @return */ public Bitmap getBitmapFromMem(String url) { return mMemCache.get(url); } /** * 向内存中存图片 * * @param url * @param bitmap */ public void putBitmapToMem(String url, Bitmap bitmap) { mMemCache.put(url, bitmap); } }
对了,要想理解这一部分,你还有了解URLConnection及其子类HttpURLConnection对象、AsyncTask对象,有机会的话本猿会捯饬捯饬这两个对象的。 本猿还开了一个个人微信公众号:猿聚于此,本猿基本每天都会在该公众号上更新文章,如果大家像获得文章更新的最新信息的话,欢迎关注该微信公众号,微信搜索brooklee123即可关注,最后出个小题,就当娱乐一下吧,想不出来答案的猿猿们可要好好努力呀: 食堂有两个程序猿,一个吃完小心翼翼地把餐具送完才走,一个没送餐具就遛了,请问那个是C++程序猿?
相关文章推荐
- Android 沉浸式状态栏
- Android广播Broadcast的学习(附demo)
- Android性能优化之三级缓存
- Android 简单实现ListView指定ITEM滑到到顶停靠
- Android学习路线(二十四)ActionBar Fragment运用最佳实践
- android开发步步为营之93:android自定义view开发之一(验证码生成器)
- Android总结——开始一个Activity
- android 结合shape和selector自定义制作立体图(节约内存)
- android: ListView历次优化
- android *** 自定义Dialog
- Android开发环境搭建总结
- android *** Toast
- Style注意事项
- Android adb基本命令-cd,ls,目录相关命令
- Android adb基本命令-cd,ls,目录相关命令
- Android adb基本命令-cd,ls,目录相关命令
- Android adb基本命令-cd,ls,目录相关命令
- 在android style.xml文件中使用自定义属性
- android *** ProgressDialog
- android 广播broadcast,有序和无序,优先级,permission等粗解