在淘寶應用首頁,會有不少張圖片,而這些首頁圖片不會常常改變,因此就須要緩存下來。這樣就沒必要每次都從網絡獲取。數組
對於系統緩存,咱們不須要作什麼處理。只須要把網絡圖片的URL賦值給Image控件就好了。這樣系統就會在每次須要用到圖片的時候,有限查找緩存裏有沒有以前下載好的。緩存
自建緩存不給Image控件賦URL,而是把圖片DownLoad下來,生成一個bitmap,而後把bitmap賦值給Image。同時將這個bitmap存儲下來。當下次要用到這幅圖的時候,就直接從存儲的位置找到這幅圖。網絡
下邊這段代碼將uri[]數組中的圖片下載下來,而後經過WriteToFile()函數將圖片保存到本地,同時,記下存儲的文件名。dom
SoftwareBitmap sb = await DownloadImage(uri[i]); if (sb != null) { //sb = await ReadFromFile(fileName[i]); SoftwareBitmapSource source = new SoftwareBitmapSource(); await source.SetBitmapAsync(sb); this.insideImage.Source = source; sb = await DownloadImage(uri[i]); fileName[i] = await WriteToFile(sb); }
當你須要使用圖片的時候,使用下列代碼,經過ReadFromFile()函數將圖片讀取出來就好了。異步
for (int i = 0; i < 50; i++) {
//SoftwareBitmap sb = await DownloadImage(uri[i]); SoftwareBitmap sb = await ReadFromFile(fileName[i]); SoftwareBitmapSource source = new SoftwareBitmapSource();
await source.SetBitmapAsync(sb); this.insideImage.Source = source;
//source.Dispose(); }
下邊我經過對比兩種緩存機制發現各有用武之地。async
1.對於幾百K到幾兆的大圖片,系統緩存有速度優點。ide
2.對於幾K到幾十K的小圖片,自建緩存區有速度優點。函數
測試背景1:三張大圖片,循環33次(共99次) 測試
圖片大小:338k 618k 1810k this
PC測試 |
|||||
系統緩存(CPU週期) |
3066584 |
3058505 |
3079367 |
3078989 |
3076976 |
自建緩存(CPU週期) |
53669280 |
51842991 |
52839051 |
52078772 |
52305373 |
Phone測試 |
|||||
系統緩存(CPU週期) |
31852799 |
32008575 |
32200748 |
31970601 |
31839003 |
自建緩存(CPU週期) |
741909215 |
750950455 |
765863510 |
760865505 |
781048686 |
結論一:對於幾百K到幾兆的大圖片,系統緩存有速度優點。
測試背景2:三張小圖片,循環33次(共99次)
圖片大小:3k 6k 60k
PC測試
系統緩存(CPU週期) |
3057284 |
3057637 |
3080880 |
3063350 |
3059105 |
自建緩存(CPU週期) |
1316247 |
1318369 |
1364584 |
1333684 |
1362956 |
Phone測試
系統緩存(CPU週期) |
32085084 |
31751734 |
31744715 |
31852230 |
32064768 |
自建緩存(CPU週期) |
27114317 |
26041012 |
26821794 |
27365796 |
30211258 |
結論二:對於幾K到幾十K的小圖片,自建緩存區有速度優點。
手機淘寶項目測試數據:
測試背景:50張小圖片,循環一次(共50次)
系統緩存 |
CPU週期 |
23689650 |
21589548 |
25409150 |
25186302 |
23121251 |
RAM |
51 |
52 |
50 |
52 |
52 |
|
自建緩存 |
CPU週期 |
3186761 |
2892837 |
2963193 |
2942235 |
2741501 |
RAM |
61 |
63 |
61 |
60 |
59 |
PS:RAM佔用是峯值,穩定後兩種方式RAM佔用相同。
經過給一個Image控件賦值,來看到效果。
一、系統緩存
系統緩存測試不能經過直接改變url的方式,由於系統緩存是異步的,他不會等一個圖片加載好再加載另外一個圖,而是直接忽略了以前的改變。
private async void test1() { stopwatch.Reset(); stopwatch.Start(); BitmapImage bi = new BitmapImage(); bi.UriSource = new Uri(uri[0]); this.insideImage.Source = bi; } private void insideImage_ImageOpened(object sender, RoutedEventArgs e) { times++; if (times == 50) { stopwatch.Stop(); textBox.Text = "任務"+testnum.ToString()+"用時:" + stopwatch.ElapsedTicks + "."; return; } BitmapImage bi = new BitmapImage(); bi.UriSource = new Uri(uri[times]); this.insideImage.Source = bi; }
二、自建緩存
private async void test2() { stopwatch.Reset(); stopwatch.Start(); for (int i = 0; i < 50; i++) { //SoftwareBitmap sb = await DownloadImage(uri[i]); SoftwareBitmap sb = await ReadFromFile(fileName[i]); SoftwareBitmapSource source = new SoftwareBitmapSource(); await source.SetBitmapAsync(sb); if (i % 3 == 0) { this.insideImage.Source = source; }
else if (i % 3 == 1) { this.insideImage1.Source = source; }
else if (i % 3 == 2) { this.insideImage2.Source = source; } //source.Dispose();
} stopwatch.Stop(); textBox.Text = "任務" + testnum.ToString() + "用時:" + stopwatch.ElapsedTicks + "."; }
附:關鍵代碼代碼
ReadFromFile()函數經過文件名讀取圖片 ,特別注意這句話
SoftwareBitmapsoftwareBitmap = awaitdecoder.GetSoftwareBitmapAsync(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Premultiplied);
必定要加上編碼方式,否則會報錯。
public async Task<SoftwareBitmap> ReadFromFile(string filename) { StorageFile file = await _localFolder.CreateFileAsync(filename, CreationCollisionOption.OpenIfExists); //var file = await StorageFile.GetFileFromApplicationUriAsync(new Uri( filename)); using (IRandomAccessStream stream = await file.OpenAsync(FileAccessMode.Read)) { // Create the decoder from the stream BitmapDecoder decoder = await BitmapDecoder.CreateAsync(stream); // Get the SoftwareBitmap representation of the file SoftwareBitmap softwareBitmap = await decoder.GetSoftwareBitmapAsync(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Premultiplied); return softwareBitmap; } }
WriteToFile()函數將bitmap寫入存儲區
public async Task<string> WriteToFile(SoftwareBitmap softwareBitmap) { string fileName = Path.GetRandomFileName(); if (softwareBitmap != null) { // save image file to cache StorageFile file = await _localFolder.CreateFileAsync(fileName, CreationCollisionOption.OpenIfExists); using (IRandomAccessStream stream = await file.OpenAsync(FileAccessMode.ReadWrite)) { BitmapEncoder encoder = await BitmapEncoder.CreateAsync(BitmapEncoder.JpegEncoderId, stream); encoder.SetSoftwareBitmap(softwareBitmap); await encoder.FlushAsync(); } } return fileName; }
DownloadImage()函數經過url下載圖片,返回bitmap
private async Task<SoftwareBitmap> DownloadImage(string url) { try { HttpClient hc = new HttpClient(); HttpResponseMessage resp = await hc.GetAsync(new Uri(url)); resp.EnsureSuccessStatusCode(); IInputStream inputStream = await resp.Content.ReadAsInputStreamAsync(); IRandomAccessStream memStream = new InMemoryRandomAccessStream(); await RandomAccessStream.CopyAsync(inputStream, memStream); BitmapDecoder decoder = await BitmapDecoder.CreateAsync(memStream); SoftwareBitmap softBmp = await decoder.GetSoftwareBitmapAsync(BitmapPixelFormat.Bgra8, BitmapAlphaMode.Premultiplied); return softBmp; } catch (Exception ex) { return null; } }