jjzjj

android - SciChart Android 实时绘图 : How to maximize graphing speed?

coder 2023-12-28 原文

我正在使用 Scichart for android 编写一个实时绘图应用程序。我一直在用

FastLineRenderableSeries 作为我的数据系列的包装器

但我想知道为了最大限度地提高绘图速度,Android SciChart 还存在哪些其他技术?

特别是当我使用 IXyDataSeries 并将 x 轴大小从 10,000 点增加到 100,000 点时,我特别注意到性能下降。在我将大约 90,000 个点添加到我的 IXyDataSeries 之前,绘图的速度一直保持很快。

谢谢大家。我是 stackoverflow 的新手……与其说是 CS 人员,不如说是 mechE。

这是我的 graphFragment 类,它以字符串形式接收 UDP 传感器数据,将其拼接并将其添加到 IXyDataSeries。

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);
    }
}

最佳答案

我查看了您的代码,但不确定我们是否可以对此做些什么。您的示例包含 6 个 XyDataSeries,XRange 从 0 到 100000,这在屏幕上给出了 600 000 个点,这对于 HTC One 上的实时示例来说非常好。在 SciChart 性能演示中,您可以看到仅使用 3 个 XyDataSeries 实例,这允许在每个系列中绘制更多点

披露:我是 SciChart Android 团队的首席开发人员


但我认为您可以通过在代码中添加一些优化来获得额外的 FPS。实时图表的主要问题在于更新图表的代码——它经常被调用,所以如果你在更新期间创建了一些对象并且不保存它,那么这可能会导致问题,因为 Android 中的 GC(GC 传递很慢而且它可以在 GC 收集所有未使用的对象时暂停所有应用程序的线程)。所以我建议你接下来做:

  • 我建议增加 heap size在您的应用程序中:如果您有效地使用内存,应用程序将拥有更多内存 - 执行的 GC 将更少。
  • 尝试减少装箱/拆箱的数量,并在频繁调用的代码(例如数据系列更新)中分配较少的对象。基本上,您需要忘记在更新数据系列的回调中创建任何对象。在您的代码中,我注意到很少有地方发生装箱/拆箱。此代码每秒调用一次,并且在循环中调用一些方法,因此装箱/拆箱的效果会显着影响应用程序的性能:

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

double xcounter = xCounters.get(sensorSeriesNumber);

xCounters.set(sensorSeriesNumber,xcounter);

我建议您使用 append override它接受 IValues。当您非常频繁地附加大量数据时,使用接受 IValue 的附加可以避免对基本类型进行不必要的装箱/拆箱。

  • 此外,除非您在创建 XyDataSeries 时确实需要 Double,否则我建议使用 Float 或 Integer。这有可能将内存消耗减少一半(8 个字节用于存储 double 与 4 个字节用于存储 int/float),因此应用程序有更多可用内存,从而可以降低执行 GC 的频率。

希望对您有所帮助。

关于android - SciChart Android 实时绘图 : How to maximize graphing speed?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39810816/

有关android - SciChart Android 实时绘图 : How to maximize graphing speed?的更多相关文章

  1. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

  2. 安卓apk修改(Android反编译apk) - 2

    最近因为项目需要,需要将Android手机系统自带的某个系统软件反编译并更改里面某个资源,并重新打包,签名生成新的自定义的apk,下面我来介绍一下我的实现过程。APK修改,分为以下几步:反编译解包,修改,重打包,修改签名等步骤。安卓apk修改准备工作1.系统配置好JavaJDK环境变量2.需要root权限的手机(针对系统自带apk,其他软件免root)3.Auto-Sign签名工具4.apktool工具安卓apk修改开始反编译本文拿Android系统里面的Settings.apk做demo,具体如何将apk获取出来在此就不过多介绍了,直接进入主题:按键win+R输入cmd,打开命令窗口,并将路

  3. (附源码)vue3.0+.NET6实现聊天室(实时聊天SignalR) - 2

    参考文章搭建文章gitte源码在线体验可以注册两个号来测试演示图:一.整体介绍  介绍SignalR一种通讯模型Hub(中心模型,或者叫集线器模型),调用这个模型写好的方法,去发送消息。  内容有:    ①:Hub模型的方法介绍    ②:服务器端代码介绍    ③:前端vue3安装并调用后端方法    ④:聊天室样例整体流程:1、进入网站->调用连接SignalR的方法2、与好友发送消息->调用SignalR的自定义方法 前端通过,signalR内置方法.invoke()  去请求接口3、监听接受方法(渲染消息)通过new signalR.HubConnectionBuilder().on

  4. ruby-on-rails - 在 Rails 应用程序的前端获取实时日志 - 2

    在Rails3.x应用程序中,我正在使用net::ssh并向远程pc运行一些命令。我想向用户的浏览器显示实时日志。比如,如果两个命令在net中运行::ssh执行即echo"Hello",echo"Bye"被传递然后"Hello"应该在执行后立即显示在浏览器中。这是代码我在ruby​​onrails应用程序中使用ssh连接和运行命令Net::SSH.start(@servers['local'],@machine_name,:password=>@machine_pwd,:timeout=>30)do|ssh|ssh.open_channeldo|channel|channel.requ

  5. ruby-on-rails - Ruby on Rails 3 - 公共(public)实时聊天 - 2

    我想使用Rails3创建一个公共(public)实时聊天应用程序。我在rails2上找到了一些例子。任何人都可以告诉你一个很好的例子/教程来使用rails3开发一个实时聊天应用程序。 最佳答案 当我试图在我的Rails3应用程序中实现一个公共(public)和私有(private)聊天系统时,我遇到了几个障碍。我查看了faye、juggernaut、node.js等。最终在尝试了几种方法之后,我能够实现一个运行良好的系统:1)我开始关注Railscast260中的faye消息传递视频指南。正如DevinM所提到的,我能够快速设置一个

  6. Android Studio开发之使用内容组件Content获取通讯信息讲解及实战(附源码 包括添加手机联系人和发短信) - 2

    运行有问题或需要源码请点赞关注收藏后评论区留言一、利用ContentResolver读写联系人在实际开发中,普通App很少会开放数据接口给其他应用访问。内容组件能够派上用场的情况往往是App想要访问系统应用的通讯数据,比如查看联系人,短信,通话记录等等,以及对这些通讯数据及逆行增删改查。首先要给AndroidMaifest.xml中添加响应的权限配置 下面是往手机通讯录添加联系人信息的例子效果如下分成三个步骤先查出联系人的基本信息,然后查询联系人号码,再查询联系人邮箱代码 ContactAddActivity类packagecom.example.chapter07;importandroid

  7. Android 10.0 设置默认launcher后安装另外launcher后默认Launcher失效的功能修复 - 2

    1.前言 在10.0的系统rom定制化开发中,在系统中有多个launcher的时候,会在开机进入launcher的时候弹窗launcher列表,让用户选择进入哪个launcher,这样显得特别的不方便所以产品开发中,要求用RoleManager的相关api来设置默认Launcher,但是在设置完默认Launcher以后,在安装一款Launcher的时候,默认Launcher就会失效,在系统设置的默认应用中Launcher选项就为空,点击home键的时候会弹出默认Launcher列表,让选择进入哪个默认Launcher.所以需要从安装Launcher的流程来分析相关的设置。来解决问题设置默认La

  8. AiBote 2022 新研发的自动化框架,支持 Android 和 Windows 系统。速度非常快 - 2

    Ai-Bot基于流行的Node.js和JavaScript语言的一款新自动化框架,支持Windows和Android自动化。1、Windowsxpath元素定位算法支持支持Windows应用、.NET、WPF、Qt、Java和Electron客户端程序和ie、edgechrome浏览器2、Android支持原生APP和H5界面,元素定位速度是appium十倍,无线远程自动化操作多台安卓设备3、基于opencv图色算法,支持找图和多点找色,1080*2340全分辨率找图50MS以内4、内置免费OCR人工智能技术,无限制获取图片文字和找字功能。5、框架协议开源,除官方node.jsSDK外,用户可

  9. Android Gradle 7.1+新版本依赖变化 - 2

    前一段时间由于工作需要把可爱的小雪狐舍弃了,找到了小蜜蜂。但是新版本的小蜜蜂出现了很多和旧版本不一样的位置。1.功能位置迁移,原来在工程build.gradle的buildscript和allprojects移动至setting.gradle并改名为pluginManagement和dependencyResolutionManagement。里面的东西依旧可以按照原来的copy过来。pluginManagement{repositories{gradlePluginPortal()google()mavenCentral()}}dependencyResolutionManagement{r

  10. ruby - Ruboto 的最佳教程(适用于 Android 的 ruby​​)? - 2

    关闭。这个问题不符合StackOverflowguidelines.它目前不接受答案。要求我们推荐或查找工具、库或最喜欢的场外资源的问题对于StackOverflow来说是偏离主题的,因为它们往往会吸引自以为是的答案和垃圾邮件。相反,describetheproblem以及迄今为止为解决该问题所做的工作。关闭9年前。Improvethisquestion我几乎用完了Ruby,但现在想试试Ruboto,android上的ruby​​。谷歌未能给我足够的(几乎没有结果)。所以任何人都可以分享一些关于Ruboto的教程。

随机推荐