Edit: Thanks for the replies. I ended up figuring out a nice solution (which I posted below) that uses a foreground service and broadcast receivers for those interested.
Original Question:
I have a simple count up timer using a handler which updates a textview. What Im trying to achieve is to
Continue the timer even if the app closes
Put out a notification and wake up the phone(if asleep) when the timer reaches its duration
I've read about using a service as it operates separate from the activity however all the examples I found seem more complicated for what I'm trying to do.
For reference heres my timer class
public class MyTimer implements Runnable {
MainActivity activity;
Handler handler;
TextView timerView;
long current_time,duration;
public MyTimer(MainActivity activity){
this.activity = activity;
this.handler = new Handler();
this.current_time = 0L;
timerView = (TextView) activity.findViewById(R.id.timerValue);
}
public MyTimer startTimer(int duration){
this.duration = duration;
handler.postDelayed(this,1000);
return this;
}
public MyTimer resetTimer(){
timerView.setText("0:00");
handler.removeCallbacks(this);
return this;
}
#Override
public void run() {
if(current_time == duration){
Toast.makeText(activity,"Timer is done",Toast.LENGTH_SHORT).show();
resetTimer();
return;
}
current_time += 1000;
int secs = (int) (current_time / 1000);
int minutes = secs / 60;
timerView.setText(Integer.toString(minutes) + ":" + String.format("%02d", secs%60));
handler.postDelayed(this, 1000);
}
}
timerView and two buttons for start/stop
I was also thinking of just storing the timer in a database during onStop/onDestroy and using the system time and its difference between the saved time to just update the timer that way. But that wouldn't solve the issue of issuing a notification and/or waking up the phone.
The examples that you find are not too complicated - in order to achieve what you want you'll need:
Bound Service which will keep track of elapsed time and will register alarm with AlarmManager
Fragment/Activity that can bind the above Service and execute methods like resetTimer(), startTimer(), getElapsedTime(). You need to perform a query to getElapsedTime() using a Handler, but 1 second timeout is too long (I'd use 0.1 second or similar).
Last note: you can't use the timeout that you set on postDelayed() in order to increment the timer. Better use something like this:
public void startTimer(long duration) {
mStartTime = System.currentTimeMillis();
mDuration = duration;
// register alarm with AlarmManager here
}
public long getElapsedTime() {
return System.currentTimeMillis() - mStartTime;
}
For those out there who might need an answer to this, after some research I decided the best approach was to use a foreground service and a handler because an alarm manager would be inefficient for such a short and constant timer.
So in Summary
In Service Class
Broadcasts the timer to main activity in which the MainActivity will receive it using a broadcastreceiver and updates the UI
Service class uses its own broadcast receiver to check if phone screen is on/off and updates the timer when it returns from sleep.
In Main Activity class
Receive the broadcast sent from the timer service and update the UI
Other logistics such as when to register/unregister broadcast receiver and sending actions to the service to either stop/start
Service Class:
//Timer service which uses a handler to monitor tick rate. Also uses a broadcast receiver
//to update the timer if the device was in sleep mode.
public class TimerService extends Service{
Intent intent;
public static final String TAG = TimerService.class.getSimpleName();
private final Handler handler = new Handler();
long currentTime, duration;
long timeSinceLastOn, elapsedTimeSinceOff;
#Override
public void onCreate() {
super.onCreate();
currentTime = duration = elapsedTimeSinceOff = 0L;
timeSinceLastOn = SystemClock.elapsedRealtime();
intent = new Intent(Constants.ACTION.BROADCAST_ACTION);
/**Starting Timer here**/
handler.removeCallbacks(timerThread);
handler.postDelayed(timerThread,0);
/**********************/
/**Broadcast receiver to check if the screen is on **/
IntentFilter screenStateFilter = new IntentFilter();
screenStateFilter.addAction(Intent.ACTION_SCREEN_ON);
screenStateFilter.addAction(Intent.ACTION_SCREEN_OFF);
registerReceiver(broadcastReceiver, screenStateFilter);
/***************************************************/
}
#Override
/**Depending on action issued by MainActivity either puts service in
*foreground with duration or destroys the service**/
public int onStartCommand(Intent intent, int flags, int startId) {
if(intent != null) {
if (intent.getAction().equals(Constants.ACTION.STARTFOREGROUND_ACTION)) {
if (intent.hasExtra(Constants.TIMER.DURATION))
duration = intent.getLongExtra(Constants.TIMER.DURATION, 0);
startForeground(Constants.NOTIFICATION_ID.FOREGROUND_SERVICE, createTimerNotification());
} else if (intent.getAction().equals(Constants.ACTION.STOPFOREGROUND_ACTION)) {
stopForeground(true);
stopSelf();
}
}
return START_STICKY;
}
/**Thread the handler uses to push to message queue. This creates a timer effect.**/
private Runnable timerThread = new Runnable() {
#Override
public void run() {
if(currentTime == duration){
stopSelf();
return;
}
currentTime += 1000;
sendTimerInfo();
handler.postDelayed(this,1000);
}
};
/**Broadcasts the timer in which the MainActivity will receive it and update the UI**/
private void sendTimerInfo(){
Log.d(TAG, "timer running: tick is " + currentTime);
intent.putExtra(Constants.TIMER.CURRENT_TIME, currentTime);
sendBroadcast(intent);
}
#Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG,"timer service finished");
unregisterReceiver(broadcastReceiver);
handler.removeCallbacks(timerThread);
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
/******************** Broadcast Receiver To Check if Screen is on**************************************/
private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
handler.removeCallbacks(timerThread);
/**If the screen is back on then update the timer and start it again**/
if(intent.getAction().equals(Intent.ACTION_SCREEN_ON)){
Log.d(TAG,"Screen is turned on");
elapsedTimeSinceOff = SystemClock.elapsedRealtime() - timeSinceLastOn;
Log.d(TAG," screen was off and updating current time by"+elapsedTimeSinceOff);
currentTime += elapsedTimeSinceOff;
handler.postDelayed(timerThread,0);
}
/**Turns off the timer when the screen is off**/
else if(intent.getAction().equals(Intent.ACTION_SCREEN_OFF)){
Log.d(TAG,"Screen is turned off");
timeSinceLastOn = SystemClock.elapsedRealtime();
}
}
};
/**Since this is foreground service it must have a notification**/
private Notification createTimerNotification() {
Intent notificationIntent = new Intent(this, MainActivity.class);
notificationIntent.setAction(Constants.ACTION.MAIN_ACTION);
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
notificationIntent,0);
Bitmap icon = BitmapFactory.decodeResource(getResources(),
R.mipmap.ic_launcher);
Notification notification = new NotificationCompat.Builder(this)
.setContentTitle("Service Timer")
.setTicker("Count up timer")
.setContentText("timer")
.setSmallIcon(R.mipmap.ic_launcher)
.setLargeIcon(Bitmap.createScaledBitmap(icon, 128, 128, false))
.setContentIntent(pendingIntent)
.setOngoing(true)
.build();
return notification;
}
}
MainActivity:
public class MainActivity extends Activity {
TextView timerView;
Intent timerService;
//Example duration of 3minutes
long currentTime, duration = 180000;
#Override
protected void onStart() {
super.onStart();
timerService = new Intent(this, TimerService.class);
//Register broadcast if service is already running
if(isMyServiceRunning(TimerService.class)){
registerReceiver(broadcastReceiver, new IntentFilter(Constants.ACTION.BROADCAST_ACTION));
}
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button startButton, stopButton;
timerView = (TextView) findViewById(R.id.timerValue);
startButton = (Button) findViewById(R.id.startButton);
stopButton = (Button) findViewById(R.id.stopButton);
//Button to Start the service when pushed
startButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
if(!isMyServiceRunning(TimerService.class)) {
timerService.setAction(Constants.ACTION.STARTFOREGROUND_ACTION);
timerService.putExtra(Constants.TIMER.DURATION,duration);
startService(timerService);
registerReceiver(broadcastReceiver, new IntentFilter(Constants.ACTION.BROADCAST_ACTION));
}
}
});
//Button to stop the service when pushed
stopButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
if(isMyServiceRunning(TimerService.class)) {
timerView.setText("0:00");
timerService.setAction(Constants.ACTION.STOPFOREGROUND_ACTION);
startService(timerService);
unregisterReceiver(broadcastReceiver);
}
}
});
}
#Override
protected void onResume() {
super.onResume();
if(!isMyServiceRunning(TimerService.class)) {
//Resets timer if no service is running
timerView.setText("0:00");
}
}
#Override
protected void onStop() {
super.onStop();
if(isMyServiceRunning(TimerService.class)) {
unregisterReceiver(broadcastReceiver);
Log.d(MainActivity.class.getSimpleName(), "unregistered broadcast");
}
}
/******************** Broadcast Receiver **************************************/
//Receives the broadcast sent out by the service and updates the UI accordingly.
private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
if(!updateUI(intent)){
if(!updateUI(timerService)){
timerService.setAction(Constants.ACTION.STOPFOREGROUND_ACTION);
startService(timerService);
showTimerCompleteNotification();
}
}
}
};
//Receives the timer from the service and updates the UI
public boolean updateUI(Intent intent){
if(!intent.hasExtra(Constants.TIMER.CURRENT_TIME)) return false;
this.currentTime = intent.getLongExtra(Constants.TIMER.CURRENT_TIME, 0L);
if(this.currentTime == duration){
timerView.setText("0:00");
Toast.makeText(this,"Timer done",Toast.LENGTH_SHORT).show();
return false;
}
int secs = (int) (currentTime / 1000);
int minutes = secs / 60;
timerView.setText(Integer.toString(minutes) + ":" + String.format("%02d", secs%60));
return true;
}
/******************************************************************************************/
/************* Helper Methods ****************************/
private void showTimerCompleteNotification() {
Intent resultIntent = new Intent(this, MainActivity.class);
PendingIntent resultPendingIntent =
PendingIntent.getActivity(
this,
0,
resultIntent,
PendingIntent.FLAG_UPDATE_CURRENT
);
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(this)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("Timer Done!")
.setContentText("Congrats")
.setContentIntent(resultPendingIntent)
.setColor(Color.BLACK)
.setLights(Color.BLUE, 500, 500)
.setDefaults(NotificationCompat.DEFAULT_VIBRATE)
.setDefaults(NotificationCompat.DEFAULT_SOUND)
.setStyle(new NotificationCompat.InboxStyle());
// Gets an instance of the NotificationManager service
final NotificationManager mNotifyMgr =
(NotificationManager) getSystemService(NOTIFICATION_SERVICE);
// Builds the notification and issues it.
mNotifyMgr.notify(Constants.NOTIFICATION_ID.FOREGROUND_SERVICE, mBuilder.build());
//Cancel the notification after a little while
Handler h = new Handler();
long delayInMilliseconds = 5000;
h.postDelayed(new Runnable() {
public void run() {
mNotifyMgr.cancel(Constants.NOTIFICATION_ID.FOREGROUND_SERVICE);
}
}, delayInMilliseconds);
}
private boolean isMyServiceRunning(Class<?> serviceClass) {
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if (serviceClass.getName().equals(service.service.getClassName())) {
return true;
}
}
return false;
}
}
Constants class:
package com.example.admin.servicetimer.service;
public class Constants {
public interface ACTION {
public static String MAIN_ACTION = "com.fahadhd.foregroundservice.action.main";
public static final String STARTFOREGROUND_ACTION = "com.fahadhd.foregroundservice.action.startforeground";
public static final String STOPFOREGROUND_ACTION = "com.fahadhd.foregroundservice.action.stopforeground";
public static final String BROADCAST_ACTION = "com.fahadhd.foregroundservice.action.broadcast";
}
public interface TIMER {
public static final String CURRENT_TIME = "com.fahadhd.foregroundservice.timer.current_time";
public static final String DURATION = "com.fahadhd.foregroundservice.timer.duration";
}
public interface NOTIFICATION_ID {
public static int FOREGROUND_SERVICE = 1;
}
}
Related
This is my first time trying to create an android app. I'm trying to create a service that can count in the background, and I want the service to broadcast the time to a receiver. It seems like the receiver does not receive anything, though, so I'm not sure if my service gets started at all.
Here's the code where I try to start the service and create the receiver:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
IntentFilter filter = new IntentFilter();
filter.addAction("EXAMPLE_BROADCAST");
clock = (TextView) findViewById(R.id.textClock);
timeReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
setClock(intent.getLongExtra("counter", 0));
}
};
registerReceiver(timeReceiver,filter);
startService(new Intent(this, CounterService.class));
}
public void setClock(long time){
clock.setText(String.valueOf(time));
}
And here's my service class:
public class CounterService extends Service {
private Handler handler;
private long initialTime;
private long timeInMilliseconds = 0L;
private boolean isActive;
Intent timeBroadcaster = new Intent("EXAMPLE_BROADCAST");
#Override
public void onCreate(){
super.onCreate();
Runnable counter = new Runnable() {
#Override
public void run() {
isActive = ((PowerManager) getSystemService(Context.POWER_SERVICE)).isInteractive();
if(isActive) {
timeInMilliseconds += 1000;
handler.postDelayed(this, 1000);
}
else {
if(timeInMilliseconds > 5000) {
timeInMilliseconds = 0;
}
handler.postDelayed(this, 1000);
}
timeBroadcaster.putExtra("counter", timeInMilliseconds);
sendBroadcast(timeBroadcaster);
}
};
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
Any help is appreciated, thanks.
The Runnable doesn't get executed itself, you need to give it to a thread.
Runnable counter = new Runnable() {
#Override
public void run() {
isActive = ((PowerManager) getSystemService(Context.POWER_SERVICE)).isInteractive();
if(isActive) {
timeInMilliseconds += 1000;
handler.postDelayed(this, 1000);
}
else {
if(timeInMilliseconds > 5000) {
timeInMilliseconds = 0;
}
handler.postDelayed(this, 1000);
}
timeBroadcaster.putExtra("counter", timeInMilliseconds);
sendBroadcast(timeBroadcaster);
}
};
Thread thread = new Thread(counter);
thread.start();
Also register your service in the manifest :
<!--Register service-->
<service android:name="yourpackage.CounterService" />
You need to register your receiver.
In your activity's onStart method
IntentFilter filter = new IntentFilter();
filter.addAction("YOUR_ACTION");
registerReceiver(timeReceiver, filter)
In your activity's onStop method
unregisterReceiver(timeReceiver)
And before sending broadcast add your action to Intent
timeBroadcaster.setAction("YOUR_ACTION");
I have an app that creates a never ending background service. The service is started when the app is launched. When the app is killed (e.g. by the user), the service sends a broadcast request that will restart it after it is killed.
The question is: when I restart the app, how can I know if the service is already running?
Naively I had thought that by restarting the service when the app is re-launched it would have stopped the existing service but this does not happen. The following code shows that if I do this, there are two services running at the same time (the printout in the timer moves from every second to every half a second).
Many thanks
public class MainActivity extends AppCompatActivity {
static Intent mServiceIntent;
private SensorService mSensorService;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ctx=this;
setContentView(R.layout.activity_main);
mSensorService= new SensorService(getCtx());
mServiceIntent = new Intent(getCtx(), mSensorService.getClass());
startService(mServiceIntent);
}
Context ctx;
public Context getCtx() {
return ctx;
}
#Override
protected void onDestroy() {
stopService(mServiceIntent);
Log.i("MAINACT", "onDestroy!");
super.onDestroy();
}
}
public class SensorService extends Service {
public int counter=0;
public SensorService(Context applicationContext) {
super();
Log.i("HERE", "here I am!");
}
public SensorService() {
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
startTimer();
return START_STICKY;
}
private Timer timer;
private TimerTask timerTask;
long oldTime=0;
public void startTimer() {
//set a new Timer
timer = new Timer();
//initialize the TimerTask's job
initializeTimerTask();
//schedule the timer, to check if the service is there in 25s
timer.schedule(timerTask, 1000, 1000); //
}
/**
* it sets the timer to check in x seconds if the sensorsService is active. If not it will start the service
*/
public void initializeTimerTask() {
timerTask = new TimerTask() {
public void run() {
long time= System.currentTimeMillis();
Log.i("in timer", "in timer ++ "+(time-oldTime)+" ++ "+ (counter++));
oldTime= time;
}
};
}
/**
* not needed
*/
public void stoptimertask() {
//stop the timer, if it's not already null
if (timer != null) {
timer.cancel();
timer = null;
}
}
#Override
public void onDestroy() {
super.onDestroy();
Log.i("EXIT", "ondestroy!");
Intent broadcastIntent = new Intent("uk.ac.shef.oak.ActivityRecognition.RestartSensor");
sendBroadcast(broadcastIntent);
stoptimertask();
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
public class SensorRestarterBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Log.i(SensorRestarterBroadcastReceiver.class.getSimpleName(), "Service Stops! Oooooooooooooppppssssss!!!!");
context.startService(new Intent(context, SensorService.class));;
}
}
private boolean isMyServiceRunning(Class<?> serviceClass) {
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if (serviceClass.getName().equals(service.service.getClassName())) {
return true;
}
}
return false;
}
And then use
isMyServiceRunning(MyService.class)
Credit
i want to start a new thread in my own service which is started from an activity. In this thread I want to update data in a database after every 3 seconds. I created the databse and initializes it in my onStartCommand() method. Where should I implement my Thread and how?
I tried this but it didn't work, the app will be unfortunately closed. Without the call of this method everything works fin.
I create this method, which i called in my onStartCommand
private void startThreadUpdatingDatabase(){
Log.d("Database", "startThreadUpdatingDatabase(was called)");
new Thread(new Runnable() {
public void run(){
//do stuff
}
}).start();
}
If you want to start a recurring task you can try different approaches:
1) Alarm
2) Handler
3) TimerTask (My least favorite)
Alarm:
private AlarmManager mAlarmManager;
private static final long ALARM_INTERVAL = 3 * 60 * 1000;
private void issueAlarm() {
if(mAlarmManager == null)
mAlarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Calendar calendar = Calendar.getInstance(Locale.US);
calendar.add(Calendar.MILLISECOND, (int) ALARM_INTERVAL);
Intent intent = new Intent(this, AlarmBroadcastReceiver.class);
alarmIntent = PendingIntent.getBroadcast(this, ALARM_REQUEST_CODE, intent, 0);
mAlarmManager.setRepeating(AlarmManager.RTC, calendar.getTimeInMillis(), ALARM_INTERVAL, alarmIntent);
}
Create your AlarmReceiver:
public class AlarmBroadcastReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
//Do DB Stuff here
}
}
And do not forget to register it in the manifest:
<receiver
android:name=".AlarmBroadcastReceiver"
android:exported="false" />
Handler:
#Override
public void onCreate() {
// Start up the thread running the service. Note that we create a
// separate thread because the service normally runs in the process's
// main thread, which we don't want to block. We also make it
// background priority so CPU-intensive work will not disrupt our UI.
HandlerThread thread = new HandlerThread("ServiceStartArguments",
Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
// Get the HandlerThread's Looper and use it for our Handler
mServiceLooper = thread.getLooper();
mServiceHandler = new ServiceHandler(mServiceLooper);
}
And queue up your postedTask
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
//As danny117 pointed out, multiple clients starting the service
//Can trigger this.
mServiceHandler.removeCallbacks(yourRunnable);
mServiceHandler.post(yourRunnable);
return super.onStartCommand(intent, flags, startId);
}
Runnable should look like:
private Runnable yourRunnable = new Runnable() {
#Override
public void run(){
//DB work here
if(mServiceHandler != null)
mServiceHandler.postDelayed(this, ALARM_INTERVAL);
}
}
Also clean up after service stops:
#Override
public void onDestroy() {
super.onDestroy();
mServiceHandler.removeCallbacks(yourRunnable);
mServiceLooper.quit();
}
Timer:
Create your Timer:
private Timer myTimer = new Timer();
Create the recurring Timer Task:
private void scheduleTask() {
myTimer.scheduleAtFixedRate(new TimerTask() {
public void run() {
//Do DB stuff here
}
}, 0, ALARM_INTERVAL);
}
References:
Scheduling Repeating Alarms
Creating a Service
To repeat with a delay you make a runnable that calls postDelayed of a handler to restart it after a set time period.
//change the notificationSmallIcon (titlebar) so it flashes every few seconds
private static Runnable iconWarnRunnable = new Runnable() {
#Override
public void run() {
if (isWarningRunning) {
long dely;
if (notificationSmallIcon == R.drawable.ic_launcher2) {
notificationSmallIcon = R.drawable.ic_launcher2x;
dely = iconWarnDelay1;
} else {
notificationSmallIcon = R.drawable.ic_launcher2;
dely = iconWarnDelay2;
}
notifyHandler.postDelayed(this, dely);
myShowNotification();
} else {
//just in nick of time
notificationSmallIcon = R.drawable.ic_launcher2;
}
}
};
final HandlerThread myThread = new HandlerThread("myHandlerThread");
private static long iconWarnDelay1;
private static long iconWarnDelay2;
#Override
public void onCreate() {
iconWarnDelay1 = 2500;
iconWarnDelay2 = 500;
myThread.start();
myThread.setPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND);
notifyHandler = new Handler(myThread.getLooper());
... somewhere you start the runnable it's really important that when you start you remove first so you always have just one running.
isWarningRunning = true;
notifyHandler.removeCallbacks(iconWarnRunnable);
notifyHandler.postDelayed(iconWarnRunnable, iconWarnDelay1);
... somewhere stop the runnable
isWarningRunning = false;
notifyHandler.removeCallbacks(iconWarnRunnable);
I have a Bound service which collects locations for 3 minutes every 15 minutes. I start a CountDownTimer once the Service is connected in onServiceConnected of ServiceConnection.
I receive all the Timer callbacks (onFinish) (onTick) perfectly as far the Activity which bindService is visible.
When Device is locked I do not receive any updates from the timer.
MyLocationService
public class MyLocationService extends Service implements MyTimerListener{
private IBinder mBinder = new MyLocationBinder();
public void onCreate() {
locationManager = new MyLocationManager(this);
}
#Override
public IBinder onBind(Intent arg0) {
return mBinder;
}
public class MyLocationBinder extends Binder
{
public MyLocationService getService()
{
return MyLocationService.this;
}
}
public void startFetchingLocations()
{
if(locationManager != null){
locationManager.startFetchingLocations();
if(publishPeriodCountDownTimer != null) {
publishPeriodCountDownTimer.cancel();
publishPeriodCountDownTimer = null;
}
publishPeriodCountDownTimer = new MyCountTimer(gpsPublishPeriod * 60 * 1000, 1000, MyLocationService.this);
publishPeriodCountDownTimer.start();
}
}
#Override
public void onTimeFinished() {
//Start fetching locations
locationManager.startFetchingLocations(); //Start 3 min CountdownTimer
}
#Override
public void onTick() {
}
public void onAllLocationsRecieved(ArrayList<Location> locations)
{
//Do stuff on Locations
locationManager.stopFetchingGPS(); //Stops 3 min countdownTimer
}
}
MyActivty
public class MyActivity
{
#Override
protected void onStart() {
super.onStart();
btnStop.setVisibility(View.VISIBLE);
MyNoificationManager.cancelAllNotifications();
if (!isLocationServiceBound) {
Intent locServiceIntent = new Intent(this, MyLocationService.class);
bindService(locServiceIntent, locationServiceConnection, Context.BIND_AUTO_CREATE);
}
}
private ServiceConnection locationServiceConnection = new ServiceConnection(){
#Override
public void onServiceDisconnected(ComponentName name) {
isLocationServiceBound = false;
}
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
MyLocationBinder locationBinder = (MyLocationBinder) service;
locationService = locationBinder.getService();
isLocationServiceBound = true;
locationService.startFetchingLocations();
}
};
}
It works as expected when the Activity is visible. The timer doesn't provide any onTick() or onTimeFinished() callbacks when the device is locked.
What could be the problem here ?
The CountDownTimer will not work when the phone goes to sleep mode. This is by design and it's to save battery life.
Alternately you can use android.app.AlarmManager instead to achieve your goal. Following is the sample code how to do that. Set the alaram like below
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(this, MyLocationService.class);
intent.putExtra("need_to_fetch_loc", true);
PendingIntent alarmIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
alarmManager.set(AlarmManager.RTC_WAKEUP, gpsPublishPeriod * 60 * 1000, alarmIntent);
Add the following method to your MyLocationService class
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if(locationManager!= null && intent.getBooleanExtra("need_to_fetch_loc", false))
{
locationManager.startFetchingLocations();
}
return super.onStartCommand(intent, flags, startId);
}
I want do develop an Android App, which calculates the battery app
So here's my idea how to do that: When the user starts the app, the app gets the battery level of the phone (For example: 80%). And after 1h the app gets the battery status again (Then it is for example: 76%). The calculation: in 1h the battery loses 4%, that means the battery will last about 20h (80/4)
I know, how to get the battery status, etc..
My question: How can I set the countdown for 1h in the background? There shouldn't be any textView where the countdown is displayed. The coundown needs to run in the background. How can I do that?
I googled and found this, but this isn't working: (I put these method in onCreate()
CountDownTimer countDownTimer = new CountDownTimer(5000, 1000) {
#Override
public void onTick(long millisUntilFinished) {
IntentFilter intFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
BroadcastReceiver batteryLevelReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0);
}
};
}
#Override
public void onFinish() {
BroadcastReceiver batteryLevelReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
int level = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0);
Toast toast = Toast.makeText(context, "Battery: " + level/4, Toast.LENGTH_LONG); // this is just, to check, if the countDown is still running
toast.show();
}
};
}
};
You need register once your broadcast receiver.
//in Oncreate
...
IntentFilter intFilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
BatteryReceiver batteryReceiver = new BatteryReceiver();
registerReceiver(batteryReceiver , intFilter);
...
public class BatteryReceiver extends BroadcastReceiver {
int startlevel = 0;
long lastUpdateTime = 0;
boolean isStartCheck = false;
#Override
public void onReceive(Context context, Intent intent) {
if(!isStartCheck){
startlevel = intent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0);
lastUpdateTime = System.currentTimeMillis();
isStartCheck = true;
} else {
if((System.currentTimeMillis() - lastUpdateTime)/1000 > 3600) {
// 1h was going. do your work.
reset();
}
}
}
public void reset() {
lastUpdateTime = System.currentTimeMillis();
isStartCheck = false;
startLevel = 0;
}
}