Android 1.5.0 Infinite Loop


#1

I have an application I’m writing with Shinobi Charts 1.5.0 where I am entering data from several sources into a chart with a time axis (inverted so new data enters at the bottom of the chart, and the chart scrolls upwards). There are three line series on the chart in independent axes, and these are autoscaled. I am rendering my own legend.

I have a datasource, that is using a TreeSet as a backing data structure, and this copies its data out into a List (sorted by time), when the chart asks for the points for display. This seemed to be working great when I was inputting real-time data. Suddenly, when the data has become more complex (real-time data, some backfill, and a bunch of historical data), the chart will go into an infinite loop, and take up all of the UI thread, causing an ANR on Android, with something like the following stack trace on the main thread:

"main" prio=5 tid=1 SUSPENDED
  | group="main" sCount=1 dsCount=0 obj=0x4159dca8 self=0x414d7408
  | sysTid=14363 nice=0 sched=0/0 cgrp=apps handle=1074205012
  | state=S schedstat=( 74324127086 8538600201 135969 ) utm=7216 stm=216 core=1
  at java.lang.Double.doubleValue(Double.java:~191)
  at com.shinobicontrols.charts.NumberAxis.E(SourceFile:224)
  at com.shinobicontrols.charts.NumberAxis.i(SourceFile:198)
  at com.shinobicontrols.charts.Axis.s(SourceFile:1435)
  at com.shinobicontrols.charts.NumberAxis.c(SourceFile:300)
  at com.shinobicontrols.charts.Axis.a(SourceFile:1423)
  at com.shinobicontrols.charts.Axis.d(SourceFile:1415)
  at com.shinobicontrols.charts.w.a(SourceFile:249)
  at com.shinobicontrols.charts.w.onMeasure(SourceFile:239)
  at android.view.View.measure(View.java:16497)
  at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5125)
  at android.widget.FrameLayout.onMeasure(FrameLayout.java:310)
  at android.view.View.measure(View.java:16497)
  at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5125)
  at android.widget.FrameLayout.onMeasure(FrameLayout.java:310)
  at android.view.View.measure(View.java:16497)
  at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:5125)
  at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1404)
  at android.widget.LinearLayout.measureHorizontal(LinearLayout.java:1052)
  at android.widget.LinearLayout.onMeasure(LinearLayout.java:590)
  at android.view.View.measure(View.java:16497)

This tends to happen when the chart has panned below the range of the data. The chart code begins consuming resources at an amazing rate, and the phone starts to hit the garbage collector very hard, and an ANR occurs. I have tried turning off animation, turning on and off gridlines and labels. Nothing seems to have an effect.

My chart setup code looks like the following:

private void setupChart(ShinobiChart chart) {
        mTimeAxis = new SpecialDateTimeAxis();
        mTimeAxis.setExtrasMap(mDrillingDepthMap);

        //mTimeAxis.setDefaultRange(new DateRange(new Date(-1 * (new Date().getTime() - 60 * 60 * 1000)), new Date(new Date().getTime() * -1)));
        //mTimeAxis.requestCurrentDisplayedRange(new Date(-1 * (new Date().getTime() - 60 * 60 * 1000)), new Date(new Date().getTime() * -1));

        NumberAxis xAxis = new NumberAxis();
        final AxisStyle xAxisStyle = xAxis.getStyle();
        xAxisStyle.getTickStyle().setLabelsShown(true);
        xAxisStyle.getTickStyle().setMinorTicksShown(false);
        xAxisStyle.getTickStyle().setMajorTicksShown(true);
        xAxis.setCurrentDisplayedRangePreservedOnUpdate(false);
        chart.addXAxis(xAxis);

        NumberAxis xAxis2 = new NumberAxis();
        final AxisStyle xAxisStyle2 = xAxis2.getStyle();
        xAxisStyle2.getTickStyle().setLabelsShown(true);
        xAxisStyle2.getTickStyle().setMinorTicksShown(false);
        xAxisStyle2.getTickStyle().setMajorTicksShown(true);
        xAxis2.setCurrentDisplayedRangePreservedOnUpdate(false);
        chart.addXAxis(xAxis2);

        NumberAxis xAxis3 = new NumberAxis();
        final AxisStyle xAxisStyle3 = xAxis3.getStyle();
        xAxisStyle3.getTickStyle().setLabelsShown(true);
        xAxisStyle3.getTickStyle().setMinorTicksShown(false);
        xAxisStyle3.getTickStyle().setMajorTicksShown(true);
        xAxis3.setCurrentDisplayedRangePreservedOnUpdate(false);
        chart.addXAxis(xAxis3);

        // axis for displaying static vertical grid lines

        NumberAxis gridAxis = new NumberAxis();
        final AxisStyle gridAxisStyle = gridAxis.getStyle();
        gridAxisStyle.getTickStyle().setLabelsShown(false);
        gridAxisStyle.getTickStyle().setMinorTicksShown(false);
        gridAxisStyle.getTickStyle().setMajorTicksShown(false);
        gridAxisStyle.getGridlineStyle().setGridlinesShown(true);
        gridAxis.setCurrentDisplayedRangePreservedOnUpdate(false);
        gridAxis.setMajorTickFrequency(25.0);
        chart.addXAxis(gridAxis);

        chart.setYAxis(mTimeAxis);
        mTimeAxis.enableGesturePanning(true);
        mTimeAxis.enableMomentumPanning(true);
        mTimeAxis.enableGestureZooming(true);
        mTimeAxis.enableBouncingAtLimits(true);
        mTimeAxis.allowPanningOutOfDefaultRange(true);
        mTimeAxis.allowPanningOutOfMaxRange(true);
        mTimeAxis.enableAnimation(true);
        mTimeAxis.setExpectedLongestLabel("");
        final AxisStyle style = mTimeAxis.getStyle();
        style.getGridlineStyle().setGridlinesShown(true);
        style.getTickStyle().setLabelsShown(true);
        style.getTickStyle().setTickGap(-50f);
        style.getTickStyle().setMinorTicksShown(false);
        style.getTickStyle().setMajorTicksShown(true);
        style.getTickStyle().setLabelTextSize(10.0f);
        mTimeAxis.setCurrentDisplayedRangePreservedOnUpdate(true);
        mTimeAxis.specifyBarColumnSpacing(new DateFrequency(1, DateFrequency.Denomination.SECONDS));

        xAxis.enableAnimation(false);
        xAxis.enableGesturePanning(false);
        xAxis.enableMomentumPanning(false);

        xAxis2.enableAnimation(false);
        xAxis2.enableGesturePanning(false);
        xAxis2.enableMomentumPanning(false);

        xAxis3.enableAnimation(false);
        xAxis3.enableGesturePanning(false);
        xAxis3.enableMomentumPanning(false);

        NumberAxis[] axes = new NumberAxis[3];
        axes[0] = xAxis;
        axes[1] = xAxis2;
        axes[2] = xAxis3;

        LegendHolder[] legendTraces = new LegendHolder[3];
        legendTraces[0] = new LegendHolder(mRootView.findViewById(R.id.legendTrace1));
        legendTraces[1] = new LegendHolder(mRootView.findViewById(R.id.legendTrace2));
        legendTraces[2] = new LegendHolder(mRootView.findViewById(R.id.legendTrace3));

        int currentAxis = 0;
        for (String varPath : mAdapterMap.keySet()) {
            final DataAdapter<Double, Date> adapter = mAdapterMap.get(varPath);
            LineSeries series = new LineSeries();
            series.setDataAdapter(adapter);
            final NumberAxis numberAxis = axes[currentAxis];
            mAxisMap.put(varPath, numberAxis);
            numberAxis.getStyle().setInterSeriesPadding(20);
            final ConfiguredTrace configuredTrace = mTraceMap.get(varPath);
            series.getStyle().setLineColor(configuredTrace.getColorForColorName());
            series.getStyle().setLineColorBelowBaseline(configuredTrace.getColorForColorName());
            series.getStyle().setFillStyle(SeriesStyle.FillStyle.NONE);
            series.getStyle().setLineWidth(2.0f);
            chart.addSeries(series, numberAxis, mTimeAxis);

            final LegendHolder legendTrace = legendTraces[currentAxis];
            legendTrace.mColorLine.setBackgroundColor(configuredTrace.getColorForColorName());
            legendTrace.mTitle.setText("");
            legendTrace.mUnits.setText("");
            legendTrace.mMin.setText(configuredTrace.getMinimum());
            legendTrace.mMax.setText(configuredTrace.getMaximum());
            legendTrace.mCurrent.setText("");

            mLegendMap.put(varPath, legendTrace);

            currentAxis++;
        }

        // grid series
        final DataAdapter<Double, Date> gridAdapter = new SimpleDataAdapter<>();
        long time = new Date().getTime();
        gridAdapter.add(new DataPoint<>(0.0, new Date(-time)));
        gridAdapter.add(new DataPoint<>(100.0, new Date(-time)));
        LineSeries gridSeries = new LineSeries();
        gridSeries.getStyle().setLineShown(false);
        gridSeries.setDataAdapter(gridAdapter);
        chart.addSeries(gridSeries, gridAxis, mTimeAxis);

    }

I realize that I haven’t given you much to go on in order to find such a loop. Plus you’ve handily obfuscated your code so the stack traces will need decoding to be of any use, I suspect. Is there any internal logging or something I can turn on to see what is happening here?


#2

Hi Thorinside.

Thanks for getting in touch. We will certainly look into this for you.

Out of interest could you please tell us the date range of your data? How far back in time does it go?

We look forward to your response.

Thanks,

Kai.


#3

Thanks Kai,

In this application, we are trying to have data appear at the bottom of the chart. Effectively this means the Y axis is flipped. The only way I could find to do this was to create all of my Date() objects with negative time values. Dates come in, they are converted to long then they are inverted and a new Date object is created with that inverted date before putting it into the chart. It’d be great if Shinobi charts supported an axis flip (not just flipping the side of the chart the labels fall on). But in the meantime, we have been using this trick (suggested in another forum post).

The range of dates is not large as far as I can tell. All within the last couple of hours.

I will spend some time, perhaps this afternoon or this evening, and try to make a cut-down version of my app that demonstrates the issue, as we have still not been able to solve it with some sort of workaround.

Thanks,

Neal 


#4

Hello Neal,

Thanks for the extra information. I actually stumbled accross a previous forum post which covers a very similar situation to what you currently face. At this point I’d like to first recommend you take a look at it to see if it helps you solve your problem:

Needless to say Neal if you still need further help with this matter please do not hesitate to get back in touch.

Thanks and kind regards,

Kai.


#5

I am still having this issue, periodically the chart will go into an infinite loop and the application will have an ANR. I currently have no solution for this.


#6

Hello Neal,

Did you manage to get a cut down version of the app which demonstrates the issue, which we could take a look at?

Thanks,

Kai 


#7

Sorry, Kai, no. This application is quite complex and we’re under time constraints.

I have since stopped getting this particular issue. I am now calculating the axis ranges manually, and also calculating the major ticks manually too. I think the combination of those two things has stopped the infinite loops from ocurring.

I wish I could get you a reproduction case.

Neal


#8

I’ve had this happen again just now. I’m going to take this conversation offline, and see if we can come to a resolution through email support.

Neal