I'm using GraphView in Android to plot realtime data (ECG), received from Bluetooth (BLE) connected sensor.
After a while (5-10 sec = 500-1000 points) the application becomes extremely slow and laggy.
The output data rate of the sensor is 100Hz and the data are received and plotted as soon as they are generated (every 10ms).
As indicated in similar occasions, I keep only the data I need to display. I tried minimizing the view (2 sec = 200 points), testing the app in different Android devices, but the problem insists. Any ideas?
import com.jjoe64.graphview.GraphView;
import com.jjoe64.graphview.series.DataPoint;
import com.jjoe64.graphview.series.LineGraphSeries;
import com.jjoe64.graphview.GridLabelRenderer;
...
//GraphView init
static LinearLayout GraphView1;
static GraphView graphView1;
static LineGraphSeries<DataPoint> Series1;
static LinearLayout GraphView2;
static GraphView graphView2;
static LineGraphSeries<DataPoint> Series2;
static LinearLayout GraphView3;
static GraphView graphView3;
static LineGraphSeries<DataPoint> Series3;
private static int Xview = 5;
private static int maxpoints = 500;
private static double xstep = 0.01d;
private static double graph2LastXValue = 0;
static boolean AutoScrollX = true;
Initialize Graphview for channel 1 (init_ch2() and init_ch3() similarly):
void init_ch1(){
GraphView1 = (LinearLayout) findViewById(R.id.Channel1);
Series1 = new LineGraphSeries<DataPoint>(new DataPoint[] {new DataPoint(0, 0)});
graphView1 = new GraphView(this);
graphView1.getViewport().setYAxisBoundsManual(Yview_manual);
graphView1.getViewport().setMinX(0);
graphView1.getViewport().setMaxX(Xview);
graphView1.getViewport().setScrollable(true);
graphView1.getViewport().setScalable(true);
graphView1.getGridLabelRenderer().setGridStyle(GridLabelRenderer.GridStyle.BOTH);
graphView1.getGridLabelRenderer().setGridColor(Color.DKGRAY);
graphView1.getGridLabelRenderer().setHorizontalLabelsVisible(false);
graphView1.getGridLabelRenderer().setVerticalLabelsVisible(true);
graphView1.getGridLabelRenderer().setNumHorizontalLabels(6);
graphView1.getGridLabelRenderer().setNumVerticalLabels(3);
graphView1.getGridLabelRenderer().setVerticalLabelsColor(Color.WHITE);
graphView1.getGridLabelRenderer().reloadStyles();
Series1.setThickness(6);
Series1.setColor(Color.GREEN);
graphView1.addSeries(Series1); // data
GraphView1.addView(graphView1);
}
Callback called when new data are received:
public void updateECGCals(BluetoothGattCharacteristic characteristic) {
Integer data1, data2, data3;
data1 = ReceiveChannelData(characteristic, i*CH_DATA_SIZE);
data2 = ReceiveChannelData(characteristic, i*CH_DATA_SIZE+3);
data3 = ReceiveChannelData(characteristic, i*CH_DATA_SIZE+6);
String strIncom1 = Integer.toString(data1); // create string from bytes array
String strIncom2 = Integer.toString(data2); // create string from bytes array
String strIncom3 = Integer.toString(data3); // create string from bytes array
Series1.appendData(new DataPoint(graph2LastXValue,Double.parseDouble(strIncom1)),AutoScrollX,maxpoints);
Series2.appendData(new DataPoint(graph2LastXValue,Double.parseDouble(strIncom2)),AutoScrollX,maxpoints);
Series3.appendData(new DataPoint(graph2LastXValue,Double.parseDouble(strIncom3)),AutoScrollX,maxpoints);
//X-axis control
if (graph2LastXValue >= Xview) {
Series1.resetData(new DataPoint[]{});
Series2.resetData(new DataPoint[]{});
Series3.resetData(new DataPoint[]{});
graph2LastXValue = 0;
} else graph2LastXValue += xstep;
graphView1.getViewport().setMinX(0);
graphView1.getViewport().setMaxX(Xview);
graphView2.getViewport().setMinX(0);
graphView2.getViewport().setMaxX(Xview);
graphView3.getViewport().setMinX(0);
graphView3.getViewport().setMaxX(Xview);
}
private static Integer ReceiveChannelData(BluetoothGattCharacteristic c, int offset) {
Integer output_code;
Integer MSByte = c.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, offset);
Integer MDByte = c.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, offset+1);
Integer LSByte = c.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, offset+2);
output_code = (MSByte << 16) + (MDByte << 8) + LSByte;
return output_code;
}
appendData()
but it's terrible performance with multiple series. I then decided that I'd use a Queue: when new data arrives, pop the data from the head and push the new value to the tail. It's better performance, but still choppy. – Gosling