Пользовательский интерфейс быстро замедляется по мере обновления графика - PullRequest
0 голосов
/ 10 мая 2019

Я занимаюсь разработкой мобильного приложения с использованием Xamarin.Forms.Приложение подключается к устройству BLE, которое передает 16 байтов данных каждые 100 мс.Я строю данные с помощью Syncfusion в формате гистограммы.

Я могу подключиться к устройству и получать данные без проблем.Но через очень короткое время приложение начинает значительно снижать производительность.Вскоре после этого он полностью останавливается.Очевидно, я делаю что-то не так при обработке входящих данных (если только это не является проблемой производительности с диаграммой Syncfusion).

В двух словах, это процесс, который я прохожу в приложении

  1. Сопряжение с устройством (вне приложения)
  2. Подключение к устройству (в приложении)
  3. Настройка передачи
  4. Обработка входящих данных с помощьюModel под названием SpectrogramModel
  5. График данных с помощью Syncfusion в View, называемом DataPage, который связан с ViewModel, называемым DataViewModel

.все, после сопряжения и подключения к устройству вызывается следующий метод.Может ли это быть вызов Device.BeginInvokeOnMainThread(), который в конечном итоге начинает блокировать приложение?Этот метод вызывается из Connection класса, который имеет ссылку на DataViewModel

private void UpdateSpectrogramChart(object sender, EventArgs e)
    {
        DebugHelper.Message(Type.Method, "UpdateSpectrogramChart");

        _characteristic.ValueUpdated += (o, args) =>
        {
            var raw = args.Characteristic.Value;

            for (int i = 0; i < raw.Length; i++)
            {
                Debug.WriteLine("Level[{0}] = {1}", i, raw[i]);
            }

            Xamarin.Forms.Device.BeginInvokeOnMainThread(() =>
            {
                DataPageViewModel.Levels.Clear();

                for (int i = SpectrogramModel.FrequencyOffset; i < raw.Length; i++)
                {
                    if (SettingsViewModel.IsViewRawData)
                    {
                        DataPageViewModel.Title = "Raw data";

                        DataPageViewModel
                        .Levels
                        .Add(
                            new SpectrogramModel(
                                raw[i],
                                1 + (i - SpectrogramModel.FrequencyOffset))
                                );
                    }
                    if (SettingsViewModel.IsViewProcessedData)
                    {
                        DataPageViewModel.Title = "Processed data";

                        DataPageViewModel
                        .Levels
                        .Add(
                            new SpectrogramModel(
                                raw[i],
                                1 + (i - SpectrogramModel.FrequencyOffset),
                                i));
                    }
                }
            });
        };
    }

SpectrogramModel выглядит следующим образом

public class SpectrogramModel
{
    public SpectrogramModel(byte level, int frequency)
    {
        Level = level;
        Frequency = frequency;
    }

    public SpectrogramModel(byte level, int frequency, int index) : this(level, frequency)
    {
        Level = ProcessRawLevel(level, index);
    }

    private double ProcessRawLevel(byte b, int index)
    {
        double multiplier = 0.75;
        double val = b;
        val *= multiplier;
        return val;
    }

    public static readonly int FrequencyOffset = 4;

    ...

DataPage выглядит следующим образом

<chart:SfChart>

        <chart:SfChart.Title>
            <chart:ChartTitle
                        Text="{Binding Title}">
            </chart:ChartTitle>
        </chart:SfChart.Title>

        <chart:SfChart.PrimaryAxis>

            <chart:CategoryAxis>
            </chart:CategoryAxis>

        </chart:SfChart.PrimaryAxis>

        <chart:SfChart.SecondaryAxis>

            <chart:NumericalAxis
                Minimum="20" 
                Maximum="100">
            </chart:NumericalAxis>

        </chart:SfChart.SecondaryAxis>

        <chart:SfChart.Series>

            <chart:ColumnSeries ItemsSource="{Binding Levels}" XBindingPath="Frequency" YBindingPath="Level"/>

        </chart:SfChart.Series>

    </chart:SfChart>

Наконец, DataViewModel, к которому привязан DataPage

public class DataViewModel : BaseViewModel
{
    public DataViewModel()
    {
        Init();
    }

    private void Init()
    {
        Levels = new ObservableCollection<SpectrogramModel>();

        for (int i = 0; i < 16; i++) Levels.Add(new SpectrogramModel(20, i));
    }

    private ObservableCollection<SpectrogramModel> _levels;
    public ObservableCollection<SpectrogramModel> Levels
    {
        get { return _levels; ; }
        set
        {
            _levels = value;
            OnPropertyChanged();
        }
    }

    private string _title;
    public string Title
    {
        get { return _title; }
        set
        {
            _title = value;
            OnPropertyChanged();
        }
    }
}

Следует отметить, что UpdateSpectrogramChart() обернут в таймер, который выглядит следующим образом

public void InitTimers()
    {
        DebugHelper.Message(Type.Method, "InitTimers");
        int SECOND = 1000 * 2;
        SpectrogramChartTimer = new Timer();
        SpectrogramChartTimer.Elapsed += new ElapsedEventHandler(UpdateSpectrogramChart);
        SpectrogramChartTimer.Interval = SECOND;
    }

Я обернул вызов метода UpdateSpectrogramChart() в (сбрасывать) неудачную попытку уменьшить снижение производительности.

Для полноты, вот тело метода, которое устанавливает прием от устройства BLE

public async Task ReceiveFromGattCharacteristic(string service, string characteristic, string descriptor = null)
    {
        DebugHelper.Message(Type.Method, "ReceiveFromGattCharacteristic");

        bleAdapter.DeviceConnected += async (s, e) =>
        {
            try
            {
                DebugHelper.Message(Type.Info, "bleAdapter.DeviceConected += async (s, e) ...");

                string[] deviceInfo = { e.Device.Name, e.Device.Id.ToString() };

                // Connect to service
                try
                {
                    DebugHelper.Message(Type.Info, "Connecting to service...");
                    _service = await e.Device.GetServiceAsync(Guid.Parse(service));
                    DebugHelper.Message(Type.Info, "OK");
                }

                catch (Exception)
                {
                    DebugHelper.Error(ErrorType.GATT, "Could not connect to service");
                }

                // Connect to characteristic
                try
                {
                    DebugHelper.Message(Type.Info, "Connecting to characteristic...");
                    _characteristic = await _service.GetCharacteristicAsync(Guid.Parse(characteristic));
                    DebugHelper.Message(Type.Info, "OK");
                }

                catch (Exception)
                {
                    DebugHelper.Error(ErrorType.GATT, "Could not connect to characteristic");
                }

                await ConfigureSpectrogram(UpdateFrequency.High, 0x1);

                try
                {
                    await _characteristic.StartUpdatesAsync();
                }

                catch
                {
                    DebugHelper.Error(ErrorType.GATT, "Error starting UpdatesAsync");
                }

                _characteristic.ValueUpdated += (o, args) =>
                {
                    var raw = args.Characteristic.Value;

                    for (int i = 4; i < raw.Length; i++)
                    {
                        Debug.WriteLine("Level[{0}] = {1}", i - 4, raw[i]);
                    }
                };
            }

            catch (Exception)
            {
                DebugHelper.Error(ErrorType.GATT, "Error in ReceiveFromGattCharacteristic");
            }
        };
    }

Ответы [ 2 ]

0 голосов
/ 14 мая 2019

Мы хотели бы сообщить вам, что некоторые конфигурации SfChart необходимо учитывать при использовании огромного количества данных и повышении производительности.

  1. Используйте SuspendSeriesNotification и ResumeSeriesNoification.

Мы можем остановить обновление диаграммы для каждой модификации в исходной коллекции элементов.С помощью методов SuspendSeriesNotification и ResumeSeriesNotification.

Xamarin.Forms.Device.BeginInvokeOnMainThread(() =>
            {
                DataPageViewModel.Levels.Clear();

Chart.SuspendSeriesNotification();

                for (int i = SpectrogramModel.FrequencyOffset; i < raw.Length; i++)
                {
                    if (SettingsViewModel.IsViewRawData)
                    {
                        DataPageViewModel.Title = "Raw data";

                        DataPageViewModel
                        .Levels
                        .Add(
                            new SpectrogramModel(
                                raw[i],
                                1 + (i - SpectrogramModel.FrequencyOffset))
                                );
                    }
                    if (SettingsViewModel.IsViewProcessedData)
                    {
                        DataPageViewModel.Title = "Processed data";

                        DataPageViewModel
                        .Levels
                        .Add(
                            new SpectrogramModel(
                                raw[i],
                                1 + (i - SpectrogramModel.FrequencyOffset),
                                i));
                    }
                }
Chart.ResumeSeriesNotification();
            });
Избегайте использования категории Ось.

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

Некоторые советы по повышению производительности от SfChart читайте в блоге ниже.https://blog.syncfusion.com/post/7-tips-to-optimize-xamarin-charts-performance.aspx#comment-10677

С уважением, Бхарати.

0 голосов
/ 10 мая 2019

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

После игры с BackgroundWorker, которая привела к еще большему количеству ошибок (возможно, потому что я не являюсь экспертом по ее использованию), я пересмотрел код и перенес обновление Model и View прямо в ReceiveFromGattCharacteristic(), метод вместо обновления Model и View в отдельном методе, следующим образом:

public void ReceiveFromGattCharacteristic(string service, string characteristic, string descriptor = null)
    {
        DebugHelper.Message(Type.Method, "ReceiveFromGattCharacteristic");

        bleAdapter.DeviceConnected += async (s, e) =>
        {
            try
            {
                DebugHelper.Message(Type.Info, "bleAdapter.DeviceConected += async (s, e) ...");

                string[] deviceInfo = { e.Device.Name, e.Device.Id.ToString() };

                // Connect to service
                try
                {
                    DebugHelper.Message(Type.Info, "Connecting to service...");
                    _service = await e.Device.GetServiceAsync(Guid.Parse(service));
                    DebugHelper.Message(Type.Info, "OK");
                }

                catch (Exception)
                {
                    DebugHelper.Error(ErrorType.GATT, "Could not connect to service");
                }

                // Connect to characteristic
                try
                {
                    DebugHelper.Message(Type.Info, "Connecting to characteristic...");
                    _characteristic = await _service.GetCharacteristicAsync(Guid.Parse(characteristic));
                    DebugHelper.Message(Type.Info, "OK");
                }

                catch (Exception)
                {
                    DebugHelper.Error(ErrorType.GATT, "Could not connect to characteristic");
                }

                await ConfigureSpectrogram(UpdateFrequency.High, 0x1);

                try
                {
                    await _characteristic.StartUpdatesAsync();
                }

                catch
                {
                    DebugHelper.Error(ErrorType.GATT, "Error starting UpdatesAsync");
                }
                // ADDITION
                _characteristic.ValueUpdated += (o, args) =>
                {
                    var raw = args.Characteristic.Value;

                    Xamarin.Forms.Device.BeginInvokeOnMainThread(() =>
                    {
                        DataPageViewModel.Levels.Clear();

                        for (int i = Models.Spectrogram.FrequencyOffset; i < raw.Length; i++)
                        {
                            if (SettingsViewModel.IsViewRawData)
                            {
                                DataPageViewModel.Title = "Raw data";

                                DataPageViewModel
                                .Levels
                                .Add(
                                    new Models.Spectrogram(
                                        raw[i],
                                        1 + (i - Models.Spectrogram.FrequencyOffset))
                                        );
                            }

                            if (SettingsViewModel.IsViewProcessedData)
                            {
                                DataPageViewModel.Title = "Processed data";

                                DataPageViewModel
                                .Levels
                                .Add(
                                    new Models.Spectrogram(
                                        raw[i],
                                        1 + (i - Models.Spectrogram.FrequencyOffset),
                                        i));
                            }
                        }
                    });
                };
            }
            // END OF ADDITION

            catch (Exception)
            {
                DebugHelper.Error(ErrorType.GATT, "Error in ReceiveFromGattCharacteristic");
            }
        };
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...