grey00
grey00

Reputation: 479

My service is behaving strangely when the phone is off

The purpose of the service is to keep track of time since a button was pressed. If the menuactivity is showing, it updates some values on the menuactivity every minute using a timer, otherwise, it just updates itself.

It seems to work fine when the application is open or closed, but when the phone is off it slows down to less than half of what it should be (only showing like 10 minutes have passed after 21 real minutes have passed).

int startTime; //time at which the button is pressed
int time; //the current time, relative to startTime 

@Override
public int onStartCommand(Intent intent, int flags, int startId){
    startTime = (int)(System.nanoTime()/1000000000.0);
    UpdateTimeTask updateTimeTask = new UpdateTimeTask();
    timer = new Timer();
    timer.schedule(updateTimeTask, 0, UPDATE_PERIOD); //update period is 60,000 (60 seconds)

    return START_STICKY;
}

public class UpdateTimeTask extends TimerTask {
    public void run() {
        updateMenu();
    }
}

public void updateMenu(){
    time = (int) Math.round((System.nanoTime() / 1000000000.0) - startTime);

    if(serviceCallbacks != null){ //it wont be null if its on menuactivity
        serviceCallbacks.updateTimeElapsed(time/3600, time/60 - (time/3600) * 60);
    }
}

If I have my phone off for a while and then go back in to menuactivity, it doesn't "fix itself" after a few cycles. I thought onStartCommand might be called more than once, but the only time the service can possibly be started is when the button is pressed.

Upvotes: 0

Views: 53

Answers (2)

grey00
grey00

Reputation: 479

The accepted answer is correct, but only solved part of the problem. The other problem was that I was using System.nanoTime() instead of .currentTimeMillis(); .nanoTime() stops when the screen is off. Putting this here for possible future googlers.

Upvotes: 2

Bob Snyder
Bob Snyder

Reputation: 38289

but the only time the service can possibly be started is when the button is pressed

That is not true. When your app go into the background (is no longer visible) it becomes a candidate to be killed if the system needs memory for other higher ranked apps. Also, the user can kill your app by swiping it from the task list. Because the service returns START_STICKY from onStartCommand(), the system will restart the service after some period of time, calling onStartCommand() with a null intent. This behavior makes a service unsuitable as the home for a data item you want to retain, such as startTime.

An alternative is to persist the value in SharedPreferences. A service is not needed and the periodic update processing can be done in your activity using the postDelayed() method of any view.

This sample activity outlines the basic logic:

public class ButtonActivity extends Activity {
    private static final String TIME_KEY = "time";
    private static final long PERIOD = 60*1000; // 60 seconds

    private SharedPreferences mPrefs;
    private Button mButton;
    private TextView mTimeView;

    private Runnable mDisplayTask = new Runnable() {
        @Override
        public void run() {
            // show the time elapsed since button press in milliseconds
            long elapsed = System.currentTimeMillis() - mPrefs.getLong(TIME_KEY, 0);
            mTimeView.setText(""+elapsed);
            // schedule next display update
            mTimeView.postDelayed(mDisplayTask, PERIOD);
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        mPrefs = getPreferences(MODE_PRIVATE);
        // clear the button press time
        setPressTime(0);

        setContentView(R.layout.activity_demo);

        mButton = (Button)findViewById(R.id.button);
        mTimeView = (TextView)findViewById(R.id.time);

        mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // save the button press time
                setPressTime(System.currentTimeMillis());
                // start the display updates
                mDisplayTask.run();
            }
        });
    }

    @Override
    protected void onPause() {
        super.onPause();
        // activity no longer visible; stop the updates
        mTimeView.removeCallbacks(mDisplayTask);
    }

    @Override
    protected void onResume() {
        super.onResume();
        // activity is visible
        // if the button has been pressed, start the display updates
        if (mPrefs.getLong(TIME_KEY, 0) > 0) {
            mDisplayTask.run();
        }
    }

    private void setPressTime(long time) {
        // persist the button press time
        SharedPreferences.Editor ed = mPrefs.edit();
        ed.putLong(TIME_KEY, time);
        ed.commit();
    }
}

Upvotes: 2

Related Questions