您的位置:首页 > 移动开发 > Android开发

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++程序猿?
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: