• 0
Rusland

[Android] программа закрывается при использовании LocationSensor и Memo

Вопрос

Происходит непонятное явление при определении GPS-координат.

На форме TLocationSensor (LSensor) для получения координат, TMemo (Memo1) для вывода информации и несколько Label (LabLat, LabLon).

Вот такой код 

procedure TForm1.LSensorLocationChanged(Sender: TObject;
  const OldLocation, NewLocation: TLocationCoord2D);
var Lat,Lon:String;
begin
  try
    Lat:=LSensor.Sensor.Latitude.ToString;
    Lon:=LSensor.Sensor.Longitude.ToString;
    LabLat.Text:='Широта:  '+Lat;
    LabLon.Text:='Долгота: '+Lon;
    Memo1.Lines.Add(Lat+';'+Lon);
  finally
  end;
end;

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

 

Кто виноват? Что делать?

Можно ли как-то отловить ошибку и записать, например, в лог-файл (чтобы понять какой компонент виноват)? Если убираю Memo, то программа не закрывается.

Изменено пользователем Rusland

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


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

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

  • 0

Событие приходит в другом потоке. А с UI можно работать только в главном потоке. Поэтому перед тем, как что-то записывать в мемо, эту запись нужно завернуть в TThread.Synchronize.

procedure TForm17.LocationSensor1LocationChanged(Sender: TObject; const [Ref] OldLocation,
  NewLocation: TLocationCoord2D);
begin
  TThread.Synchronize(procedure
  begin
    Memo1.Lines.Add('');
  end);
end;
Rusland и Kitty понравилось это

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


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

Brovin Yaroslav, не уверен что понял... кто создал тот другой поток, LocationSensor (я то потоков не создаю)? Из-за этого возникает ошибка при обращении к переменным LSensor.Sensor.Latitude и LSensor.Sensor.Longitude ? Тогда как объяснить то что программа работает, если я значения записываю в Label-ы (программа тоже должна рушиться)?

UI? Напомню, что приложение я запускаю на своем телефоне Android (вер.4.2.2)

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


Ссылка на сообщение
Поделиться на других сайтах
  • 0
  1. То что вы не создаете, это не означает, что ваша программа использует всего один поток. Этот сенсор на андроиде работает в другом потоке. Вам нужно понимать только одно, что работать с UI контролами можно только в UI потоке. 
  2. На счет TLabel - вам просто повезло. Это простой контрол. А вот с TMemo, как вы поняли, такое везение не проходит. Так как TMemo вычисляет размеры линий. И если вы в момент вычисления линий, добавляете или удаляете линию, то можно спокойно поймать EArgumentOfRange исключение
Rusland понравилось это

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


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

Очень часто вижу реализацию через Мемо напрямую, в частности на этом форуме :) Странно что там у людей работало )

 

 

procedure TForm17.LocationSensor1LocationChanged(Sender: TObject; const [Ref] OldLocation,
  NewLocation: TLocationCoord2D);
begin
  TThread.Synchronize(procedure
  begin
    Memo1.Lines.Add('');
  end);
end;

Объясните пожалуйста как правильно сделать Synchronize? (Выше приведенный код не работает)

 

 

 то можно спокойно поймать EArgumentOfRange исключение

И почему это исключение не вываливается на экран как ошибка, а приложение просто берет и закрывается?

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


Ссылка на сообщение
Поделиться на других сайтах
  • 0
TThread.Synchronize(nil, procedure
begin
  Memo1.Lines.Add('');
end);
И почему это исключение не вываливается на экран как ошибка, а приложение просто берет и закрывается?

 

потому что работает в другом потоке!

Изменено пользователем ZuBy
Rusland и Brovin Yaroslav понравилось это

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


Ссылка на сообщение
Поделиться на других сайтах
  • 0
procedure TForm17.LocationSensor1LocationChanged(Sender: TObject; const [Ref] OldLocation,
  NewLocation: TLocationCoord2D);
begin
  TThread.Synchronize(procedure
  begin
    Memo1.Lines.Add('');
  end);
end;

А как этот код будет выглядеть в С++ Builder?

Спасибо.

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


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

Kitty, наверное что-то типа: 

  TThread::Synchronize(
  void
  {
   Memo1->Lines->Add("");
  });

но не уверен

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


Ссылка на сообщение
Поделиться на других сайтах
  • 0
TThread.Synchronize(nil, procedure
begin
  Memo1.Lines.Add('');
end);

Спасибо. Не вылетает  ;)

 

 

 

Вам нужно понимать только одно, что работать с UI контролами можно только в UI потоке. 

Теперь становится яснее.

C какими еще компонентами кроме Memo надо работать через Synchronize?

 

Какое ограничение на размер строк в Мемо?

Изменено пользователем Rusland

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


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

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

 

ограничения по Memo я поставил 1000 символом и после чего очищаю его (для логов использовал)

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

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


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

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

Я вот тоже прикрутил TMapView и в зависимости от показаний LocationSensor ставлю на карте Маркер текущего положения. Поставил Timer и в нем вызываю вот такую процедуру:

 
procedure TForm2.SetMarker();
var Descr: TMapMarkerDescriptor;
    MyLocation: TMapCoordinate;
begin
  TThread.Synchronize(nil, procedure
  begin
    try
      if (TabControl.ActiveTab=TabMap)and((PrevLat<>LSensor.Sensor.Latitude) or (PrevLon<>LSensor.Sensor.Longitude)) then
      begin
        PrevLat:=LSensor.Sensor.Latitude;
        PrevLon:=LSensor.Sensor.Longitude;
        MyLocation:=TMapCoordinate.Create(LSensor.Sensor.Latitude,LSensor.Sensor.Longitude);
        MapView.Location:=MyLocation;
        Descr:=TMapMarkerDescriptor.Create(MyLocation);
        Descr.Draggable:=True;
        if Assigned(MyMarker) then MyMarker.Remove; // удаляю предыдущий маркер
        MyMarker:=MapView.AddMarker(Descr);         // добавляю на карту новый 
      end;
    except
      ShowMessage('Произошла непредвиденная ошибка');
    end;
  end);
end;

Во-первых, на текущий момент программа периодически закрывается, хоть и используется Synchronize. От чего это может происходить?

Во-вторых, если я вызываю эту процедуру прямо из обработчика LocationChanged, то программа зависает. Нельзя слишком часто обращаться к компоненту MapView? Можно ли как-то узнать что в MapView загрузка завершена?

 

Ничего лучше кроме маркера не придумал. Может есть другие варианты указания текущего местоположения?

Есть параметр скорость LSensor.Sensor.Speed - но он показывает что-то странное. При движении 40-50км в час, он показывает 9-10. Как правильно узнать скорость?

Изменено пользователем Rusland

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


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

Если у вас зависает программа, то замените Synchronize на TThread.Queue. Это точно должно помочь

 

Этим летом мне выделили время и я плотно занимался проблемой "черного экрана" на андроиде и креша при закрытии приложения на Андроиде. На форуме было очень много обсуждений этой проблемы. Поэтому есть пара советов, как ее избежать в текущих версия среды:

  1. Все операции требуемые выполнять в Delphi UI потоке нужно выполнять в Synchronize или Queue. Второе предпочтительнее. Отличие между Synchronize и Queue - только в том, что первое остановит выполнение потока до тех пор, пока не выполниться код в SynchronizeQueue наборот, поставить в очередь ваш код на выполнение. Поэтому если вы можете выполнять вашу задачу асинхронно, то лучше использовать Queue, если нет, то Synchronize.

В вашем случае вы можете выполнить добавление маркера асинхронно, так как результат сенсора вы не используете в будущем для вычислений. То есть у вас только задача отображения данных в Real Time. Поэтому 

Rusland, zairkz, Kitty и 1 другому понравилось это

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


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

Подскажите, пожалуйста, где можно найти пример использования Queue в С++ Builder. Спасибо.

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


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

Если у вас зависает программа, то замените Synchronize на TThread.Queue. Это точно должно помочь

 

Этим летом мне выделили время и я плотно занимался проблемой "черного экрана" на андроиде и креша при закрытии приложения на Андроиде. На форуме было очень много обсуждений этой проблемы. Поэтому есть пара советов, как ее избежать в текущих версия среды:

  1. Все операции требуемые выполнять в Delphi UI потоке нужно выполнять в Synchronize или Queue. Второе предпочтительнее. Отличие между Synchronize и Queue - только в том, что первое остановит выполнение потока до тех пор, пока не выполниться код в SynchronizeQueue наборот, поставить в очередь ваш код на выполнение. Поэтому если вы можете выполнять вашу задачу асинхронно, то лучше использовать Queue, если нет, то Synchronize.

В вашем случае вы можете выполнить добавление маркера асинхронно, так как результат сенсора вы не используете в будущем для вычислений. То есть у вас только задача отображения данных в Real Time. Поэтому 

 

Вот спасибо огроменное! Заменил Synchronize на Queue и не зависает теперь при вызове из LocationChanged  :D (не люблю Timer-ы)

 

PS. помогите кто-нибудь Kitty

Изменено пользователем Rusland

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


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

Если у вас зависает программа, то замените Synchronize на TThread.Queue. Это точно должно помочь

 

Этим летом мне выделили время и я плотно занимался проблемой "черного экрана" на андроиде и креша при закрытии приложения на Андроиде. На форуме было очень много обсуждений этой проблемы. Поэтому есть пара советов, как ее избежать в текущих версия среды:

  1. Все операции требуемые выполнять в Delphi UI потоке нужно выполнять в Synchronize или Queue. Второе предпочтительнее. Отличие между Synchronize и Queue - только в том, что первое остановит выполнение потока до тех пор, пока не выполниться код в SynchronizeQueue наборот, поставить в очередь ваш код на выполнение. Поэтому если вы можете выполнять вашу задачу асинхронно, то лучше использовать Queue, если нет, то Synchronize.

В вашем случае вы можете выполнить добавление маркера асинхронно, так как результат сенсора вы не используете в будущем для вычислений. То есть у вас только задача отображения данных в Real Time. Поэтому 

 

Это все конечно хорошо, но не всегда работает.

Вот такая зверская конструкция на Windows отрабатывается на ура, а на андроиде бывают лаги

TTask.Run(procedure 
begin 
  // тут обращение к базе например
  TThread.Synchronize(nil, procedure
  begin
     //  тут разбираем данные
    TTask.Run(procedure 
    begin
      // тут сохраняем
      TThread.Queue(nil, procedure 
      begin
         // тут обновляем данные в визуальных компонентах
      end);
    end);
   end);
end);

пришлось отказаться от такой конструкции, но иногда она жутко удобна

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

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


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

Вот такая зверская конструкция на Windows отрабатывается на ура, а на андроиде бывают лаги

 

 

{$IFDEF offtop}Мсье знает толк в извращениях :D {$ENDIF}

Равиль Зарипов (ZuBy) и zairkz понравилось это

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


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

Создайте аккаунт или войдите для комментирования

Вы должны быть пользователем, чтобы оставить комментарий

Создать аккаунт

Зарегистрируйтесь для получения аккаунта. Это просто!


Зарегистрировать аккаунт

Войти

Уже зарегистрированы? Войдите здесь.


Войти сейчас

  • Сейчас на странице   0 пользователей

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