androidandroid-fragmentsandroid-activityandroid-sensorsandroid-powermanager

Android unable to start sensor service class from fragment


I have a main activity with a nav drawer that brings up different pages as fragments. In one of the fragments there is a button, that when clicked will start a sensor service and store sensor info to a database in the background even when the screen is turn off (wake lock).

However, when the button is clicked the onStartCommand method doesn't seem to be called (its not logging anything to logcat)

I'm not sure why the service isn't starting. I've seen a number of tutorials/questions on this, but they all use activities, but in my case I am trying to start the sensor service class from a fragment within a MainActivity. A lot of my confusion stems from mixing fragments and activities, and how to correctly start services from a fragment

Here is the fragment with the button:

public class StartFragment extends Fragment implements View.OnClickListener {
    Button startButton;
    Boolean started = false;
    CoordinatorLayout coordinatorLayout;
    MainActivity mainActivity;

    public StartFragment() {
        // Required empty public constructor
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {

        View view = inflater.inflate(R.layout.fragment_start, container, false);

        coordinatorLayout = (CoordinatorLayout) getActivity().findViewById(R.id.coordinator_layout);

        //Set the nav drawer item highlight
        mainActivity = (MainActivity)getActivity();
        mainActivity.navigationView.setCheckedItem(R.id.nav_start);

        //Set actionbar title
        mainActivity.setTitle("Start");

        //Set onclick listener for save button
        startButton = (Button) view.findViewById(R.id.startButton);
        startButton.setOnClickListener(this);

        // Inflate the layout for this fragment
        return view;
    }

    @Override
    public void onClick(View v) {
        if (!started){
            mainActivity.startService(new Intent(mainActivity, SensorService.class));
            startButton.setText(getResources().getString(R.string.start_button_label_stop));
            Snackbar.make(coordinatorLayout, "Recording...", Snackbar.LENGTH_SHORT).show();
        } else {
            mainActivity.stopService(new Intent(mainActivity, SensorService.class));
            startButton.setText(getResources().getString(R.string.start_button_label_start));
            Snackbar.make(coordinatorLayout, "Recording stopped.", Snackbar.LENGTH_SHORT).show();
        }

        started = !started;
    }
}

And here is the SensorService class that should run on button click:

public class SensorService extends Service implements SensorEventListener {

    public static final String TAG = SensorService.class.getName();
    public static final int SCREEN_OFF_RECEIVER_DELAY = 500;

    private SensorManager sensorManager = null;
    private WakeLock wakeLock = null;
    private Sensor sensor;
    Sensor accelerometer;
    Sensor gyroscope;
    Sensor gravity;
    Sensor magnetic;

    private void registerListener() {
        sensorManager.registerListener(this, accelerometer, SensorManager.SENSOR_DELAY_FASTEST);
        sensorManager.registerListener(this, gyroscope, SensorManager.SENSOR_DELAY_FASTEST);
        sensorManager.registerListener(this, gravity, SensorManager.SENSOR_DELAY_FASTEST);
        sensorManager.registerListener(this, magnetic, SensorManager.SENSOR_DELAY_FASTEST);
    }

    private void unregisterListener() {
        sensorManager.unregisterListener(this);
    }

    public BroadcastReceiver receiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            Log.i(TAG, "onReceive("+intent+")");

            if (!intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
                return;
            }

            Runnable runnable = new Runnable() {
                public void run() {
                    Log.i(TAG, "Runnable executing.");
                    unregisterListener();
                    registerListener();
                }
            };

            new Handler().postDelayed(runnable, SCREEN_OFF_RECEIVER_DELAY);
        }
    };

    public void onAccuracyChanged(Sensor sensor, int accuracy) {
        //Safe not to implement
    }


    public void onSensorChanged(SensorEvent event) {
        //TODO add async task to insert to DB
    }

    @Override
    public void onCreate() {
        super.onCreate();

        sensorManager = (SensorManager) getSystemService(SENSOR_SERVICE);
        accelerometer = sensorManager.getDefaultSensor(MainActivity.TYPE_ACCELEROMETER);
        gyroscope = sensorManager.getDefaultSensor(MainActivity.TYPE_GYROSCOPE);
        gravity = sensorManager.getDefaultSensor(MainActivity.TYPE_GRAVITY);
        magnetic = sensorManager.getDefaultSensor(MainActivity.TYPE_MAGNETIC);

        PowerManager manager =
                (PowerManager) getSystemService(Context.POWER_SERVICE);
        wakeLock = manager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);

        registerReceiver(receiver, new IntentFilter(Intent.ACTION_SCREEN_OFF));
    }

    @Override
    public void onDestroy() {
        unregisterReceiver(receiver);
        unregisterListener();
        wakeLock.release();
        stopForeground(true);
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        super.onStartCommand(intent, flags, startId);

        Log.d(TAG, "onStartCommand");

        startForeground(Process.myPid(), new Notification());
        registerListener();
        wakeLock.acquire();

        return START_STICKY;
    }
}

Solution

  • Try adding this in your XML Manifest inside the application tag, outside anything else:

    <service android:name=".SensorService" />
    

    Let me know if this helped.