Here's what I am trying to do. I have an activity that needs to process some accelerometer data. So I created a service that extends "Service" and implements "SensorEventListener"
Here's the code for my activity:
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.graphics.Color;
import android.os.IBinder;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import com.jjoe64.graphview.GraphView;
import com.jjoe64.graphview.series.DataPoint;
import com.jjoe64.graphview.series.LineGraphSeries;
public class GraphActivity1 extends ActionBarActivity {
private GraphView graph1;
private GraphView graph2;
private DataPoint[] xdata = new DataPoint[64];
private DataPoint[] ydata = new DataPoint[64];
private DataPoint[] zdata = new DataPoint[64];
private DataPoint[] fxdata = new DataPoint[64];
private DataPoint[] fydata = new DataPoint[64];
private DataPoint[] fzdata = new DataPoint[64];
private Complex[] xcdata = new Complex[64];
private Complex[] ycdata = new Complex[64];
private Complex[] zcdata = new Complex[64];
private Complex[] xfdata = new Complex[64];
private Complex[] yfdata = new Complex[64];
private Complex[] zfdata = new Complex[64];
private final int FFT_SIZE = 64;
private boolean RUN_FLAG = true;
private char run_times = 10;
BoundService myService;
boolean isBound = false;
private ServiceConnection myConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className,
IBinder service) {
BoundService.MyLocalBinder binder = (BoundService.MyLocalBinder) service;
myService = binder.getService();
isBound = true;
}
public void onServiceDisconnected(ComponentName arg0) {
isBound = false;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_graph_activity1);
Log.i("New Tag", "Graph Activity Created");
graph1 = (GraphView)findViewById(R.id.timegraph);
graph2 = (GraphView)findViewById(R.id.frequencygraph);
Intent intent = new Intent(this, BoundService.class);
startService(intent);
Log.i("New Tag", "Service Started");
bindService(intent, myConnection, Context.BIND_AUTO_CREATE);
while (!isBound){
Log.i("New Tag", "Service Binding in Progress");
}
Log.i("New Tag", "Service Bound");
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_graph_activity1, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public void onStart(){
Log.i("New Tag", "Graph Activity Started");
while(RUN_FLAG){
Log.i("New Tag", "Entered While Loop");
int i_state = myService.getStatus();
//Log.i("New Tag", "State is"+i_state);
if (i_state == 1){
Log.i("New Tag", "Entered Second While Loop");
Complex[][] alldata = myService.getData();
xcdata = alldata[0];
ycdata = alldata[1];
zcdata = alldata[2];
for (int i = 0; i < FFT_SIZE; i++){
xdata[i] = new DataPoint(i, xcdata[i].re());
ydata[i] = new DataPoint(i, ycdata[i].re());
zdata[i] = new DataPoint(i, zcdata[i].re());
}
xfdata = FFT.fft(xcdata);
yfdata = FFT.fft(ycdata);
zfdata = FFT.fft(zcdata);
for (int i = 0; i < FFT_SIZE; i++){
fxdata[i] = new DataPoint(i, xfdata[i].abs());
fydata[i] = new DataPoint(i, yfdata[i].abs());
fzdata[i] = new DataPoint(i, zfdata[i].abs());
}
LineGraphSeries<DataPoint> series1t = new LineGraphSeries<DataPoint>(xdata);
LineGraphSeries<DataPoint> series2t = new LineGraphSeries<DataPoint>(ydata);
LineGraphSeries<DataPoint> series3t = new LineGraphSeries<DataPoint>(zdata);
LineGraphSeries<DataPoint> series1f = new LineGraphSeries<DataPoint>(fxdata);
LineGraphSeries<DataPoint> series2f = new LineGraphSeries<DataPoint>(fydata);
LineGraphSeries<DataPoint> series3f = new LineGraphSeries<DataPoint>(fzdata);
series1t.setColor(Color.RED);
series2t.setColor(Color.GREEN);
series3t.setColor(Color.BLUE);
graph1.addSeries(series1t);
graph1.addSeries(series2t);
graph1.addSeries(series3t);
graph1.setTitle("Time Domain Data [x(red), y(green), z(blue)]");
series1f.setColor(Color.RED);
series2f.setColor(Color.GREEN);
series3f.setColor(Color.BLUE);
graph2.addSeries(series1f);
graph2.addSeries(series2f);
graph2.addSeries(series3f);
graph2.setTitle("Frequency Domain Data [x(red), y(green), z(blue)]");
graph1.removeAllSeries();
graph2.removeAllSeries();
}
run_times --;
if (run_times == 0)
{
RUN_FLAG = false;
}
}
}
}
and here's the code for my Service:
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import android.widget.Toast;
import com.jjoe64.graphview.series.DataPoint;
public class BoundService extends Service implements SensorEventListener {
private SensorManager senSensorManager;
private Sensor senAccelerometer;
private long lastUpdate = 0;
private int data_index = 0;
private static final int FFT_SIZE = 64;
private Complex[] xdata;
private Complex[] ydata;
private Complex[] zdata;
private Complex[][] alldata;
private final IBinder myBinder = new MyLocalBinder();
private int data_ready = 0;
@Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
return myBinder;
}
public int onStartCommand (Intent intent, int flags, int startId){
Log.i("New Tag","Service Called");
senSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
senAccelerometer = senSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
senSensorManager.registerListener(this, senAccelerometer , SensorManager.SENSOR_DELAY_NORMAL);
return START_STICKY;
}
public Complex[][] getData() {
if (data_ready == 0){
Log.i("New Tag","getStatus Called, Data not ready");
return null;
}else {
Log.i("New Tag","getStatus Called, Data is ready");
alldata[0] = xdata;
alldata[1] = ydata;
alldata[2] = zdata;
data_ready = 0;
data_index = 0;
return alldata;
}
}
public class MyLocalBinder extends Binder {
BoundService getService() {
return BoundService.this;
}
}
public int getStatus(){
Log.i("New Tag","getStatus Called");
return data_ready;
}
public void onSensorChanged(SensorEvent sensorEvent) {
long nowTime = System.currentTimeMillis();
if ((nowTime - lastUpdate) > 100){
lastUpdate = nowTime;
Sensor mySensor = sensorEvent.sensor;
if (mySensor.getType() == Sensor.TYPE_ACCELEROMETER) {
float x = sensorEvent.values[0];
float y = sensorEvent.values[1];
float z = sensorEvent.values[2];
if (data_index < FFT_SIZE) {
xdata[data_index] = new Complex(x);
ydata[data_index] = new Complex(y);
zdata[data_index] = new Complex(z);
data_index++;
Log.i("New Tag","Data not Ready");
}
else if (data_index == FFT_SIZE)
{
data_ready = 1;
Log.i("New Tag","Data is Ready");
Toast.makeText(getApplicationContext(), "Data Reading Complete", Toast.LENGTH_SHORT).show();
}
}
}
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
}
When running this code, the program crashes. When I followed the tags, they stop after displaying:
06-28 15:16:14.632 28418-28441/adameve.relsa D/OpenGLRenderer﹕ endAllStagingAnimators on 0xb49b8b00 (RippleDrawable) with handle 0xaec2cfe0
06-28 15:16:20.921 28418-28418/adameve.relsa I/New Tag﹕ Graph Activity Created
06-28 15:16:20.929 28418-28418/adameve.relsa I/New Tag﹕ Service Bound
06-28 15:16:20.929 28418-28418/adameve.relsa I/New Tag﹕ Graph Activity Started
06-28 15:16:20.929 28418-28418/adameve.relsa I/New Tag﹕ Entered While Loop
06-28 15:16:20.930 28418-28418/adameve.relsa D/AndroidRuntime﹕ Shutting down VM
06-28 15:16:20.932 28418-28418/adameve.relsa E/AndroidRuntime﹕ FATAL EXCEPTION: main
Process: adameve.relsa, PID: 28418
java.lang.RuntimeException: Unable to start activity ComponentInfo{adameve.relsa/adameve.relsa.GraphActivity1}: java.lang.NullPointerException: Attempt to invoke virtual method 'int adameve.relsa.BoundService.getStatus()' on a null object reference
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2325)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2387)
at android.app.ActivityThread.access$800(ActivityThread.java:151)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1303)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5254)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'int adameve.relsa.BoundService.getStatus()' on a null object reference
at adameve.relsa.GraphActivity1.onStart(GraphActivity1.java:107)
at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1236)
at android.app.Activity.performStart(Activity.java:6006)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2288)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2387)
at android.app.ActivityThread.access$800(ActivityThread.java:151)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1303)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5254)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)
Thank you so much for your help in advance.
Sincerely
The guide for Bound Services explains that a call to bindService()
returns immediately, but the actual binding occurs asynchronously. The service is not available for use until your activity receives the onServiceConnected()
callback. Your activity calls bindService()
in onCreate()
. It then calls myService.getStatus()
in onStart()
. This is happening before the binding has completed and onServiceConnected()
has been called, so myService
is null, and you get the exception.
You need to restructure your code so that your graph creation processing is not started before the service binding has completed.
And there is another change needed. You cannot put your graph processing in a endless while-loop in onStart()
. Long running processing cannot be performed on the main thread. It will interfere with other UI processing and will result in an "Application Not Responding" failure after a few seconds.