You need to return START_STCKY
in your onStartCommand()
method for the service to run even when app is closed.
....
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STCKY;
}
....
You can refer thislink for correctly implementing a service.
Alternatively, you can refer this SO question.
Update
Use a Foreground Service
to avoid your Service being killed. In order to make your service Foreground, replace your onStartCommand
code with this
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Intent notificationIntent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
notificationIntent, 0);
Notification notification = new NotificationCompat.Builder(this)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("Timer")
.setContentText("Doing some work...")
.setContentIntent(pendingIntent).build();
startForeground(1337, notification);
cdt = new CountDownTimer(30000, 1000) {
public void onTick(long millisUntilFinished) {
bi.putExtra("countdown", millisUntilFinished);
sendBroadcast(bi);
}
public void onFinish() {
stopForeground(true);
}
};
cdt.start();
return START_STICKY;
}
Udpate 2: Counter using Service
and SharedPreferences
Replace your Actvity's code with this:
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Handler;
import android.support.v4.os.ResultReceiver;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;
import java.util.Calendar;
import java.util.Date;
public class MainActivity extends AppCompatActivity {
private static final String TAG = MainActivity.class.getSimpleName();
private static final String SHARED_PREF = "MyPref";
private final static int MAX_COUNTER = 30;
public static final String KEY_COUNTER_SECONDS = "seconds";
public static final String KEY_SAVED_COUNTER = "saved_counter";
public static final String KEY_SAVED_TIME_MILLI = "saved_time_milli";
MyResultReceiver mReceiver;
TextView mTvCounter;
SharedPreferences mSharedPref;
long mMaxCounterValueInSeconds = MAX_COUNTER;
long mCurCounterValue = 0;
boolean mShouldSaveValues;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTvCounter = (TextView) findViewById(R.id.tv_counter);
mReceiver = new MyResultReceiver(null);
mSharedPref = getSharedPreferences(SHARED_PREF, Context.MODE_PRIVATE);
}
@Override
protected void onResume() {
super.onResume();
//register listener
MyService.registerReceiver(mReceiver);
//get values from shared pref
long savedCounter = mSharedPref.getLong(KEY_SAVED_COUNTER, -1);
long savedTime = mSharedPref.getLong(KEY_SAVED_TIME_MILLI, -1);
//if -1 counter was running when app was closed, get saved values from shared pref
if (savedTime != -1) {
//elapsedTime is the time spent in seconds while the app was in background
long elapsedTime = (getCurrentTimeInMilli() - savedTime)/1000; //convert to sec
mCurCounterValue = savedCounter + elapsedTime;
if(mCurCounterValue < MAX_COUNTER){
//calculate current counter value from values retrieved from shared pref
mMaxCounterValueInSeconds = MAX_COUNTER - mCurCounterValue;
//start the value with updated max count value
startService(mMaxCounterValueInSeconds);
}else{
mCurCounterValue = MAX_COUNTER;
}
}else{
//if counter was not running, start the service with max count value = MAX_COUNTER
startService(mMaxCounterValueInSeconds);
}
//update text view
mTvCounter.setText("" + mCurCounterValue);
}
private void startService(long maxCounter){
mShouldSaveValues = true;
Intent intent = new Intent(this, MyService.class);
Bundle bundle = new Bundle();
bundle.putLong(KEY_COUNTER_SECONDS, maxCounter);
intent.putExtras(bundle);
startService(intent);
}
@Override
protected void onPause() {
super.onPause();
//stop the service
stopService(new Intent(this, MyService.class));
//unregister listener
MyService.unregisterReceiver();
if(mShouldSaveValues) {//save the values only when counter has started
//save values in the shared preference
SharedPreferences.Editor editor = mSharedPref.edit();
Log.d(TAG, "saving counter: " + Long.parseLong(mTvCounter.getText().toString()));
editor.putLong(KEY_SAVED_COUNTER, Long.parseLong(mTvCounter.getText().toString()));
editor.putLong(KEY_SAVED_TIME_MILLI, getCurrentTimeInMilli());
editor.apply();
}
}
/**
* This method returns current time in milli seconds
*
* @return time in milliseconds
*/
private long getCurrentTimeInMilli() {
Calendar cal = Calendar.getInstance();
Date date = cal.getTime();
long timeInMilli = date.getTime();
return timeInMilli;
}
/**
* ResultReceiver is used to get values from MyService.class
* It is registered in onResume() &
* unregistered in onPause()
*/
class MyResultReceiver extends ResultReceiver {
public MyResultReceiver(Handler handler) {
super(handler);
}
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
super.onReceiveResult(resultCode, resultData);
String strMilliFinished = resultData.getString(MyService.KEY_MSG);
updateUI(Long.parseLong(strMilliFinished));
}
private void updateUI(final long milliFinished) {
runOnUiThread(new Runnable() {
@Override
public void run() {
mCurCounterValue++;
mTvCounter.setText("" + mCurCounterValue);
if(milliFinished == 0) {
//resetting counter values
mShouldSaveValues = false;
mMaxCounterValueInSeconds = MAX_COUNTER;
mCurCounterValue = 0;
SharedPreferences.Editor editor = mSharedPref.edit();
editor.putLong(KEY_SAVED_COUNTER, -1);
editor.putLong(KEY_SAVED_TIME_MILLI, -1);
editor.apply();
}
}
});
}
}
}
Replace your Service code with this:
import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.os.IBinder;
import android.support.v4.app.NotificationCompat;
import android.support.v4.os.ResultReceiver;
import android.util.Log;
public class MyService extends Service {
public static final String KEY_MSG = "msg";
CountDownTimer cdt = null;
private static ResultReceiver mReceiver;
public MyService() {
}
public static void registerReceiver(ResultReceiver receiver) {
mReceiver = receiver;
}
public static void unregisterReceiver() {
mReceiver = null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Bundle bundle = intent.getExtras();
long maxCounterValueInSeconds = bundle.getLong(MainActivity.KEY_COUNTER_SECONDS);
long maxCounter = maxCounterValueInSeconds * 1000;
cdt = new CountDownTimer(maxCounter, 1000) {
public void onTick(long millisUntilFinished) {
sendMessage(1, "" + millisUntilFinished);
}
public void onFinish() {
sendMessage(1, "" + 0);
stopSelf();
}
};
cdt.start();
return START_STICKY;
}
private void sendMessage(int resultCode, String message) {
if (mReceiver != null) {
Bundle bundle = new Bundle();
bundle.putString(KEY_MSG, message);
mReceiver.send(resultCode, bundle);
}
}
@Override
public void onDestroy() {
super.onDestroy();
cdt.cancel();
}
@Override
public IBinder onBind(Intent intent) {
throw new UnsupportedOperationException("Not yet implemented");
}
}
Note: I am using ResultReceiver
instead of BroadcastReciver