Ленивая загрузка и многопоточность - PullRequest
4 голосов
/ 01 февраля 2012

Я пытаюсь использовать библиотеку iCarousel в monotouch.Я успешно перенес библиотеку, все работает отлично, но приложение вылетает, если вы вводите слишком много UIImageViews с изображениями внутри, что является нормальным, потому что iCarousel похож на UIScrollView.

Я определенно должен каким-то образом использовать ленивую систему загрузки из вторичного потока и отображать только 3-4 изображения одновременно, но я не знаю, как сделать эту работу плавной.

На данный момент я установил это в делегате iCarousel:

bool threadsAlive = true; 

public cDelegate() 
{ 
    ThreadPool.QueueUserWorkItem( delegate { refresh_visible(); } ); 
} 

public override void DidScroll (iCarousel carousel) 
{ 
        scrolling = true; 
} 

public override void DidEndScrollingAnimation (iCarousel carousel) 
{ 
        scrolling = false;  
        //show images that are currently on the screen 
        ThreadPool.QueueUserWorkItem( delegate { ShowCurrent();         } ); 
        //hides images that are not on the screen         
        ThreadPool.QueueUserWorkItem( delegate { hideInvisibleImages(); } ); 
} 

void refresh_visible() 
{ 
        while( threadsAlive ) 
        { 
                while( scrolling ) 
                { 
                        ShowCurrent(); 
                } 
        } 
} 

void refresh_hidden() 
{ 
        while( threadsAlive ) 
        { 
                while( scrolling ) 
                { 
                        hideInvisibleImages(); 
                } 
        } 
} 

public void ShowCurrent() 
{ 
        var          ds = _carousel.DataSource as cDataSource; 
        var left_index  = _carousel.CurrentItemIndex - 1; 
        var right_index = _carousel.CurrentItemIndex + 2; 
        if( left_index  <  0 ) left_index  = 0; 
        if( right_index >= ds.Lista.Count ) right_index = ds.Lista.Count - 1; 
        // 
        for( var i = left_index; i < right_index ; i++ ) 
        { 
                var img = ds.Lista[i]; 
                if( img.Image == null ) 
                { 
                        BeginInvokeOnMainThread( delegate{ 
                                img.Image = UIImage.FromFile( img.UserObject.ToString() ); 
                        }); 
                } 
        } 
} 


void hideInvisibleImages() 
{ 
        Console.WriteLine("ascund!"); 
        var          ds = _carousel.DataSource as cDataSource; 
        var left_index  = _carousel.CurrentItemIndex - 1; 
        var right_index = _carousel.CurrentItemIndex + 2; 
        if( left_index  <  0 ) left_index  = 0; 
        if( right_index >= ds.Lista.Count ) right_index = ds.Lista.Count - 1; 
        // 
        for( var i=0; i<left_index; i++ ) 
        { 
                var img   = ds.Lista[i]; 
                if( img.Image != null ) 
                { 
                        img.Image.Dispose(); 
                        img.Image = null; 
                } 
        } 
        for( var i=right_index; i<ds.Lista.Count; i++ ) 
        { 
                var img   = ds.Lista[i]; 
                if( img.Image != null ) 
                { 
                        img.Image.Dispose(); 
                        img.Image = null; 
                } 
        } 
} 

Код на самом деле очень прост: есть основной поток, который показывает только 1 изображение слева от текущегоИндекс и два изображения заранее, а другой поток, который очищает все остальные изображения, скрывает их.

Работает, память в порядке, но не гладкая на устройстве, немного "зависает" при прокрутке.Есть еще один способ сделать это?Или, может быть, я должен изменить алгоритм?

Ответы [ 2 ]

2 голосов
/ 01 февраля 2012

Я немного запутался, как работает ваша прокрутка, но я думаю, что приведенный ниже код может дать вам неплохую отправную точку для решения этой проблемы.

Как указал usr в разделе комментариев, вы постоянно вращаетесь в цикле и обновляете изображение как можно быстрее (если я правильно понимаю ваш код):

void refresh_visible() 
{ 
        while( threadsAlive ) 
        { 
                while( scrolling ) 
                { 
                        ShowCurrent(); 
                } 
        } 
} 

void refresh_hidden() 
{ 
        while( threadsAlive ) 
        { 
                while( scrolling ) 
                { 
                        hideInvisibleImages(); 
                } 
        } 
} 

Кажется, что хорошей идеей будет иметь соответствующую частоту обновления при любой прокрутке. Вы должны установить частоту обновления, которая составляет от 24 до 30 кадров в секунду.

Примерно так может быть в порядке:

using System.Threading;

class YourClass
{
    // Tick every 42 millisecond or about 24 times per second
    private readonly int _refreshRate = 42;

    private volatile bool _scrolling;
    private Timer _timer;
    YourClass()
    {
        _timer = new Timer(TimerTick, null, 0, _refreshRate);
    }

    public void TimerTick(object state)
    {
        if (_scrolling)
        {
            ShowCurrent();
            HideInvisibleImages();
        }
    }

    void ShowCurrent()
    {
        //...
    }

    void HideInvisibleImages()
    {
        //...
    }
}

Обратите внимание, что если вы создаете и уничтожаете много экземпляров YourClass, вам также следует избавиться от объекта Timer, когда закончите с ним. Делегат Timer будет содержать ссылку на экземпляр YourClass и не позволит собирать мусор.

2 голосов
/ 01 февраля 2012

У вас есть цикл, который не позволяет ЦП любому другому потоку / процессу и приводит к очень высокой загрузке ЦП. Это заставляет его зависать при прокрутке.

Попробуйте использовать Thread.Sleep (1) или небольшое время ожидания в методе refresh_visible.

...