3

Android用Schartを使用してリアルタイムグラフアプリを書いています。使ってきました

データ系列のラッパーとしてのFastLineRenderableSeries

しかし、グラフの速度を最大化するために、Android SciChart で他にどのような手法が存在するのでしょうか?

特に、IXyDataSeriesを使用して x 軸のサイズを 10,000 から 100,000 ポイントに増やすと、パフォーマンスが低下することに気付きました。IXyDataSeriesに約 90,000 ポイントを追加するまで、グラフ作成の速度は一貫して高速のままです。

みんなありがとう。私はstackoverflowを初めて使用しています... CSの人というよりはmechEの方が多いです。

UDP センサー データを文字列として取り込み、スプライスして IXyDataSeries に追加する私の graphFragment クラスを次に示します。

public class GraphFragment extends Fragment { 

    //Various fields...
    //UDP Settings
    private UdpClient client;
    private String hostname;
    private int remotePort;
    private int localPort;

    //Use to communicate with UDPDataClass
    private Handler handler;

    private boolean listenerExists = false;
    private int xBound = 100000; //**Graphing Slows if xBound is TOO large**
    private int yBound = 5000;
    private boolean applyBeenPressed = false;

    private GraphDataSource dataSource; //Gets data from UDPDataClass
    private SciChartSurface plotSurface; //Graphing Surface
    protected final SciChartBuilder sciChartBuilder = SciChartBuilder.instance();

    //Data Series containers
    //Perhaps it would be better to use XyySeries here?
    private final IXyDataSeries<Double, Double> dataSeriesSensor1 = sciChartBuilder.newXyDataSeries(Double.class, Double.class).build();
    private final IXyDataSeries<Double, Double> dataSeriesSensor2 = sciChartBuilder.newXyDataSeries(Double.class, Double.class).build();
    private final IXyDataSeries<Double, Double> dataSeriesSensor3 = sciChartBuilder.newXyDataSeries(Double.class, Double.class).build();
    private final IXyDataSeries<Double, Double> dataSeriesSensor4 = sciChartBuilder.newXyDataSeries(Double.class, Double.class).build();
    private final IXyDataSeries<Double, Double> dataSeriesSensor5 = sciChartBuilder.newXyDataSeries(Double.class, Double.class).build();
    private final IXyDataSeries<Double, Double> dataSeriesSensor6 = sciChartBuilder.newXyDataSeries(Double.class, Double.class).build();
    private ArrayList<IXyDataSeries<Double,Double>> dataSeriesList = new ArrayList<>(Arrays.asList(dataSeriesSensor1,dataSeriesSensor2,dataSeriesSensor3,dataSeriesSensor4, dataSeriesSensor5, dataSeriesSensor6));
    private ArrayList<Double> xCounters = new ArrayList<>(Arrays.asList(0.0,0.0,0.0,0.0,0.0,0.0));

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    final View frag = inflater.inflate(R.layout.graph_fragment, container, false);

    plotSurface = (SciChartSurface) frag.findViewById(R.id.dynamic_plot);

    dataSource = new GraphDataSource(); //Run the data handling on a separate thread
    dataSource.start();

    UpdateSuspender.using(plotSurface, new Runnable() {
        @Override
        public void run() {
            final NumericAxis xAxis = sciChartBuilder.newNumericAxis().withVisibleRange(0,xBound).build();
            final NumericAxis yAxis = sciChartBuilder.newNumericAxis().withVisibleRange(0,yBound).build();

            //These are wrappers for the series we will add the data to...It contains the formatting
            final FastLineRenderableSeries rs1 = sciChartBuilder.newLineSeries().withDataSeries(dataSeriesSensor1).withStrokeStyle(ColorUtil.argb(0xFF, 0x40, 0x83, 0xB7)).build(); //Light Blue Color
            final FastLineRenderableSeries rs2 = sciChartBuilder.newLineSeries().withDataSeries(dataSeriesSensor2).withStrokeStyle(ColorUtil.argb(0xFF, 0xFF, 0xA5, 0x00)).build(); //Light Pink Color
            final FastLineRenderableSeries rs3 = sciChartBuilder.newLineSeries().withDataSeries(dataSeriesSensor3).withStrokeStyle(ColorUtil.argb(0xFF, 0xE1, 0x32, 0x19)).build(); //Orange Red Color
            final FastLineRenderableSeries rs4 = sciChartBuilder.newLineSeries().withDataSeries(dataSeriesSensor4).withStrokeStyle(ColorUtil.argb(0xFF, 0xFF, 0xFF, 0xFF)).build(); //White color
            final FastLineRenderableSeries rs5 = sciChartBuilder.newLineSeries().withDataSeries(dataSeriesSensor5).withStrokeStyle(ColorUtil.argb(0xFF, 0xFF, 0xFF, 0x99)).build(); //Light Yellow color
            final FastLineRenderableSeries rs6 = sciChartBuilder.newLineSeries().withDataSeries(dataSeriesSensor6).withStrokeStyle(ColorUtil.argb(0xFF, 0xFF, 0x99, 0x33)).build(); //Light Orange color

            Collections.addAll(plotSurface.getXAxes(), xAxis);
            Collections.addAll(plotSurface.getYAxes(), yAxis);
            Collections.addAll(plotSurface.getRenderableSeries(), rs1, rs2, rs3, rs4, rs5, rs6);
        }
    });

    return frag;
    }

 //This class receives the UDP sensor data as messages to its handler
 //Then it splices the data
 //Adds the data to the IXySeries
 //Then the UpdateSuspender updates the graph
 //New data arrives approx every 50 ms (around 20x a second)
 //Graphing slows when xAxis is increased to ~100,000
 //X data is only counters...Only care about Y data
 public class GraphDataSource extends Thread{

    public void run(){
        Looper.prepare();
        //Get Data from UDP Data Class when its available
        handler = new Handler(){
            public void handleMessage(Message msg){
                String sensorData = msg.getData().getString("data"); //Data receiveds
                if(dataValid(sensorData)){
                    sensorData = sensorData.replaceAll("\\s", "");
                    final String[] dataSplit = sensorData.split(","); //split the data at the commas

                    UpdateSuspender.using(plotSurface, new Runnable() {    //This updater graphs the values
                            @Override
                            public void run() {
                                spliceDataAndAddData(dataSplit);
                            }
                        });
                }
            }
        };
        Looper.loop();
    }

    /**
     *
     * @param data string of the udp data
     * @return true if the data isn't corrupted..aka the correct length
     */
    private boolean dataValid(String data){
        return ((data.length() == 1350));
    }

    /**
     *
     * @param dataSplit String[] of the entire data
     *  Adds the each sensor data to the IXySeries representing the data
     */
    private void spliceDataAndAddData(String[] dataSplit){
        addToSensorSeries(dataSplit, 1);
        addToSensorSeries(dataSplit, 2);
        addToSensorSeries(dataSplit, 3);
        addToSensorSeries(dataSplit, 4);
        addToSensorSeries(dataSplit, 5);
        addToSensorSeries(dataSplit, 6);
    }

    /**
     *
     * @param dataSplit data to split into individual sensor array
     *                  must contain only string representations of numbers
     * @param sensorSeriesNumber which sensors to collect the data points of
     * Adds the data to the corresponding IXySeries 
     */
    private void addToSensorSeries(String[] dataSplit, int sensorSeriesNumber){
        sensorSeriesNumber -= 1;  //Adds each value individually to the series
        double xcounter = xCounters.get(sensorSeriesNumber);
        int i = sensorSeriesNumber;
        int dataSize = dataSplit.length - 1;
        String num = "";
        while(true){
            if(i < 6){ //This is the base case...add the first set of data
                num = dataSplit[i];
                try {
                    if(xcounter > xBound){
                        xcounter = 0;
                        dataSeriesList.get(sensorSeriesNumber).clear();
                    }
                    dataSeriesList.get(sensorSeriesNumber).append(xcounter, Double.parseDouble(num)); //appends every number...
                }catch (Exception e){
                    //Corrupt data
                }
            }else if((i) <= dataSize && i >= 6){ //Will start to get hit after the second time
                num = dataSplit[i];
                try {
                    if(xcounter > xBound){
                        xcounter = 0;
                        dataSeriesList.get(sensorSeriesNumber).clear();
                    }
                    dataSeriesList.get(sensorSeriesNumber).append(xcounter, Double.parseDouble(num));
                }catch (Exception e){
                    //Corrupt data
                }
            }else{
                break;
            }
            xcounter++;
            i += 6;
        }
        xCounters.set(sensorSeriesNumber,xcounter);
    }
}
4

1 に答える 1

4

私はあなたのコードを調べましたが、それについて何かできるかどうかわかりません。あなたの例には、XRange が 0 から 100000 までの 6 つの XyDataSeries が含まれています。これにより、画面上に 600 000 ポイントが表示されます。これは、HTC One でのリアルタイムの例にはかなり適しています。SciChart パフォーマンス デモでは、各シリーズでより多くのポイントを描画できる 3 つの XyDataSeries インスタンスのみの使用を確認できます。

開示: 私は SciChart Android チームの主任開発者です


しかし、コードにいくつかの最適化を追加することで、余分な FPS を得ることができると思います。リアルタイム チャートの主な問題は、チャートを更新するコードにあります。非常に頻繁に呼び出されるため、更新中にいくつかのオブジェクトを作成して保存しないと、Android の GC が原因で問題が発生する可能性があります (GC パスは遅く、 GC がすべての未使用オブジェクトを収集している間、すべてのアプリケーションのスレッドを一時停止できます)。したがって、次のことをお勧めします。

  • アプリケーションのヒープサイズを増やすことをお勧めします。メモリを効率的に使用すると、アプリケーションのメモリが増え、実行するGCが少なくなります。
  • ボックス化/ボックス化解除の量を減らし、頻繁に呼び出されるコード (データ系列の更新など) に割り当てるオブジェクトを減らすようにしてください。基本的に、データ系列を更新するコールバックでのオブジェクトの作成を忘れる必要があります。あなたのコードでは、ボックス化/ボックス化解除が発生する場所がほとんどないことに気付きました。このコードは毎秒呼び出され、一部のメソッドはループ内で呼び出されるため、ボックス化/ボックス化解除の効果がアプリケーションのパフォーマンスに大きく影響する可能性があります。

dataSeriesList.get(sensorSeriesNumber).append(xcounter, Double.parseDouble(num));

double xcounter = xCounters.get(sensorSeriesNumber);

xCounters.set(sensorSeriesNumber,xcounter);

IValue を受け入れる追加オーバーライドを使用することをお勧めします。IValues を受け入れる append を使用すると、大量のデータを頻繁に追加するときに、プリミティブ型の不要なボックス化/ボックス化解除を回避できます。

  • また、XyDataSeries を作成するときに Double が本当に必要でない限り、Float または Integer を使用することをお勧めします。これにより、メモリ消費量を半分に削減できる可能性があり (double を格納するのに 8 バイト vs int/float を格納するのに 4 バイト)、その結果、アプリケーションはより多くの空きメモリを持ち、GC の実行頻度を減らすことができます。

これがあなたを助けることを願っています。

于 2016-10-10T07:38:45.420 に答える