I have an Android app with manage a countdowntimer (class CountDownTimer) that it is shown in app screen, to show how much time left to arrive 00:00.
My problem now is that when I press home button or when I launch another app the app/timer does not run in the background. So I suspect I must do a service, but how? Colleagues have said to me that I must do something special for when the device gets locked in order for the timer to continue. I want to wake up when arrived to 0.
What is the best way to do this?
You must create your CountDownTimer class and extend it from Service class.
By doing so your activity becomes a service and it runs in the background even if you close
the application or the screen it doesnt matter it just does the task that you assign to it in the background .
First off, You need to use a Handler to operate the timer, but check out this great post to see if it helps:
How to set a timer in android
Android Developer Resources touch on this topic as well:
http://developer.android.com/resources/articles/timed-ui-updates.html
The easiest way is use the service with timer async. You can download the source from here (Android Countdown Timer Run In Background)
Service Class:
package com.countdowntimerservice;
import android.app.Service;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Handler;
import android.os.IBinder;
import android.preference.PreferenceManager;
import android.support.annotation.Nullable;
import android.util.Log;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.TimeUnit;
public class Timer_Service extends Service {
public static String str_receiver = "com.countdowntimerservice.receiver";
private Handler mHandler = new Handler();
Calendar calendar;
SimpleDateFormat simpleDateFormat;
String strDate;
Date date_current, date_diff;
SharedPreferences mpref;
SharedPreferences.Editor mEditor;
private Timer mTimer = null;
public static final long NOTIFY_INTERVAL = 1000;
Intent intent;
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
super.onCreate();
mpref = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
mEditor = mpref.edit();
calendar = Calendar.getInstance();
simpleDateFormat = new SimpleDateFormat("HH:mm:ss");
mTimer = new Timer();
mTimer.scheduleAtFixedRate(new TimeDisplayTimerTask(), 5, NOTIFY_INTERVAL);
intent = new Intent(str_receiver);
}
class TimeDisplayTimerTask extends TimerTask {
#Override
public void run() {
mHandler.post(new Runnable() {
#Override
public void run() {
calendar = Calendar.getInstance();
simpleDateFormat = new SimpleDateFormat("HH:mm:ss");
strDate = simpleDateFormat.format(calendar.getTime());
Log.e("strDate", strDate);
twoDatesBetweenTime();
}
});
}
}
public String twoDatesBetweenTime() {
try {
date_current = simpleDateFormat.parse(strDate);
} catch (Exception e) {
}
try {
date_diff = simpleDateFormat.parse(mpref.getString("data", ""));
} catch (Exception e) {
}
try {
long diff = date_current.getTime() - date_diff.getTime();
int int_hours = Integer.valueOf(mpref.getString("hours", ""));
long int_timer = TimeUnit.HOURS.toMillis(int_hours);
long long_hours = int_timer - diff;
long diffSeconds2 = long_hours / 1000 % 60;
long diffMinutes2 = long_hours / (60 * 1000) % 60;
long diffHours2 = long_hours / (60 * 60 * 1000) % 24;
if (long_hours > 0) {
String str_testing = diffHours2 + ":" + diffMinutes2 + ":" + diffSeconds2;
Log.e("TIME", str_testing);
fn_update(str_testing);
} else {
mEditor.putBoolean("finish", true).commit();
mTimer.cancel();
}
}catch (Exception e){
mTimer.cancel();
mTimer.purge();
}
return "";
}
#Override
public void onDestroy() {
super.onDestroy();
Log.e("Service finish","Finish");
}
private void fn_update(String str_time){
intent.putExtra("time",str_time);
sendBroadcast(intent);
}
}
Related
I'm making this app that tells how much longer till we reach a destination but whenever I try to use it it just black screens just crashes. I think it has something to do with the loop but I can't find anything online Can anyone help? Thanks in advance.................................................................................................................
package com.example.myapplication;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
import java.util.Calendar;
import java.util.TimeZone;
import java.time.*;
import java.math.*;
public class TimeTIll extends AppCompatActivity {
private static final String DateUtils = null;
TextView txt;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_time_till);
txt = (TextView) findViewById(R.id.TimeDipslay);
int i = 0;
while (i < 10) {
Program();
i++;
try {
Thread.sleep(1000);
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
}
public void Program() {
int totalMinutesSinceMidnight = 0;
long ms = 0;
long secondsM = 0;
long SecondsTillEstimate = 0;
long SecondsTillArrival1 = 0;
double MinutesTillArrival = 0;
double hoursTillArrival = 0;
double SecondsTillArrival = 0;
Calendar now = Calendar.getInstance();
Calendar midnight = Calendar.getInstance();
midnight.set(Calendar.HOUR_OF_DAY, 0);
midnight.set(Calendar.MINUTE, 0);
midnight.set(Calendar.SECOND, 0);
midnight.set(Calendar.MILLISECOND, 0);
try {
Thread.sleep(2000);
} catch (InterruptedException ex) {
Thread.currentThread().interrupt();
}
now = Calendar.getInstance();
midnight = Calendar.getInstance();
ms = now.getTime().getTime() - midnight.getTime().getTime();
totalMinutesSinceMidnight = (int) (ms / 1000);
secondsM = totalMinutesSinceMidnight;
SecondsTillEstimate = (60 * 60 * 23);
SecondsTillArrival1 = SecondsTillEstimate - secondsM;
hoursTillArrival = Math.floor(SecondsTillArrival1 / 3600);
SecondsTillArrival1 -= (hoursTillArrival * 3600);
MinutesTillArrival = Math.floor(SecondsTillArrival1 / 60);
SecondsTillArrival1 -= (MinutesTillArrival * 60);
SecondsTillArrival = SecondsTillArrival1;
txt.setText((int) hoursTillArrival + " Hours " + (int) MinutesTillArrival + " Minutes " + (int) SecondsTillArrival + " Seconds Till Arrival In Floriada");
}
}
Please read about the activity life-cycle. You cannot call long-running tasks in onCreate and expect them to show anything other than an ANR crash (your onCreate method blocks the UI thread with all those sleep statements and generates an ANR crash before it even gets to the onStart and onResume stages where it actually shows the activity).
If you want the UI to change dynamically, you could use a handler to execute a method at a regular interval. For example, to update something at a regular interval (1000 ms here) you could do:
public class MainActivity extends AppCompatActivity {
private final Handler handler = new Handler();
private TextView txt;
private int count = 0;
private final int updateFreqMs = 1000; // call update every 1000 ms
#Override
protected void onPause() {
super.onPause();
handler.removeCallbacksAndMessages(null);
}
#Override
protected void onResume() {
super.onResume();
handler.postDelayed(new Runnable() {
#Override
public void run() {
updateTime();
handler.postDelayed(this, updateFreqMs);
}
}, updateFreqMs);
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_time_till);
txt = findViewById(R.id.TimeDipslay);
}
private void updateTime() {
// here you update the displayed value
// this will be called every second indefinitely
// do your math and generate a string to print, just showing
// a counter here to demonstrate
++count;
txt.setText("Count = " + count);
}
}
I think that the problem is that you are trying to make sleep the main thread of the application. In Android I knew that for security reasons this can't be done.
(Look this https://www.codementor.io/tips/0743378261/non-freezing-sleep-in-android-app for details )
Try this solution instead:
How to pause / sleep thread or process in Android?
You Shouldn`t use it inside main thread specially when you are doing operation inside it.
You may have to use some thing like that.
viewModelScope.launch(Dispatchers.IO) {
while (statues==true){
if (statue==true){
delay(500)
//ToDo
}
}
}
So, in my Android sensor app, I have a service (foreground) which runs continuously in a different process and records separate sensors' data (accelerometer, magnetometer, GPS). I have used a timer of period 100 ms to fetch and write the data in various CSV files for different sensors. The problem is that the data recording is not consistent, i.e. the timer is not working properly or something. It skips some intervals like for few 100 ms or sometimes even few secs while it should record ten samples per second. I have defined custom classes for the sensors, and they are working properly.
Here is my code for the service:
package com.example.ark.ark.Services;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import java.text.SimpleDateFormat;
import android.os.Build;
import android.os.Environment;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.support.annotation.RequiresApi;
import android.support.v7.app.NotificationCompat;
import com.example.ark.ark.Constants;
import com.example.ark.ark.R;
import com.example.ark.ark.Sensors.Accelerometer;
import com.example.ark.ark.Sensors.Gps;
import com.example.ark.ark.Sensors.Magnetometer;
import com.example.ark.ark.Sensors.Rotation;
import com.example.ark.ark.activity.MainActivity;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.*;
import static com.example.ark.ark.Constants.*;
/**
* Created by ark on 12/8/17.
*/
#RequiresApi(api = Build.VERSION_CODES.N)
public class DataRecording extends Service {
private Magnetometer magnetometer;
private Accelerometer accelerometer;
private Gps gps;
private Rotation rot;
private Timer myTimer, gpsTimer;
//frequency variables
private int acc_mag_freq = 100;
private int gps_freq = 2000;
SimpleDateFormat time = new SimpleDateFormat(TIMESTAMP_FORMAT);
SimpleDateFormat time1 = new SimpleDateFormat("dd-MM-yyyy_HH:mm:ss");
//Declaring file variables
private File sdDirectory = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + DIRECTORY);
File accDirectory = new File(sdDirectory + Constants.DIRECTORY_ACC);
File magDirectory = new File(sdDirectory + Constants.DIRECTORY_MAG);
File gpsDirectory = new File(sdDirectory + Constants.DIRECTORY_GPS);
File rotationDirectory = new File(sdDirectory + Constants.DIRECTORY_ROTATION);
private File datafile_Rot;
private File dataFile_Acc;
private File dataFile_Mag;
private File dataFile_Gps;
private FileOutputStream dataOutputStream_Acc, dataOutputStream_Mag, dataOutputStream_Gps, dataOutputStream_Rot;
private StringBuilder data_Acc = new StringBuilder();
private StringBuilder data_Mag = new StringBuilder();
private StringBuilder data_Gps = new StringBuilder();
private StringBuilder data_Rot = new StringBuilder();
private String mode = new String();
private String acc_name, mag_name, rot_name, gps_name, user = "";
#Override
public void onCreate() {
Intent notify = new Intent(this, MainActivity.class);
PendingIntent pendingintent = PendingIntent.getActivity(this, 0, notify, 0);
Notification notification = new NotificationCompat.Builder(this)
.setSmallIcon(R.mipmap.icon_1_round)
.setContentTitle("Mobility")
.setContentText("Recording Data")
.setContentIntent(pendingintent).build();
startForeground(1337, notification);
//Creating objects of the different sensor's class
magnetometer = new Magnetometer(this);
accelerometer = new Accelerometer(this);
gps = new Gps(this);
rot = new Rotation(this);
data_Gps.append("\n");
data_Acc.append("\n");
if (magnetometer.isMagAvailable()) {
data_Rot.append("\n");
data_Mag.append("\n");
}
super.onCreate();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
if (intent != null && intent.getExtras() != null) {
user = intent.getExtras().getString("username");
String t = intent.getExtras().getString("acc_mag_freq");
String r = intent.getExtras().getString("gps_freq");
mode = intent.getExtras().getString("mode");
acc_mag_freq = Integer.parseInt(t);
gps_freq = Integer.parseInt(r);
}
//Creating variables for file names
acc_name = user + "_" + time1.format(new Date()) + "_" + Constants.DATA_FILE_NAME_ACC;
gps_name = user + "_" + time1.format(new Date()) + "_" + Constants.DATA_FILE_NAME_GPS;
mag_name = user + "_" + time1.format(new Date()) + "_" + Constants.DATA_FILE_NAME_MAG;
rot_name = user + "_" + time1.format(new Date()) + "_" + Constants.DATA_FILE_NAME_ROTATION;
dataFile_Acc = new File(accDirectory, acc_name);
if (magnetometer.isMagAvailable()) {
dataFile_Mag = new File(magDirectory, mag_name);
if (mode.equals("1"))
datafile_Rot = new File(rotationDirectory, rot_name);
}
dataFile_Gps = new File(gpsDirectory, gps_name);
try {
dataOutputStream_Acc = new FileOutputStream(dataFile_Acc, true);
if (magnetometer.isMagAvailable()) {
dataOutputStream_Mag = new FileOutputStream(dataFile_Mag, true);
if (datafile_Rot != null)
dataOutputStream_Rot = new FileOutputStream(datafile_Rot, true);
}
dataOutputStream_Gps = new FileOutputStream(dataFile_Gps, true);
} catch (IOException e) {
e.printStackTrace();
}
myTimer = new Timer();
myTimer.schedule(new TimerTask() {
#Override
public void run() {
getDataSensors();
}
}, 0, acc_mag_freq);
gpsTimer = new Timer();
gpsTimer.schedule(new TimerTask() {
#Override
public void run() {
getDataGps();
}
}, 0, gps_freq);
return START_STICKY;
}
public void getDataGps() {
data_Gps.append(time.format(new Date()));
data_Gps.append(",");
float[] gpsreading = gps.getReading();
data_Gps.append(String.format("%.10f", gpsreading[0]));
data_Gps.append(",");
data_Gps.append(String.format("%.10f", gpsreading[1]));
data_Gps.append("\n");
try {
dataOutputStream_Gps.write(data_Gps.toString().getBytes());
data_Gps.setLength(0);
} catch (IOException e) {
e.printStackTrace();
}
}
public void getDataSensors() {
data_Acc.append(time.format(new Date()));
data_Acc.append(",");
if (magnetometer.isMagAvailable()) {
data_Mag.append(time.format(new Date()));
data_Mag.append(",");
if (mode == "1") {
data_Rot.append(time.format(new Date()));
data_Rot.append(",");
}
}
float[] acc = accelerometer.getLastReading();
float[] mag = magnetometer.getLastReading();
float[] rotVal = rot.getLastReading();
//accelerometer data_Acc
data_Acc.append(String.format("%.3f", acc[0]));
data_Acc.append(",");
data_Acc.append(String.format("%.3f", acc[1]));
data_Acc.append(",");
data_Acc.append(String.format("%.3f", acc[2]));
//data_Acc.append(",");
if (magnetometer.isMagAvailable()) {
//magnetometer data_Acc
data_Mag.append(String.format("%.3f", mag[0]));
data_Mag.append(",");
data_Mag.append(String.format("%.3f", mag[1]));
data_Mag.append(",");
data_Mag.append(String.format("%.3f", mag[2]));
//data_Acc.append(",");
// write this data_Acc to file
data_Mag.append("\n");
if (mode == "1") {
data_Rot.append(String.format("%.3f", rotVal[0]));
data_Rot.append(",");
data_Rot.append(String.format("%.3f", rotVal[1]));
data_Rot.append(",");
data_Rot.append(String.format("%.3f", rotVal[2]));
//data_Acc.append(",");
// write this data_Acc to file
data_Rot.append("\n");
}
}
data_Acc.append("\n");
try {
dataOutputStream_Acc.write(data_Acc.toString().getBytes());
if (magnetometer.isMagAvailable()) {
dataOutputStream_Mag.write(data_Mag.toString().getBytes());
if (mode == "1") {
dataOutputStream_Rot.write(data_Rot.toString().getBytes());
}
}
data_Acc.setLength(0);
data_Rot.setLength(0);
data_Mag.setLength(0);
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void onDestroy() {
super.onDestroy();
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
As far as I know Android is not designed to support hard real time events, so I don't think you can achieve that in any way.
Anyway I think you could try using alarms and defining the service as a foreground service, if you can use these things in your app, from what I know it won't ensure that data retrieval is executed every 100 ms, but it's likely to do it on a more regular basis.
I am getting time when screen off and screen on .
I am getting time when screen off in "text1".
and getting time when screen on in "text2".
Now i want to calculate time difference between screen off and screen on.
Unable to calculate time difference .
I want to show time difference in "text3".
MainActivity.java
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.text.format.DateFormat;
import android.util.Log;
import android.widget.TextView;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
public class MainActivity extends Activity {
TextView text1,text2,text3;
Date Date1,Date2;
Date d,d1;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
text1 = (TextView) findViewById(R.id.text1);
text2 = (TextView) findViewById(R.id.text2);
text3 =(TextView) findViewById(R.id.text3);
Timer updateTimer = new Timer();
updateTimer.schedule(new TimerTask()
{
public void run()
{
try
{
String string1 = "s";
Date time1 = new SimpleDateFormat("HH:mm:ss ").parse(string1);
Calendar calendar1 = Calendar.getInstance();
calendar1.setTime(time1);
String string2 = "s1";
Date time2 = new SimpleDateFormat("HH:mm:ss ").parse(string2);
Calendar calendar2 = Calendar.getInstance();
calendar2.setTime(time2);
calendar2.add(Calendar.DATE, 1);
Date x = calendar1.getTime();
Date xy = calendar2.getTime();
long diff = x.getTime() - xy.getTime();
int Hours = (int) (diff/( 60 * 60));
int Mins = (int) (diff/(60)) % 60;
//System.out.println("diff hours" + diffHours);
String diff1 = Hours + ":" + Mins; // updated value every1 second
text3.setText( diff1);
}
catch (Exception e)
{
e.printStackTrace();
}
}
}, 0, 1000);
// INITIALIZE RECEIVER
IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_SCREEN_OFF);
BroadcastReceiver mReceiver = new ScreeReceiver();
registerReceiver(mReceiver, filter);
// YOUR CODE
}
#Override
protected void onPause() {
// WHEN THE SCREEN IS ABOUT TO TURN OFF
if (ScreeReceiver.wasScreenOn) {
// THIS IS THE CASE WHEN ONPAUSE() IS CALLED BY THE SYSTEM DUE TO A SCREEN STATE CHANGE
d = new Date();
CharSequence s = DateFormat.format("hh:mm:ss", d.getTime());
// d=format.parse(d);
// s=d.getTime();
///Log.i(""+s);
System.out.println("SCREEN TURNED OFF");
text1.setText("SCREEN TURNED OFF" + s);
Log.i("hi", "s"+s);
//System.out.println("s");
} else {
// THIS IS WHEN ONPAUSE() IS CALLED WHEN THE SCREEN STATE HAS NOT CHANGED
}
super.onPause();
}
#Override
protected void onResume() {
// ONLY WHEN SCREEN TURNS ON
if (!ScreeReceiver.wasScreenOn) {
// THIS IS WHEN ONRESUME() IS CALLED DUE TO A SCREEN STATE CHANGE
d1 = new Date();
CharSequence s1 = DateFormat.format("hh:mm:ss", d1.getTime());
text2.setText("SCREEN TURNED ON" + s1);
Log.i("hi", "s"+s1);
// System.out.println("s");
System.out.println("SCREEN TURNED ON");
} else {
}
super.onResume();
}
}
ScreenReceiver.java
public class ScreeReceiver extends BroadcastReceiver {
public static boolean wasScreenOn = true;
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
// DO WHATEVER YOU NEED TO DO HERE
wasScreenOn = false;
} else if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
// AND DO WHATEVER YOU NEED TO DO HERE
wasScreenOn = true;
}
}
}
1:
String string1 = "s";
can't be parsed as a Date. Why don't you use the d and d1 fields you've stored?
long diff = d1.getTime() - d.getTime();
2:
public long getTime() Returns the number of milliseconds since January
1, 1970, 00:00:00 GMT represented by this Date object.
(Bold my emphasis)
You calculate the time difference as if it were in seconds.
Try:
int Hours = (int) (diff/( 60 * 60 * 1000));
int Mins = (int) (diff/(60 * 1000)) % 60;
Attaching image in which i am getting screen on/off time. and difference time between them.
public class MainActivity extends Activity {
TextView text1,text2,text3;
Date d,d1;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
text1 = (TextView) findViewById(R.id.text1);
text2 = (TextView) findViewById(R.id.text2);
text3 =(TextView) findViewById(R.id.text3);
// INITIALIZE RECEIVER
IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_SCREEN_OFF);
BroadcastReceiver mReceiver = new ScreeReceiver();
registerReceiver(mReceiver, filter);
// YOUR CODE
}
#Override
protected void onPause() {
// WHEN THE SCREEN IS ABOUT TO TURN OFF
if (ScreeReceiver.wasScreenOn) {
// THIS IS THE CASE WHEN ONPAUSE() IS CALLED BY THE SYSTEM DUE TO A SCREEN STATE CHANGE
d = new Date();
CharSequence s = DateFormat.format("hh:mm:ss", d.getTime());
System.out.println("SCREEN TURNED OFF");
text1.setText("SCREEN TURNED OFF" + s);
Log.i("hi", "s"+ s);
} else {
// THIS IS WHEN ONPAUSE() IS CALLED WHEN THE SCREEN STATE HAS NOT CHANGED
}
super.onPause();
}
#Override
protected void onResume() {
// ONLY WHEN SCREEN TURNS ON
if (!ScreeReceiver.wasScreenOn) {
// THIS IS WHEN ONRESUME() IS CALLED DUE TO A SCREEN STATE CHANGE
d1 = new Date();
CharSequence s1 = DateFormat.format("hh:mm:ss", d1.getTime());
text2.setText("SCREEN TURNED ON" + s1);
Log.i("hi", "s"+ s1);
System.out.println("SCREEN TURNED ON");
Timer updateTimer = new Timer();
updateTimer.schedule(new TimerTask()
{
public void run()
{
try
{
runOnUiThread(new Runnable() {
#Override
public void run() {
//stuff that updates ui
long diff = d1.getTime() - d.getTime();
int Hours = (int) (diff/( 1000* 60 * 60));
int Mins = (int) (diff/(1000*60)) % 60;
Log.i("Difference ", ""+ diff);
String diff1= Hours + ":" + Mins; // updated value every 60 second
text3.setText( diff1 );
Log.i("Differ", "diff"+ diff1);
}
});
}
catch (Exception e)
{
e.printStackTrace();
}
}
}, 0, 600000);
} else {
}
super.onResume();
}
}
My understanding is,existing Screen OFF and ON intents are not exactly mean that the device is in sleep and waked up respectively. Any applications on the device holds partial wake lock, device will not be in deep sleep but screen may be off/on.
Is there any intents to listen CPU "WAKE UP" and "SLEEP" ?
Is there any way, we know CPU is waked UP from deep sleep ?
I needed a tool to do exactly this when troubleshooting some timing behavior on my app in the background. So I made my own class to do it. See code below. Here's how you use it:
CpuSleepDetector.getInstance().setSleepEndNotifier(new CpuSleepDetector.SleepEndNotifier() {
#Override
public void cpuSleepEnded(long sleepDurationMillis) {
Log.d(TAG, "The CPU just exited sleep. It was sleeping for "+sleepDurationMillis+" ms.");
}
});
CpuSleepDetector.getInstance().logDump();
The logDump method will dump the last 100 sleep events to LogCat. This is useful in troubleshooting, becaue to get the CPU to sleep, I had to not only disconnect my USB cable from my phone, I actually had to turn off my adb connection over WiFi. This way, you can reconnect adb at a later time and use the logDump method to get recent detections.
I know this is an old question, but hopefully this will be useful to somebody else.
Here's the code for the detector class:
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.SystemClock;
import android.util.Log;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
public class CpuSleepDetector {
private static final String TAG = CpuSleepDetector.class.getSimpleName();
private static CpuSleepDetector instance = null;
private HandlerThread thread;
private Handler handler;
private SleepEndNotifier notifier;
public static CpuSleepDetector getInstance() {
if (instance == null) {
instance = new CpuSleepDetector();
}
return instance;
}
private CpuSleepDetector() {
thread = new HandlerThread("cpuSleepDetectorThread");
thread.start();
handler = new Handler(thread.getLooper());
watchForSleep();
}
private void watchForSleep(){
// uptime stalls when cpu stalls
final long uptimeAtStart = SystemClock.uptimeMillis();
final long realtimeAtStart = SystemClock.elapsedRealtime();
handler.postDelayed(new Runnable() {
#Override
public void run() {
long uptimeAtEnd = SystemClock.uptimeMillis();
long realtimeAtEnd = SystemClock.elapsedRealtime();
long realtimeDelta = realtimeAtEnd - realtimeAtStart;
long uptimeDelta = uptimeAtEnd - uptimeAtStart;
final long sleepTime = realtimeDelta - uptimeDelta;
if (sleepTime > 1) {
detectedStalls.put(new Date(), sleepTime);
prune();
if (notifier != null) {
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
notifier.cpuSleepEnded(sleepTime);
}
});
}
}
watchForSleep();
}
}, 1000);
}
private HashMap<Date,Long> detectedStalls = new HashMap<Date,Long>();
private HashMap<Date,Long> getDetectedStalls() {
return detectedStalls;
}
private void prune() {
int numberToPrune = detectedStalls.size() - 100;
if (numberToPrune > 0) {
HashMap<Date,Long> newDetectedStalls = new HashMap<Date,Long>();
ArrayList<Date> dates = new ArrayList<>(getDetectedStalls().keySet());
Collections.sort(dates);
for (int i = numberToPrune; i < detectedStalls.size(); i++) {
newDetectedStalls.put(dates.get(i), detectedStalls.get(dates.get(i)));
}
detectedStalls = newDetectedStalls;
}
}
public void logDump() {
Log.d(TAG, "Last 100 known CPU sleep incidents:");
ArrayList<Date> dates = new ArrayList<>(getDetectedStalls().keySet());
Collections.sort(dates);
for (Date date: dates) {
Log.d(TAG, ""+date+": "+getDetectedStalls().get(date));
}
}
public void setSleepEndNotifier(SleepEndNotifier notifier) {
this.notifier = notifier;
}
public interface SleepEndNotifier {
public void cpuSleepEnded(long sleepDurationMillis);
}
}
i am working on a task scheduler applications as my college project, i have a service that checks the expire time of an task. i had implemented handlers to check the expire time. When the expire time of an application is matched with current time then it sends a status bar notification. At this point i am pausing the Thread using Thread.sleep method for a minute, which is causing my application to hang. At logcat it shows heavy CPU usages by applications.
i am fetching the data from database, but it works fine when Thread.sleep is not called.
Please help.
Here is the code:
package com.apps.niit.taskm;
import java.util.ArrayList;
import java.util.Calendar;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.util.Log;
public class ExpireTimeService extends Service {
DataHelper dh;
ArrayList<String> tData=new ArrayList<String>();
String date;
Calendar c;
String str;
String str1;
String str2;
String str3;
String str4;
String str5;
int notificationID=1;
String [][] data;
NotificationManager notificationManager;
#Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
return null;
}
#Override
public void onCreate(){
super.onCreate();
dh = new DataHelper(this);
fetchData();
handler.removeCallbacks(updateTimeTask);
handler.postDelayed(updateTimeTask, 1000);
}
public void fetchData(){
String eDate = android.text.format.DateFormat.format("d/M/yyyy", new java.util.Date()).toString();
tData.addAll(this.dh.selectDate(eDate));
data =new String[tData.size()][4];
if(!tData.isEmpty()){
for(int i=0; i<tData.size();i++){
breakString(tData.get(i));
data[i][0]=str1;
data[i][1]=str2;
data[i][2]=str3;
data[i][3]=str4;
}
}
}
public void stopService(){
stopSelf();
}
private Runnable updateTimeTask = new Runnable() {
public void run(){
try {
String time = android.text.format.DateFormat.format("k:m", new java.util.Date()).toString();
for(int i=0; i<tData.size();i++){
if(data[i][3].equals(time)){
//send notification code goes here
String serName = Context.NOTIFICATION_SERVICE;
notificationManager = (NotificationManager)getSystemService(serName);
String ticker= data[i][0]+" "+data[i][1]+" "+data[i][2]+" "+data[i][3];
long when= System.currentTimeMillis();
int icon = R.drawable.icon;
Notification notification = new Notification(icon, ticker,when);
Intent intent = new Intent (getApplicationContext(), DisplayTask.class);
Bundle myBundle = new Bundle();
myBundle.putString("str1", data[i][0]);
myBundle.putString("str2", data[i][1]);Log.i("data1",data[i][1]);
myBundle.putString("str3", data[i][2]);Log.i("data1",data[i][2]);
myBundle.putString("str4", data[i][3]);Log.i("data1",data[i][3]);
intent.putExtras(myBundle);
PendingIntent launchIntent = PendingIntent.getActivity(getApplicationContext(), 0, intent, 0);
notification.setLatestEventInfo(getApplicationContext(), "", "", launchIntent);
notificationID=1;
notificationManager.notify(notificationID, notification);
Thread.sleep(10000);
}
}
handler.postDelayed(this, 1000);
} catch (Exception e) {
// TODO Auto-generated catch block
Log.e("Error from service", e.toString());
}
}
};
private void breakString(String str) {
// TODO Auto-generated method stub
str1 = str.substring(0, str.indexOf(";"));
str = str.substring(str1.length()+1, str.length());
str2 = str.substring(0, str.indexOf(";"));
str = str.substring(str2.length()+1, str.length());
str3 = str.substring(0, str.indexOf(";"));
str4 = str.substring(str3.length()+1, str.length());
}
#Override
public void onDestroy() {
super.onDestroy();
if (handler != null)
handler.removeCallbacks(updateTimeTask);
handler = null;
}
private Handler handler = new Handler();
}
When you're using Handler's postDelayed function here is what happens:
Handler puts your Runnable into the current thread's Looper queue.
When time comes, your code in Runnable is run on the UI thread.
Note that its not that Handler always puts Runnable to UI thread queue. It puts Runnable to current thread's queue, and your current thread was the UI thread.
So if you put Thread.sleep or anything time consuming (like network communication) in that updateTimeTask it will hang the whole UI thread.
In your case you should use ScheduledExecutorService, see scheduleWithFixedDelay function. Or as alternative, you can start AsyncTask from your updateTimeTask function and do all heavy-lifting and Thread.sleep in doInBackgrund function.