• 0
estra

TThread Как правильно запускать анимацию в отдельном потоке?

Вопрос

Хочу сделать переключение TabItem'ов с 3D эффектом (Красивое 3D переключение вкладок в TabControl). Ну устройстве наблюдаются сильные тормоза при анимации, по-этому хочу запускать в отдельном потоке, но получаю ошибку ALOOPER_POLL_ERROR. Как исправить?

TTabControl_3DRotation.zip

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах

2 ответа на этот вопрос

  • 0

Вся отрисовка выполняется в отдельном потоке (известный как UI Thread), из других потоков рисовать в нем запрещено. При попытке это сделать, вы получите исключение. На андроиде оно несет название "ALOOPER_POLL_ERROR". На других платформах будет немного другой текст ошибки.  Чтобы отрисовка происходила в главном потоке (UI Thread) нужно добавить синхронизацию потоков через 

TThread.Synchronize 

или 

TThread.Queen

Однако, в этом случае ваш поток будет ожидать момент отрисовки в главном потоке и в момент отрисовки простаивать. В итоге вы получите то, от чего пытаетесь убежать. 

В вашем случае, медленная скорость анимации может зависеть от больших картинок в TTabControl. Так же она может зависеть от сложной иерархии объектов на форме.

Andrey Efimov понравилось это

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
  • 0

Да, у меня сложной иерархия объектов на форме, но это не изменить. Так что же, нет никак не решить и просто отказаться? Обидно однако  :( 

Поделиться сообщением


Ссылка на сообщение
Поделиться на других сайтах
Гость
Эта тема закрыта для публикации сообщений.

  • Похожие публикации

    • Автор: gonzales
      Доброго времени суток!
      Решаю следующую задачу, в приложении динамически формируются разные объекты, наследники от одного класса. При формировании объектов заполняется динамический массив этих элементов. Далее я хочу в отдельном потоке для каждого из элементов массива получить его состояние, то есть делаю запрос к серверу. Все это повешено на таймер, каждую секунду должен отрабатываться запрос. Все более менее работает в Windows, а на Андроиде со временем приложение валится. Вот код таймера, для читаемости я удалил куски с различными вариантами E. RootElements - это массив TEssense от которого есть наследники. Функции GetBoardCurrentValue, GetBoardMaxValue - по сути запросы к серверу. 
      Подскажите, правильно ли я оформляю работу с потоками для работы на Андроиде?
      procedure TForm1.MasterTimerTimer(Sender: TObject); begin TTask.Run( procedure var l, d, a: byte; i,j:integer; E: TEssence; p: Pointer; VirtualNode: IXMLNode; VirtualElementNode: IXMLNode; id: byte; begin l := Length(Form1.RoomElements); for j := 0 to l - 1 do begin E := Form1.RoomElements[j]; // Реле if E is TRele then begin d := (E as TRele).Device_ID; a := (E as TRele).Device_Adress; if Form1.GetBoardCurrentValue(d, a) = true then begin TThread.Synchronize(nil, procedure begin (E as TRele).ReleSwitch.IsChecked := Form1.device[d].Board[a].CurrentValue.ToBoolean; end); end; // (E as TRele).ReleOnTimer(E) end // Диммер else if E is TDimmer then begin d := (E as TDimmer).Device_ID; a := (E as TDimmer).Device_Adress; if Form1.GetBoardMaxValue(d, a) = true then begin TThread.Synchronize(nil, procedure begin if (Form1.device[d].Board[a].Type_ID = TType.Светодиод) or (Form1.device[d].Board[a].Type_ID = TType.Диммер220) then begin (E as TDimmer).DimmerValue.Text := (Form1.device[d].Board[a].MaxValue).ToString; end; end); end; // (E as TDimmer).DimmerOnTimer(E) end // Таймер else if E is TSTimer then begin id := (E as TSTimer).STimerIndex; Form1.FillHTTPRequest(0, 0, HTTP_GET_TIMER_INFO, id); if Form1.AnswerIsComming = HTTP_GET_TIMER_INFO then begin TThread.Synchronize(nil, procedure begin if Form1.HTTPAnswer.Data1 = 0 then (E as TSTimer).Interval.Text := 'OFF' else (E as TSTimer).Interval.Text := 'ON' end); end; // (E as TSTimer).STimerOnTimer(E); end; end; end); end;   
    • Автор: Alex Bakulin
      Есть простая вроде бы задачка - в фоне загружать картинки для ListView. Делаю это так:
      procedure TImageThread.Execute; var i: integer; begin for i := 0 to FListView.Items.Count -1 do begin FimageIndex := i; Synchronize(LoadImage); end; end; Ожидается, что с ListView в это время будет относительно комфортно работать, и в фоне в ImageList будут подгружаться картинки и появляться в ListView. А на деле получается так, что все приложение виснет аццки на время загрузки картинок и не отвечает. И это как под Windows так и под Android.
    • Автор: Mazzay
      Всем добрый день!
      Приоритет нитки для Windows имеет тип TThreadPriority:
      enum DECLSPEC_DENUM TThreadPriority : unsigned char { tpIdle, tpLowest, tpLower, tpNormal, tpHigher, tpHighest, tpTimeCritical }; __property TThreadPriority Priority = {read=GetPriority, write=SetPriority, nodefault}; Тут по названиям элементов множества всё предельно понятно.
       
      Для Android тип уже int:
      __property int Priority = {read=GetPriority, write=SetPriority, nodefault}; Нигде не могу найти возможные варианты значений для приоритета. Кто-нибудь знает?
       
    • Автор: notricky
      На Android код, который работает исправно на Win  вызывает ошибку "CalledFromWrongThreadException: Only the original thread that created a view hierarcy can touch its views" 
      Смысл таков, что я пытаюсь показать форму из треда, у которой BorderStyle=none (роли это не играет).
      Решение в Андроиде заключается в том, чтобы пускать через  runOnUiThread  (то есть выполнять интерфейсные штуки в главном потоке). Как я понимаю, в firemonkey эту фичу должен выполнять Synchronize().
      Тем не менее, ошибка возникает.
      А при запуске в режиме дебага на андроид девайсе событие кнопки вообще не срабатывает иногда. А если срабатывает, то возникает описанное выше исключение.
      Я собрал тестовый пример и в нем не сразу видна ошибка, тогда как получил я ее на рабочем проекте.
      Цель: показать бизибокс на время бекграундных действий. Этот бизибокс у меня сначала был просто на каждой форме и я интерфейсно его вызывал, но теперь решил сделать отедльной формой (как и тоаст), но почему так происходит я не понял. Вы что скажете?
       
      unit Unit1; interface uses System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.Controls.Presentation, FMX.StdCtrls; type TForm1 = class(TForm) Button1: TButton; procedure Button1Click(Sender: TObject); private { Private declarations } public { Public declarations } end; TWorkThread = class(TThread) public procedure Execute; override; end; var Form1: TForm1; implementation {$R *.fmx} uses unit2; procedure TForm1.Button1Click(Sender: TObject); var t: TWorkThread; begin t := TWorkThread.Create(True); t.FreeOnTerminate := true; t.Start; end; { TWorkThread } procedure TWorkThread.Execute; begin inherited; Self.Synchronize(procedure begin Form2.Show; Form2.Top := Form1.Top; Form2.Left := Form1.Left; Form2.BringToFront; end); Terminate; end; end.  
    • Автор: Камышев Александр
      Windows, FMX
      Возможно не совсем в тему форума, вопрос по архитектуре серверных служб, хотелось бы услышать мнения.
      Ситуация:
      IdHTTTPServer на каждый запрос создает поток, в этом потоке не обойтись без обращения к пулу данных в памяти. Пул - несколько наборов актуальных данных. Наборы данных асинхронно получаются из БД, имеют связи многие ко многим, один ко многим и периодически кэшируются в память в основном потоке. Т.к. обращение к пулу из потока - соответственно пул должен быть потокозащищенным. После обработки запроса, данные также отправляются в  основном потоке в очередь БД.
      1. Если весь пул закрыть в TCriticalSection - то на время использования его одним потоком все остальные будут ожидать. Обращение к очереди БД из потока получается также должно быть потокозащищенным. Не изящно.
      2. Можно задачу обработки положить в некую потокозащищенную очередь и остановить поток c помощью TSimpleEvent->WaitFor( INFINITE ). Далее в основном потоке работать с пулом данных и очередью БД без критических сессий и, после получения результата, запустить поток SetEvent(). Код будет проще и понятней, однако задачи будут выполняться синхронно, последовательно как и в первом случае.
      3. Можно закрывать TCriticalSection отдельные наборы данных, это возможно несколько увеличит быстродействие (не факт!), усложнит код и увеличит вероятность deadlock, т.к. для обработки одного запроса используется несколько наборов данных. Deadlock не будет, если перед обращением к следующему набору ( critical_section->Enter() ) копировать что необходимо из предыдущего и отпускать его ( critical_section->Leave() ) - тут становится важна стоимость операторов копирования.При больших объемах, стоимость копирования может перекрыть весь профит от частного обращения к наборам.
      TThread полезно использовать при длительных операциях ввода вывода и ресурсоемких операциях, т.е. когда нужно подождать, не останавливая основной поток. Выигрыша в производительности полагаю нет, к тому же переключения критических секций также имеют стоимость. 
      Вопросы:
      1. Какой вариант предпочтительней? Есть стандартные схемы?
      2. Как влияет количество ядер, процессоров, на быстродействие во втором и третьем случае?
    • Автор: david_yusupov
      Вопрос очень прост, как убить запущенный поток не дожидаясь его завершение.
      Пример:
      procedure TBufferThread.Execute;
      begin
          while not Self.Terminated do
          begin
                Sleep(10000);
          end;
      end;
       
      Используя ReportMemoryLeaksOnShutdown показывает, что поток жив, после закрытие программы?
      PS
      Поток так же жив после вызова процедуры   TThread.Terminate (После того как заглянул вовнутрь понял почему, и поэтому возник вопрос, как убить поток?)
    • Автор: 97mik
      Как сделать так, чтобы во время выполнения долговременных операций у меня не "замораживался" интерфейс приложения? 
       
      Например, я выполняю по сети скачивания большого файла и на время скачивания отображаю индикатор загрузки. Но пока файл полностью не загрузиться индикатор не работает.
    • Автор: Tarik02
      Я загружаю картинку в отдельном потоке из сервера(пока из localhost). Иногда бывает, картинка загружается, иногда - нет. Если закрыть программу, выскакивает ошибка:

    • Автор: kidrock1
      Всем привет, друзья. Столкнулся со следующей проблемой. Создаётся поток с целью загрузки картинок во время чтения статьи. Да так чтобы основная форма в момент загрузки не была заморожена. Вот код:
      type ThreadHTTP1=class(TThread) private i:integer; public procedure Execute;override; procedure ShowResult1; end; procedure ThreadHTTP1.Execute; var j:integer; begin inherited; try lStream1 := TMemoryStream.Create; Form3.IdHTTP1.Get('http://totalmma.ru/newsupload/8306.jpg',lStream1); except // ShowMessage('no'); end; Synchronize(ShowResult1); end; procedure ThreadHTTP1.ShowResult1; begin ShowMessage(IntToStr(Form3.IDHTTP1.Response.ContentLength)); Form3.Image1.Bitmap.LoadFromStream(lStream1); Form3.Image1.Visible := true; Form3.Caption:=IntToStr(i)+' kbs'; Form3.IdHTTP1.Free; Stream.Free; end; procedure TForm3.Button1Click(Sender: TObject); var MyHTTP1:ThreadHTTP1; begin MyHTTP1 := ThreadHTTP1.Create(False); end; Так вот проблема в следующем. На винде всё работает. А вот когда компилируешь под андройд и переносишь на телефон, при нажатии на кнопке ничего не происходит. Очень прошу помочь, друзья.
    • Автор: Kikoma
      Следующая ситуация:
       
      Есть база данных с полями: id, product_name, price, cart (корзина) - Думаю пояснять излишне.
       
      по условию cart>0 формируется запрос и заполняется TListBox кастомизированным Item-ом который содержит SpinBox. при изменении SpinBox вызывается процедура, которая вносит изменение в БД (cart) и высчитывает сумму в этом Item.
       
      Все работает, все хорошо, но...
      Задумал я что при SpinBox = 0, у меня этот TListBoxItem исчезал, для этого на изменение SpinBox если он равен 0, я запускаю процедуру формирования (Заполнения) этого TListBox заново.
       
      Вываливается ошибка Access ..to address XXX, при чем при пошаговой трассировки исключение вызывает FMX.Edit строка 3811 CustomEditBox.Change; в procedure TValueRangeCustomEditBox.DoAfterChange; (DELPHI XE6)
       
      т.е. моя процедура полностью отрабатывается (Заполняется новый список Item-ов) и возникает эта ошибка.
       
      При чем на 32-bit Windows все работает нормально, только на андроиде возникает это исключение, при чем приложение продолжает нормально функционировать.
       
      Если я правильно понимаю, то эта процедура DoAfterChange пытается что то сделать с объектом, которого уже нет.
       
      Это баг или я неправильно алгоритм построил?
       
       
  • Сейчас на странице   0 пользователей

    Нет пользователей, просматривающих эту страницу