I'm having a problem with a college project in which I need to display real-time readings from an arduino on an android device. The arduino and Bluetooth link are working fine. On the android side, I start a new thread to retrieve these values and add them to an arraylist FullSignal.
private synchronized void theloop(){
AsyncTask.execute(new Runnable() {
@Override
public void run() {
try
{
while(btSocket.isConnected() ){
if(!pause) {
BufferedReader r = new BufferedReader(new InputStreamReader(btSocket.getInputStream()));
String total = "";
String line;
if ((line = r.readLine()) != null) {
total = (line);
}
if (!total.equals("")) {
double xtoadd = ((new Date().getTime()) - startOfRun) / 1000;
DataPoint dp = new DataPoint(xtoadd, Double.parseDouble(total));
FullSignal.add(dp);
sleep(10);
}
}
else{
sleep(10);
This thread is doing what I want and puts the readings into the arraylist, (the time they arrive, and the value). Then I have another thread which calls an Update_Graph method every 50ms, and passes in the current FullSignal arraylist.
private synchronized void Update_Graph_loop() {
Thread thread = new Thread()
{
@Override
public void run() {
try {
while(!ending) {
if(!pause){
Update_Graph(FullSignal);
sleep(50);
}
else{
sleep(50);
}
}
} catch (InterruptedException e) {
Toast.makeText(getBaseContext(), e.toString(), Toast.LENGTH_LONG).show();
}
}
};
thread.start();
}
The Update_Graph it calls, does some maths to get the the graph displaying the values which were read from the arduino and when they arrived.
private synchronized void Update_Graph(ArrayList<DataPoint> FS)
{
graph = (GraphView) findViewById(R.id.graph);
series = new LineGraphSeries<DataPoint>();
for (int i = dpToDisp.length - 1; i >= 0; i--) {
DataPoint dp = FS.get(FS.size() - (dpToDisp.length - i));
dpToDisp[i] = new DataPoint(((dp.getX() * 1000 + startOfRun) - (new Date().getTime())) / 1000, dp.getY());
}
series.resetData(dpToDisp);
series.setThickness(20);
series.setColor(Color.RED);
graph.removeAllSeries();
graph.addSeries(series);
}
The app behaves exactly as I want it to for a while (Usually between 5 - 25 secs) and then crashes. Screenshot of app running before crash
The console shows a Null Object Reference error but doesn't show the line of my code causing it.
E/AndroidRuntime: FATAL EXCEPTION: main
Process: app.stephen.com.samsungs2test, PID: 28668
java.lang.NullPointerException: Attempt to invoke virtual method 'int java.lang.Integer.intValue()' on a null object reference
at com.jjoe64.graphview.GridLabelRenderer.drawVerticalSteps(GridLabelRenderer.java:1302)
at com.jjoe64.graphview.GridLabelRenderer.draw(GridLabelRenderer.java:1071)
at com.jjoe64.graphview.GraphView.drawGraphElements(GraphView.java:299)
at com.jjoe64.graphview.GridLabelRenderer.draw(GridLabelRenderer.java:1062)
at com.jjoe64.graphview.GraphView.drawGraphElements(GraphView.java:299)
at com.jjoe64.graphview.GraphView.onDraw(GraphView.java:323)
at android.view.View.draw(View.java:16068)
The way it crashes does not seem to be dependent on time or a memory problem, which makes me think that it is a multi-threading issue.
I have made all my methods private and synchronized. Is there any other steps I can make to make my program thread safe? Or can anyone see where the problem might be arising?
I don't have a solution but here's a few random thoughts:
synchronized
method, at least from how you're using it for Update_Graph_loop
GraphView.onDraw
method, that method is run on the UI thread, adding synchronized keyword to a method won't prevent GraphView.onDraw
to access an object that you're using inside the method you've synchronizedLineGraphSeries
, maybe that's the source of the problem2 possible causes:
a) GraphView
has a reference to an object you passed to it and you are modifying that object while the View
is drawing
b) This is not a thread synchronization issue, you've just passed some corrupted data to GraphView
, somehow
if the problem is a), could try this:
instead of calling methods on GraphView
from other threads, post a runnable on it so it will run on the UI thread
graph.post(new Runnable(){
@Override
public void run(){
graph.removeAllSeries();
graph.addSeries(series);
}
});