App快取管理
無論大型或小型應用,靈活的快取可以說不僅大大減輕了伺服器的壓力,而且因為更快速的使用者體驗而方便了使用者。
Android的apk可以說是作為小型應用,其中99%的應用並不是需要實時更新的,而且詬病於蝸牛般的移動網速,與伺服器的資料互動是能少則少,這樣使用者體驗才更好,這也是我們有時捨棄webview而採用json傳輸資料的原因之一。
採用快取,可以進一步大大緩解資料互動的壓力,特此,我們簡略列舉一下快取管理的適用環境:
1. 提供網路服務的應用
2. 資料更新不需要實時更新,但是哪怕是3-5分鐘的延遲也是可以採用快取機制。
3. 快取的過期時間是可以接受的(不會因為快取帶來的好處,導致某些資料因為更新不及時而影響產品的形象等)
帶來的好處:
1. 伺服器的壓力大大減小
2. 客戶端的響應速度大大變快(使用者體驗)
3. 客戶端的資料載入出錯情況大大較少,大大提高了應有的穩定性(使用者體驗)
4. 一定程度上可以支援離線瀏覽(或者說為離線瀏覽提供了技術支援)
一、快取管理的方法
這裡的快取管理的原理很簡:通過時間的設定來判斷是否讀取快取還是重新下載。
裡面會有一些細節的處理,後面會詳細闡述。
基於這個原理,目前鄙人見過的兩種比較常見的快取管理方法是:資料庫法和檔案法。
二、資料庫法快取管理
這種方法是在下載完資料檔案後,把檔案的相關資訊如url,路經,下載時間,過期時間等存放到資料庫,下次下載的時候根據url先從資料庫中查詢,如果查詢到當前時間並未過期,就根據路徑讀取本地檔案,從而實現快取的效果。
從實現上我們可以看到這種方法可以靈活存放檔案的屬性,進而提供了很大的擴充套件性,可以為其它的功能提供一定的支援;
從操作上需要建立資料庫,每次查詢資料庫,如果過期還需要更新資料庫,清理快取的時候還需要刪除資料庫資料,稍顯麻煩,而資料庫操作不當又容易出現一系列的效能,ANR問題,實現的時候要謹慎,具體作的話,但也只是增加一個工具類或方法的事情。
還有一個問題,快取的資料庫是存放在/data/data/<package>/databases/目錄下,是佔用記憶體空間的,如果快取累計,容易浪費記憶體,需要及時清理快取。
當然這種方法從目前一些應用的實用上看,我沒有發現什麼問題。
本文我側重強調第二種方法,第一種方法的實現,就此掠過。
三、檔案法快取管理
這種方法,使用File.lastModified()方法得到檔案的最後修改時間,與當前時間判斷是否過期,從而實現快取效果。
實現上只能使用這一個屬性,沒有為其它的功能提供技術支援的可能。
操作上倒是簡單,比較時間即可。本身處理也不容易帶來其它問題,代價低廉。
四、檔案法快取管理的兩點說明
1. 不同型別的檔案的快取時間不一樣。
籠統的說,不變檔案的快取時間是永久,變化檔案的快取時間是最大忍受不變時間。
說白點,圖片檔案內容是不變的,直到清理,我們是可以永遠讀取快取的。
配置檔案內容是可能更新的,需要設定一個可接受的快取時間。
2. 不同環境下的快取時間標準不一樣。
無網路環境下,我們只能讀取快取檔案,哪怕快取早就過期。
WiFi網路環境下,快取時間可以設定短一點,一是網速較快,而是流量不要錢。
移動資料流量環境下,快取時間可以設定長一點,節省流量,就是節省金錢,而且使用者體驗也更好。
舉個例子吧,最近本人在做的一個應用在wifi環境下的快取時間設定為5分鐘,移動資料流量下的快取時間設定為1小時。
這個時間根據自己的實際情況來設定:資料的更新頻率,資料的重要性等。
五、何時重新整理
開發者一方面希望儘量讀取快取,使用者一方面希望實時重新整理,但是響應速度越快越好,流量消耗越少越好,是一個矛盾。
其實何時重新整理我也不知道,這裡我提供兩點建議:
1. 資料的最長多長時間不變,對應用無大的影響。
比如,你的資料更新時間為1天,則快取時間設定為4~8小時比較合適,一天他總會看到更新,如果你覺得你是資訊類應用,再減少,2~4小時,如果你覺得資料比較重要或者比較受歡迎,使用者會經常把玩,再減少,1~2小時,依次類推。
為了保險起見,你可能需要毫無理由的再次縮減一下。
2. 提供重新整理按鈕。
上面說的保險起見不一定保險,最保險的方法使在相關介面提供一個重新整理按鈕,為快取,為載入失敗提供一次重新來過的機會,有了這個重新整理按鈕,我們的心也才真的放下來。
六、檔案快取法的具體實現
針對配置檔案的快取,我新建了一個類ConfigCache:
import java.io.File;
import java.io.IOException;
import android.util.Log;
import com.tianxia.app.floworld.AppApplication;
import com.tianxia.app.floworld.utils.FileUtils;
import com.tianxia.app.floworld.utils.NetworkUtils;
public class ConfigCache {
private static final String TAG = ConfigCache.class.getName();
public static final int CONFIG_CACHE_MOBILE_TIMEOUT = 3600000; //1 hour
public static final int CONFIG_CACHE_WIFI_TIMEOUT = 300000; //5 minute
public static String getUrlCache(String url) {
if (url == null) {
return null;
}
String result = null;
File file = new File(AppApplication.mSdcardDataDir + "/" + getCacheDecodeString(url));
if (file.exists() && file.isFile()) {
long expiredTime = System.currentTimeMillis() - file.lastModified();
Log.d(TAG, file.getAbsolutePath() + " expiredTime:" + expiredTime/60000 + "min");
//1. in case the system time is incorrect (the time is turn back long ago)
//2. when the network is invalid, you can only read the cache
if (AppApplication.mNetWorkState != NetworkUtils.NETWORN_NONE && expiredTime < 0) {
return null;
}
if(AppApplication.mNetWorkState == NetworkUtils.NETWORN_WIFI
&& expiredTime > CONFIG_CACHE_WIFI_TIMEOUT) {
return null;
} else if (AppApplication.mNetWorkState == NetworkUtils.NETWORN_MOBILE
&& expiredTime > CONFIG_CACHE_MOBILE_TIMEOUT) {
return null;
}
try {
result = FileUtils.readTextFile(file);
} catch (IOException e) {
e.printStackTrace();
}
}
return result;
}
public static void setUrlCache(String data, String url) {
File file = new File(AppApplication.mSdcardDataDir + "/" + getCacheDecodeString(url));
try {
//建立快取資料到磁碟,就是建立檔案
FileUtils.writeTextFile(file, data);
} catch (IOException e) {
Log.d(TAG, "write " + file.getAbsolutePath() + " data failed!");
e.printStackTrace();
}
}
public static String getCacheDecodeString(String url) {
//1. 處理特殊字元
//2. 去除字尾名帶來的檔案瀏覽器的檢視凌亂(特別是圖片更需要如此類似處理,否則有的手機開啟相簿,全是我們的快取圖片)
if (url != null) {
return url.replaceAll("[.:/,%?&=]", "+").replaceAll("[+]+", "+");
}
return null;
}
}
從實現上我們全面考慮了幾個細節,註釋已經說明,不再贅述。
然後我們呼叫方法如下:
void getConfig(){
//首先嚐試讀取快取
String cacheConfigString = ConfigCache.getUrlCache(CONFIG_URL);
//根據結果判定是讀取快取,還是重新讀取
if (cacheConfigString != null) {
showConfig(cacheConfigString);
} else {
//如果快取結果是空,說明需要重新載入
//快取為空的原因可能是1.無快取;2. 快取過期;3.讀取快取出錯
AsyncHttpClient client = new AsyncHttpClient();
client.get(CONFIG_URL, new AsyncHttpResponseHandler(){
@Override
public void onSuccess(String result){
//成功下載,則儲存到本地作為後面快取檔案
ConfigCache.setUrlCache(result, CONFIG_URL);
//後面可以是UI更新,僅供參考
showConfig(result);
}
@Override
public void onFailure(Throwable arg0) {
//根據失敗原因,考慮是顯示載入失敗,還是再讀取快取
}
});
}
}
這樣配置檔案既能有效快取,又能及時更新了,同時支援離線瀏覽。
七、小結
智慧手機的快取管理應用非常的普遍和需要,是提高使用者體驗的有效手段之一。
當然,快取管理一些內容沒有細說,如圖片快取,快取清理等,這些處理起來比較簡單。
相關文章
- SpringBoot快取管理(一) 預設快取管理Spring Boot快取
- uniappp js快取APPJS快取
- 用Java寫一個分散式快取——快取管理Java分散式快取
- 【UniApp】-uni-app-資料快取APP快取
- SpringBoot快取管理(二) 整合Redis快取實現Spring Boot快取Redis
- 快取原理與微服務快取自動管理快取微服務
- 基於ObjectMapper的本地快取ObjectAPP快取
- localStorage和SessionStorage,Application,Cache快取SessionAPP快取
- 如何管理需要拼接的快取 Key快取
- 快取穿透、快取擊穿、快取雪崩、快取預熱快取穿透
- uni-app快取器的封裝APP快取封裝
- 快取穿透、快取擊穿、快取雪崩快取穿透
- 快取穿透、快取雪崩、快取擊穿快取穿透
- SpringBoot快取管理(三) 自定義Redis快取序列化機制Spring Boot快取Redis
- Redis快取擊穿、快取穿透、快取雪崩Redis快取穿透
- HTTP快取——協商快取(快取驗證)HTTP快取
- [Redis]快取穿透/快取擊穿/快取雪崩Redis快取穿透
- 快取穿透 快取雪崩快取穿透
- Spring 整合 Ehcache 管理快取詳解Spring快取
- Glide 核心設計二: 快取管理IDE快取
- 快取問題(一) 快取穿透、快取雪崩、快取併發 核心概念快取穿透
- 如何在SPRING中同時管理本地快取和分散式快取? - techblogSpring快取分散式
- 快取穿透、快取擊穿、快取雪崩區別快取穿透
- 快取問題(四) 快取穿透、快取雪崩、快取併發 解決案例快取穿透
- ServiceWorker 快取與 HTTP 快取快取HTTP
- mybatis快取-二級快取MyBatis快取
- MyBatis快取機制(一級快取,二級快取)MyBatis快取
- 快取、快取演算法和快取框架簡介快取演算法框架
- 快取淘汰、快取穿透、快取擊穿、快取雪崩、資料庫快取雙寫一致性快取穿透資料庫
- vuex狀態管理與瀏覽器快取Vue瀏覽器快取
- Hadoop 集中式的快取管理demoHadoop快取
- redis進階之快取管理(1課時)Redis快取
- android 快取管理及LRU演算法Android快取演算法
- HTML5應用程式快取Application CacheHTML快取APP
- Redis詳解(十二)------ 快取穿透、快取擊穿、快取雪崩Redis快取穿透
- 面試總結 —— Redis “快取穿透”、“快取擊穿”、“快取雪崩”面試Redis快取穿透
- 快取穿透,快取擊穿,快取雪崩解決方案分析快取穿透
- 快取穿透、快取雪崩和快取擊穿是什麼?快取穿透