標籤:windows phone 8.1
11.2.4 大資料量網狀圖片列表的非同步載入和記憶體最佳化
虛擬化技術可以讓Windows Phone上的大資料量列表不必擔心會一次性載入所有的資料,保證了UI的流程性。對於虛擬化的技術,我們不僅僅只是依賴其來給列表載入資料,還可以利用虛擬化的特性去做更多的事情。虛擬化技術有一個很重要的特性就是,它可以準確地判斷出哪些清單項目處於手機螢幕中,可以動態地去更新這些資料。基於這樣的特性,我們可以給列表的功能做更多的最佳化。
那麼下面我們基於一個例子來講解利用虛擬化技術去做列表的效能最佳化。有這麼一個需求,需要實現一個圖片的列表,圖片都是來自網路的,然後資料集合也很大。做這個網狀圖片列表功能時會面臨著兩個問題,一個是圖片的載入會比較耗時,兩外一個是當不斷地滑動會讓資料集合載入的圖片佔用的記憶體會越來越高。
對於第一個問題,可以採用非同步載入的方式來解決,這樣列表載入完之後,圖片再顯示出來,列表首次載入的速度會很快。那麼我們可以通過後台線程調用網路請求下載圖片,下載完圖片之後再觸發UI線程把圖片顯示出來。
第二個問題是要解決記憶體的問題,那麼可以使用弱參考型別(WeakReference類)來儲存圖片的資料。弱引用就是不保證不被記憶體回收行程回收的對象,它擁有比較短暫的生命週期,在記憶體回收行程掃描它所管轄的記憶體地區過程中,一旦發現了只具有弱引用的對象,就會回收它的記憶體,不過一般情況下,記憶體回收行程的線程優先順序很低,也就不會很快發現那些只有弱引用的對象。當記憶體的使用會影響到程式的流暢啟動並執行時候,記憶體回收行程,就會按照優先次序把存在時間長的弱引用對象回收,從而釋放記憶體。所以弱引用特別適合在當前這種情況下,佔用大量記憶體,但通過記憶體回收功能回收以後很容易重新建立的圖片對象。圖片下載完之後會存放在弱引用對象裡面,當檢查到資料被回收的時候,再進行非同步載入,當然你也可以把圖片用隔離儲存區 (Isolated Storage)存起來,這樣也就免去了再次請求網路的操作。
下面我們來實現網狀圖片列表的非同步載入和記憶體最佳化的樣本:
代碼清單11-8:網狀圖片列表(原始碼:第11章\Examples_11_8)
(1)建立資料實體類Data類,在Data類裡面封裝非同步載入圖片和弱引用的邏輯。
650) this.width=650;" alt="複製代碼" src="http://common.cnblogs.com/images/copycode.gif" />
Data.cs檔案主要代碼------------------------------------------------------------------------------------------------------------------ // Data類從INotifyPropertyChanged派生,要實現綁定屬性改變的事件,用於圖片非同步請求完成之後可以更新到UI上 public class Data: INotifyPropertyChanged { // 圖片名字屬性 public string Name { get; set; } // 當前的頁面對象,用於觸發UI線程 public Page Page { get; set; } // 圖片的網路地址 private Uri imageUri; public Uri ImageUri { get { return imageUri; } set { if (imageUri == value) { return; } imageUri = value; bitmapImage = null; } } // 若引用對象,用於儲存下載好的圖片對象 WeakReference bitmapImage; // ImageSource屬性用於綁定到列表的Image控制項上 public ImageSource ImageSource { get { if (bitmapImage != null) { // 如果弱引用沒有沒回收,則取弱引用的值 if (bitmapImage.IsAlive) return (ImageSource)bitmapImage.Target; else Debug.WriteLine("資料已經被回收"); } // 弱引用已經被回收那麼則通過圖片網路地址進行非同步下載 if (imageUri != null) { Task.Factory.StartNew(() =>{ DownloadImage(imageUri);}); } return null; } } // 下載圖片的方法 void DownloadImage(object state) { HttpWebRequest request = WebRequest.CreateHttp(state as Uri); request.BeginGetResponse(DownloadImageComplete, request); } // 完成圖片下載的回調方法 async void DownloadImageComplete(IAsyncResult result) { HttpWebRequest request = result.AsyncState as HttpWebRequest; HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(result); // 讀取網路的資料 Stream stream = response.GetResponseStream(); int length = int.Parse(response.Headers["Content-Length"]); // 注意需要把資料流重新複製一份,否則會出現跨線程錯誤 // 網路下載到的圖片資料流,屬於後台線程的對象,不能在UI上使用 Stream streamForUI = new MemoryStream(length); byte[] buffer = new byte[length]; int read=0; do { read = stream.Read(buffer, 0, length); streamForUI.Write(buffer, 0, read); } while (read == length); streamForUI.Seek(0, SeekOrigin.Begin); // 觸發UI線程處理位元影像和UI更新 await Page.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { BitmapImage bm = new BitmapImage(); bm.SetSource(streamForUI.AsRandomAccessStream()); // 把圖片位元影像對象存放到若引用對象裡面 if (bitmapImage == null) bitmapImage = new WeakReference(bm); else bitmapImage.Target = bm; //觸發UI綁定屬性的改變 OnPropertyChanged("ImageSource"); } ); } // 屬性改變事件 async void OnPropertyChanged(string property) { var hander = PropertyChanged; if (hander != null) await Page.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { hander(this, new PropertyChangedEventArgs(property)); }); } public event PropertyChangedEventHandler PropertyChanged; }
650) this.width=650;" alt="複製代碼" src="http://common.cnblogs.com/images/copycode.gif" />
(2)使用ListView控制項綁定到資料Data對象的資料集合。
650) this.width=650;" alt="複製代碼" src="http://common.cnblogs.com/images/copycode.gif" />
MainPage.xaml檔案主要代碼------------------------------------------------------------------------------------------------------------------ <ListView x:Name="listView"> <ListView.ItemTemplate> <DataTemplate> <StackPanel> <TextBlock Text="{Binding Name}" Height="80"></TextBlock> <Image Source="{Binding ImageSource}" Width="200" Height="200"></Image> </StackPanel> </DataTemplate> </ListView.ItemTemplate> </ListView>
650) this.width=650;" alt="複製代碼" src="http://common.cnblogs.com/images/copycode.gif" />
650) this.width=650;" alt="複製代碼" src="http://common.cnblogs.com/images/copycode.gif" />
MainPage.xaml.cs檔案主要代碼------------------------------------------------------------------------------------------------------------------ public MainPage() { InitializeComponent(); // 建立一個有1000個Data對象的資料集合 List<Data> Items = new List<Data>(); for (int i = 0; i < 1000; i++) { // 在網路地址後面加上index=i是為了保證每個網路地址的不一樣 // 這樣就不會產生網路資料緩衝,更加接近真實的網狀圖片列表 Items.Add(new Data { Name = "Test" + i, Page = this, ImageUri = new Uri("http://pic002.cnblogs.com/images/2012/152755/2012120917494440.png?index=" + i) }); } listView.ItemsSource=Items; }
650) this.width=650;" alt="複製代碼" src="http://common.cnblogs.com/images/copycode.gif" />
650) this.width=650;" src="http://images.cnitblog.com/i/152755/201406/021430521454984.png" />
本文來源於《深入理解Windows Phone 8.1 UI控制項編程》
原始碼下載:http://vdisk.weibo.com/s/zt_pyrfNHoezI
歡迎關注我的微博@WP林政
WP8.1技術交流群:372552293
[WP8.1UI控制項編程]Windows Phone大資料量網狀圖片列表的非同步載入和記憶體最佳化