android 實(shí)現(xiàn)類似微信緩存和即時(shí)更新好友頭像示例
引言
使用微信時(shí)我們會(huì)發(fā)現(xiàn),首次進(jìn)入微信的好友列表時(shí),會(huì)加載好友頭像,但是再次進(jìn)入時(shí),就不用重新加載了,而且其他頁(yè)面都不用重新加載,說(shuō)明微信的好友頭像是緩存在本地的,然后好友修改頭像后,又會(huì)及時(shí)的更新,這個(gè)功能是如何實(shí)現(xiàn)的呢,我們來(lái)分析一下
分析
關(guān)于頭像緩存的實(shí)現(xiàn)
頭像是網(wǎng)絡(luò)圖片,而且數(shù)據(jù)量較大,如果用我們常用的SharedPreferences將頭像以Bitmap的形式存儲(chǔ),勢(shì)必會(huì)造成OOM,這個(gè)方法是行不通的,我們存儲(chǔ)的只能是圖片的地址,但是如果只存儲(chǔ)地址的話,要轉(zhuǎn)化成圖片,還是要通過(guò)網(wǎng)絡(luò)請(qǐng)求重新加載,達(dá)不到我們要求的效果,所以我們需要在磁盤中單獨(dú)開辟一塊空間,將頭像以Bitmap的形式進(jìn)行存儲(chǔ),如何實(shí)現(xiàn)呢?其實(shí)關(guān)于網(wǎng)絡(luò)圖片的緩存,有很多開源的第三方框架,比較可靠好用的如xUtils,Glide,volley,Universal-Image-Loader,Picasso,Fresco等等。
下面我們以常用的xUtils為例首先對(duì)BitmapUtils的實(shí)例化,對(duì)于磁盤緩存路徑,磁盤緩存空間大小,內(nèi)存緩存的空間大小,內(nèi)存緩存百分比可以自定義,也可以使用默認(rèn)配置,代碼如下:
/**
* @param context 上下文
*/
public BitmapUtils(Context context) {
this(context, null);
}
/**
* @param context 上下文
* @param diskCachePath 磁盤高速緩存路徑
*/
public BitmapUtils(Context context, String diskCachePath) {
if (context == null) {
throw new IllegalArgumentException("context may not be null");
}
this.context = context.getApplicationContext();
globalConfig = BitmapGlobalConfig.getInstance(this.context, diskCachePath);
defaultDisplayConfig = new BitmapDisplayConfig();
}
/**
*
* @param context 上下文
* @param diskCachePath 磁盤高速緩存路徑
* @param memoryCacheSize 內(nèi)存緩存空間大小
*/
public BitmapUtils(Context context, String diskCachePath, int memoryCacheSize) {
this(context, diskCachePath);
globalConfig.setMemoryCacheSize(memoryCacheSize);
}
/**
*
* @param context 上下文
* @param diskCachePath 磁盤高速緩存路徑
* @param memoryCacheSize 內(nèi)存緩存空間大小
* @param diskCacheSize 磁盤高速緩存空間大小
*/
public BitmapUtils(Context context, String diskCachePath, int memoryCacheSize, int diskCacheSize) {
this(context, diskCachePath);
globalConfig.setMemoryCacheSize(memoryCacheSize);
globalConfig.setDiskCacheSize(diskCacheSize);
}
/**
*
* @param context 上下文
* @param diskCachePath 磁盤高速緩存路徑
* @param memoryCachePercent 內(nèi)存緩存百分比
*/
public BitmapUtils(Context context, String diskCachePath, float memoryCachePercent) {
this(context, diskCachePath);
globalConfig.setMemCacheSizePercent(memoryCachePercent);
}
/**
*
* @param context 上下文
* @param diskCachePath 磁盤高速緩存路徑
* @param memoryCachePercent 內(nèi)存緩存百分比
* @param diskCacheSize 磁盤緩存空間大小
*/
public BitmapUtils(Context context, String diskCachePath, float memoryCachePercent, int diskCacheSize) {
this(context, diskCachePath);
globalConfig.setMemCacheSizePercent(memoryCachePercent);
globalConfig.setDiskCacheSize(diskCacheSize);
}
一般情況下,我們只需要使用默認(rèn)配置就可以了,即
BitmapUtils bitmap = new BitmapUtils(context);
然后對(duì)圖片的緩存和顯示
/**
* 根據(jù)圖片路徑,顯示到具體的View上
* @param container 要把圖片顯示到的View
* @param uri 圖片路徑
*/
public <T extends View> void display(T container, String uri) {
display(container, uri, null, null);
}
/**
* 根據(jù)圖片路徑,顯示到具體的View上
* @param container 要把圖片顯示到的View
* @param uri 圖片路徑
* @param displayConfig
*/
public <T extends View> void display(T container, String uri, BitmapDisplayConfig displayConfig) {
display(container, uri, displayConfig, null);
}
/**
* 根據(jù)圖片路徑,顯示到具體的View上
* @param container 要把圖片顯示到的View
* @param uri 圖片路徑
* @param callBack 加載過(guò)程回調(diào)各種狀態(tài)
*/
public <T extends View> void display(T container, String uri, BitmapLoadCallBack<T> callBack) {
display(container, uri, null, callBack);
}
/**
* 根據(jù)圖片路徑,顯示到具體的View上
* @param container 要把圖片顯示到的View
* @param uri 圖片路徑
* @param displayConfig 位圖顯示配置
* @param callBack
*/
public <T extends View> void display(T container, String uri, BitmapDisplayConfig displayConfig, BitmapLoadCallBack<T> callBack) {
if (container == null) {
return;
}
if (callBack == null) {
callBack = new DefaultBitmapLoadCallBack<T>();
}
if (displayConfig == null || displayConfig == defaultDisplayConfig) {
displayConfig = defaultDisplayConfig.cloneNew();
}
// Optimize Max
BitmapSize size = displayConfig.getBitmapMaxSize();SizedisplayConfig.setBitmapMaxSize(BitmapCommonUtils.optimizeMaxSizeByView(container, size.getWidth(), size.getHeight()));
container.clearAnimation();
if (TextUtils.isEmpty(uri)) {
callBack.onLoadFailed(container, uri, displayConfig.getLoadFailedDrawable());
return;
}
// start loading
callBack.onPreLoad(container, uri, displayConfig);
// find bitmap from mem cache.
Bitmap bitmap = globalConfig.getBitmapCache().getBitmapFromMemCache(uri, displayConfig);
if (bitmap != null) {
callBack.onLoadStarted(container, uri, displayConfig);
callBack.onLoadCompleted(
container,
uri,
bitmap,
displayConfig,
BitmapLoadFrom.MEMORY_CACHE);
} else if (!bitmapLoadTaskExist(container, uri, callBack)) {
final BitmapLoadTask<T> loadTask = new BitmapLoadTask<T>(container, uri, displayConfig, callBack);
// get executor
PriorityExecutor executor = globalConfig.getBitmapLoadExecutor();
File diskCacheFile = this.getBitmapFileFromDiskCache(uri);
boolean diskCacheExist = diskCacheFile != null && diskCacheFile.exists();
if (diskCacheExist && executor.isBusy()) {
executor = globalConfig.getDiskCacheExecutor();
}
// set loading image
Drawable loadingDrawable = displayConfig.getLoadingDrawable();
callBack.setDrawable(container, new AsyncDrawable<T>(loadingDrawable, loadTask));
loadTask.setPriority(displayConfig.getPriority());
loadTask.executeOnExecutor(executor);
}
}
從這段代碼中我們可以看到,當(dāng)要加載某張圖片時(shí),會(huì)根據(jù)圖片地址進(jìn)行查找是否有對(duì)應(yīng)的bitmap緩存圖片,如果有就直接引用緩存,如果沒(méi)有就加載并緩存,所以我們對(duì)圖片的緩存只需要實(shí)現(xiàn)以上方法就可以了,而且只要設(shè)置相同的緩存路徑,就可以實(shí)現(xiàn)一個(gè)頁(yè)面緩存后,其他頁(yè)面有相同圖片也可以調(diào)用。那么緩存之后,好友更新頭像,又是怎么做到即時(shí)更新的呢?
緩存后如何實(shí)現(xiàn)即時(shí)更新頭像
根據(jù)查閱的資料,可以歸結(jié)為以下幾種實(shí)現(xiàn)方式:
1.在服務(wù)器返回用戶數(shù)組時(shí)多加一個(gè)字段頭像最后一次修改時(shí)間或者修改過(guò)幾次等標(biāo)志符,與緩存進(jìn)行比較,是否有變化
2.利用圖片的checkSum來(lái)實(shí)現(xiàn),如果check到這個(gè)數(shù)字有變化,就會(huì)自動(dòng)去更新
3.利用socket監(jiān)聽,當(dāng)好友頭像更新時(shí)候首先會(huì)告訴服務(wù)器,服務(wù)器將變化通知推送到所有好友,好友監(jiān)聽收到通知后自動(dòng)更新
第一種方法和第二種方法本質(zhì)是一致的,通過(guò)請(qǐng)求服務(wù)器的數(shù)據(jù)與本地緩存進(jìn)行對(duì)比,是由客戶端處理的,第三種方法的話你換一次頭像就要服務(wù)器去提醒你的所有好友一遍,服務(wù)器壓力會(huì)不會(huì)比較大
仔細(xì)去研究一下微信,就會(huì)發(fā)現(xiàn),當(dāng)好友頭像修改后,如果你停留在某個(gè)頁(yè)面,進(jìn)入的這個(gè)頁(yè)面是之前進(jìn)入過(guò)的,還沒(méi)有銷毀,頭像是不會(huì)改變的,你需要打開一個(gè)新的頁(yè)面或者重新進(jìn)入微信,才會(huì)更新頭像,由此看出,微信并不是用的第三種方式,而是采用了前兩種方式的實(shí)現(xiàn)原理,只有在創(chuàng)建一個(gè)Activity或fragment時(shí),調(diào)用接口,讀取服務(wù)器數(shù)據(jù)時(shí)才會(huì)更新頭像
總結(jié)
通過(guò)以上的分析,我們基本捋清了思路,要實(shí)現(xiàn)類似微信的緩存和更新還有頭像,先是在磁盤開辟一個(gè)空間,用于讀寫頭像的Bitmap,然后創(chuàng)建頁(yè)面時(shí),讀取服務(wù)器數(shù)據(jù)和本地緩存進(jìn)行比較,如果有變化就進(jìn)行更新
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
MVVM和MVVMLight框架介紹及在項(xiàng)目中的使用詳解
這篇文章主要為大家介紹了MVVM和MVVMLight的介紹及在項(xiàng)目中的使用詳解有需要的朋友可以借鑒參考下,祝大家除夕快樂(lè)多多進(jìn)步2022-01-01
Android使用gradle讀取并保存數(shù)據(jù)到BuildConfg流程詳解
這篇文章主要介紹了Android使用gradle從資源目錄讀取數(shù)據(jù)并存到BuildConfg內(nèi),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧2023-02-02
logcat命令使用方法和查看android系統(tǒng)日志緩沖區(qū)內(nèi)容的方法
這篇文章主要介紹了logcat命令使用方法和查看android系統(tǒng)日志緩沖區(qū)內(nèi)容的方法,需要的朋友可以參考下2014-02-02
Android模擬開關(guān)按鈕點(diǎn)擊打開動(dòng)畫(屬性動(dòng)畫之平移動(dòng)畫)
這篇文章主要介紹了Android模擬開關(guān)按鈕點(diǎn)擊打開動(dòng)畫(屬性動(dòng)畫之平移動(dòng)畫)的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-09-09
取消Android Studio項(xiàng)目與SVN關(guān)聯(lián)的方法
今天小編就為大家分享一篇關(guān)于取消Android Studio項(xiàng)目與SVN關(guān)聯(lián)的方法,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2018-12-12
Android動(dòng)畫教程之屬性動(dòng)畫詳解
這篇文章主要給大家介紹了關(guān)于Android動(dòng)畫教程之屬性動(dòng)畫的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)各位Android開發(fā)者們具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-05-05

