从Jamendo加载到进入主页,Jamendo中所涉及的两种缓存都已经涉及到。
(一) RequestCache(服务器请求缓存)
从服务器上下载数据非常耗时,并且耗电。所以避免重复下载很有必要。Jamendo的RequestCache的原则是:保留最近10次(这个值可以自己设定)的网络请求。如果超过,清除最早的缓存内容。在调用Call获取服务器数据时,首先在RequestCache中查找,是否存在,如果不存在,向服务器请求,并将请求到的数据加入缓存中。很好理解的流程。RequestCache的代码如下:
- /*
- * Copyright (C) 2009 Teleca Poland Sp. z o.o. <[email protected]>
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- package com.teleca.jamendo.api.util;
- import java.util.Hashtable;
- import java.util.LinkedList;
- /**
- * @author Lukasz Wisniewski
- */
- public class RequestCache {
- // TODO cache lifeTime
- private static int CACHE_LIMIT = 10;//保存最近十次的请求数据,key为url
- @SuppressWarnings("unchecked")
- private LinkedList history;
- private Hashtable<String, String> cache;
- @SuppressWarnings("unchecked")
- public RequestCache(){
- history = new LinkedList();
- cache = new Hashtable<String, String>();
- }
- @SuppressWarnings("unchecked")
- public void put(String url, String data){
- history.add(url);
- // too much in the cache, we need to clear something
- if(history.size() > CACHE_LIMIT){
- String old_url = (String) history.poll();
- cache.remove(old_url);
- }
- cache.put(url, data);
- }
- public String get(String url){
- return cache.get(url);
- }
- }
Note:RequestCache使用的存储集合是HashTable,它不允许Null的key和value,并且是同步安全的。因为存储的数量较少,且是最耗时的操作,存储空值无意义,所以选用HashTable。
HashTable相关内容,参看:http://mikewang.blog.51cto.com/3826268/856865
(二) ImageCache缓存(图片缓存)
图片缓存的流程图如下:
代码如下:
- public class ImageCache extends WeakHashMap<String, Bitmap> {
- private static final long serialVersionUID = 1L;
- public boolean isCached(String url){
- return containsKey(url) && get(url) != null;
- }
- }
显然,ImageCache继承了WeakHashmap。
WeakHashmap非常重要。我专门做了一个整理,相关内容我的博文:http://mikewang.blog.51cto.com/3826268/880775
当然,我们仍然对ImageCache缓存的取舍不是很清楚。回到RemoteImageView的setImageUrl方法。代码如下:
- public void setImageUrl(String url){
- // Log.d("img_url", "img_url is :" + url);
- if (mUrl != null && mUrl.equals(url) && (mCurrentlyGrabbedUrl == null ||//1:url赋给全局变量mUrl,两者相等且都为空,但是未执行,所以mCurrentlyGrabbedUrl为空
- (mCurrentlyGrabbedUrl != null && !mCurrentlyGrabbedUrl.equals(url)))) {//2:第n(n>1)次执行时,并未完成downloadTask
- mFailure++;
- if(mFailure > MAX_FAILURES){//超过指定重连次数
- Log.e(JamendoApplication.TAG, "Failed to download "+url+", falling back to default image");
- loadDefaultImage();
- return;
- }
- } else {
- mUrl = url;
- mFailure = 0;
- }
- updateCacheSize();
- if (mCacheSize>0 && (url.contains(ALBUMS) || url.contains(RADIOS))) {//只有两类路径图片需要缓存
- String fileName = convertUrlToFileName(url);
- String dir = getDirectory(fileName);
- Log.d("img_url", "dir is :" + dir);
- String pathFileName = dir + "/" + fileName;
- Log.d("img_url", "pathFileName is :" + pathFileName);
- Bitmap tbmp = BitmapFactory.decodeFile(pathFileName);//从指定文件保存路径解码处图片
- if (tbmp == null) {
- Log.d(JamendoApplication.TAG, "Image is not present, try to download");
- try{
- new DownloadTask().execute(url);
- } catch (RejectedExecutionException e) {
- // do nothing, just don't crash
- }
- } else {
- Log.i(JamendoApplication.TAG, "Loading album cover from file");
- this.setImageBitmap(tbmp);
- updateFileTime(dir,fileName );
- }
- removeAlbumCoversCache(dir, fileName);//对专辑图片的缓存处理:比较大120k
- removeRadioCoversCache(dir, fileName);//对广播图片的缓存处理:比较小8k
- }
- else {
- Log.i(JamendoApplication.TAG, "File not cached supported" + url);
- ImageCache imageCache = JamendoApplication.getInstance()
- .getImageCache();
- if (imageCache.isCached(url)) {
- this.setImageBitmap(imageCache.get(url));
- } else {
- try {
- Log.i(JamendoApplication.TAG, "Image is not present, try to download");
- new DownloadTask().execute(url);
- } catch (RejectedExecutionException e) {
- // do nothing, just don't crash
- }
- }
- }
- }
1. 对两类图片(AlbumCover和RadioCover)进行处理,打了log之后,AlbumCover的大小在120k左右,主要用在播放页面做背景。RadioCover的大小在8k左右,主要用来显示图标。对两者的处理也不同。大小是15倍左右的关系。代码如下:
- removeAlbumCoversCache(dir, fileName);//对专辑图片的缓存处理:比较大120k
- removeRadioCoversCache(dir, fileName);//对广播图片的缓存处理:比较小8k
对AlbumCover,因为它的文件大小比较大,所以统计是否超出总的缓存容量时,以它的总大小计算就ok。代码如下:
- private void removeAlbumCoversCache(String dirPath, String filename) {
- if (!filename.contains(ALBUM_COVER_MARKER)) {
- return;
- }
- File dir = new File(dirPath);
- File[] files = dir.listFiles();
- if (files == null) {
- // possible sd card is not present/cant write
- return;
- }
- int dirSize = 0;
- for (int i = 0; i < files.length; i++) {
- if (files[i].getName().contains(ALBUM_COVER_MARKER)) {//计算出指定路径指定类型文件大小:只计算出大图片的大小,小的不用计算,简化
- dirSize += files[i].length();
- }
- }
- if (dirSize > mCacheSize * MB || FREE_SD_SPACE_NEEDED_TO_CACHE > freeSpaceOnSd()) {
- int removeFactor = (int) ((0.4 * files.length) + 1);//移出缓存文件夹的四分之一文件
- Arrays.sort(files, new FileLastModifSort());//按照从老到新的顺序排列
- Log.i(JamendoApplication.TAG, "Clear some album covers cache files ");
- for (int i = 0; i < removeFactor; i++) {//移出选定数量的缓存文件(其中只删除占空间比较大的专辑图片,抓大舍小)
- if (files[i].getName().contains(ALBUM_COVER_MARKER)) {
- files[i].delete();
- }
- }
- }
- }
对RadioCover,因为其所占空间很小,所以按照时间来清理。超过指定日期的文件就清理掉。清除缓存(下载)超过指定天数的文件,只保存最近45天内的文件。代码如下:
- private void removeRadioCoversCache(String dirPath, String filename) {
- if (filename.contains(ALBUM_COVER_MARKER)) {//筛选出非专辑图片类图片
- return;
- }
- File file = new File(dirPath, filename);
- if (file.lastModified() != 0
- && System.currentTimeMillis() - file.lastModified() > mTimeDiff) {//清除缓存(下载)超过指定天数的文件,即所有的Radio类图片,只保存最近45天内的文件
- Log.i(JamendoApplication.TAG, "Clear some album or radio thumbnail cache files ");
- file.delete();
- }
- }
2. 思考
这让做的好处是,抓住了真正耗时的关键,有的放矢。