[WP8.1UI控件編程]Windows Phone大數據量網絡圖片列表的異步加載和內存優化

11.2.4 大數據量網絡圖片列表的異步加載和內存優化

    虛擬化技術可讓Windows Phone上的大數據量列表沒必要擔憂會一次性加載全部的數據,保證了UI的流程性。對於虛擬化的技術,咱們不單單只是依賴其來給列表加載數據,還能夠利用虛擬化的特性去作更多的事情。虛擬化技術有一個很重要的特性就是,它能夠準確地判斷出哪些列表項處於手機屏幕中,能夠動態地去更新這些數據。基於這樣的特性,咱們能夠給列表的功能作更多的優化。html

    那麼下面咱們基於一個例子來說解利用虛擬化技術去作列表的性能優化。有這麼一個需求,須要實現一個圖片的列表,圖片都是來自網絡的,而後數據集合也很大。作這個網絡圖片列表功能時會面臨着兩個問題,一個是圖片的加載會比較耗時,兩外一個是當不斷地滑動會讓數據集合加載的圖片佔用的內存會愈來愈高。編程

    對於第一個問題,能夠採用異步加載的方式來解決,這樣列表加載完以後,圖片再顯示出來,列表首次加載的速度會很快。那麼咱們能夠經過後臺線程調用網絡請求下載圖片,下載完圖片以後再觸發UI線程把圖片顯示出來。緩存

    第二個問題是要解決內存的問題,那麼可使用弱引用類型(WeakReference類)來存儲圖片的數據。弱引用就是不保證不被垃圾回收器回收的對象,它擁有比較短暫的生命週期,在垃圾回收器掃描它所管轄的內存區域過程當中,一旦發現了只具備弱引用的對象,就會回收它的內存,不過通常狀況下,垃圾回收器的線程優先級很低,也就不會很快發現那些只有弱引用的對象。當內存的使用會影響到程序的流暢運行的時候,垃圾回收器,就會按照優先次序把存在時間長的弱引用對象回收,從而釋放內存。因此弱引用特別適合在當前這種狀況下,佔用大量內存,但經過垃圾回收功能回收之後很容易從新建立的圖片對象。圖片下載完以後會存放在弱引用對象裏面,當檢查到數據被回收的時候,再進行異步加載,固然你也能夠把圖片用獨立存儲存起來,這樣也就免去了再次請求網絡的操做。性能優化

    下面咱們來實現網絡圖片列表的異步加載和內存優化的示例:網絡

代碼清單11-8網絡圖片列表(源代碼:第11章\Examples_11_8)dom

    (1)建立數據實體類Data類,在Data類裏面封裝異步加載圖片和弱引用的邏輯。異步

複製代碼

Data.cs文件主要代碼------------------------------------------------------------------------------------------------------------------    // Data類從INotifyPropertyChanged派生,要實現綁定屬性改變的事件,用於圖片異步請求完成以後能夠更新到UI上
    public class Data: INotifyPropertyChanged
    {        // 圖片名字屬性
        public string Name { get; set; }        // 當前的頁面對象,用於觸發UI線程
        public Page Page { get; set; }        // 圖片的網絡地址
        private Uri p_w_picpathUri;        public Uri ImageUri
        {            get
            {                return p_w_picpathUri;
            }            set
            {                if (p_w_picpathUri == value)
                {                    return;
                }
                p_w_picpathUri = 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 (p_w_picpathUri != null)
                {
                    Task.Factory.StartNew(() =>{ DownloadImage(p_w_picpathUri);});
                }                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;
    }

複製代碼

    (2)使用ListView控件綁定到數據Data對象的數據集合。async

複製代碼

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>

複製代碼

複製代碼

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/p_w_picpaths/2012/152755/2012120917494440.png?index=" + i) });
        }
        listView.ItemsSource=Items;
    }

複製代碼

本文來源於《深刻理解Windows Phone 8.1 UI控件編程》ide

源代碼下載:http://vdisk.weibo.com/s/zt_pyrfNHoezI性能

歡迎關注個人微博@WP林政

WP8.1技術交流羣:372552293

相關文章
相關標籤/搜索