I am writing a data logger app and I need to make an http request every 5 minutes exactly. The user is aware of the battery drain and that's ok for me. I am using a foreground service with the appropriate notification for that and I have a handler thread to witch I post runnable tasks every 5 minutes. It seems that when the phone enter DOZE MODE the thread is suspended and no runnables are executed. Is that normal behaviour or I am missing something?
Any help on how to do that will be appreciated.
Service code that starts the thread:
private void startTheForegroundService() {
NotificationCompat.Builder builder = new NotificationCompat.Builder(MainService.this);
builder.setPriority(Notification.PRIORITY_HIGH);
startForeground(1000, builder.build());
httpThread = new HttpThread("Ping Thread", Thread.NORM_PRIORITY);
httpThread.start();
httpThread.loop();
}
Thread code:
public class HttpThread extends HandlerThread {
public Handler mHandler;
public HttpThread(String name, int priority) {
super(name, priority);
}
public synchronized void waitUntilReady() {
mHandler = new Handler(getLooper(), new Handler.Callback() {
#Override
public boolean handleMessage(Message msg) {
return false;
}
});
}
public void loop() {
waitUntilReady();
mHandler.post(new Runnable(){
#Override
public void run() {
//code for the http request.
}
});
}
}
Is that normal behaviour
Yes, for the Doze mode in Android 6.0, which triggers when the app has been stationary for an hour or so. The M Developer Preview extends Doze mode, such that it also partially triggers even when the device is moving, though I think your scenario would continue to work in that case.
The user is aware of the battery drain
The user can add you to the battery optimization whitelist (Settings > Apps > (gear icon) > Battery Optimization).
Related
I have a Service that shows a toast every 10s, but I'd expect Doze to slow it to every 15 minutes when it's in the background as the app is not whitelisted. Here's my service:
#Override
public void onCreate() {
super.onCreate();
if (mTimer != null)
mTimer = null;
// Create new Timer
mTimer = new Timer();
// Required to Schedule DisplayToastTimerTask for repeated execution with an interval of `2 min`
mTimer.scheduleAtFixedRate(new DisplayToastTimerTask(), TIMER_DELAY, TIMER_INTERVAL);
}
...
private class DisplayToastTimerTask extends TimerTask {
#Override
public void run() {
mHandler.post(new Runnable() {
#Override
public void run() {
Toast.makeText(getApplicationContext(), "Hello world", Toast.LENGTH_SHORT).show();
}
});
}
}
What I'm getting in the emulator is that the toast is shown every 10s even when I'm at the home screen. Doesn't Doze restrict all non-whitelisted background services/apps to 15 min waking time?
Doze occurs only when the screen is off, and has been for some minimum amount of time. It has nothing to do with just backgrounding.
In addition, Timers or Threads set by the service wouldn't automatically be canceled, as 1)the OS doesn't know about them and 2)There's no way to know if its safe to do so, or if doing so would cause deadlock or other errors. They may be delayed to the Doze allowed rate of execution, but would not be stopped.
I want to write a app to do advertise in android BLE. I want to get the "when advertising stop event", what should I do? I need to do something like: when advertising timeout, do something. But I can I get the timeout or stop event?
Based on your comments, which indicate that you want to advertise for a bit, then stop, your best bet is to use a Handler.
In the Runnable() code, you could either set a flag, as I've done below, or call some other method to handle a more complex operation. If you get a device that connects before the timeout expires, you can call mHandler.removeCallBacks().
import android.os.Handler;
public class thisClase {
Handler mHandler = new Handler();
boolean timedOut = false;
public static final int TIMEOUT = 30*1000; //30 second timeout
Runnable mRunnable = new Runnable() {
#Override
public void run() {
//Put your code to stop advertisting here.
timedOut = true;
}
}
public void startAdvert() {
mHandler.postDelayed(mRunnable, TIMEOUT);
//Put code to start advertising here.
timedOut=false;
//Start Advertising.
}
//Call this method when a device connects to cancel the timeout.
public void whenDeviceConnects() {
//Cancel the runnable.
mHandler.removeCallbacks(mRunnable);
}
}
I have a Runnable r that is triggered periodically once per minute by handler.postDelayed(60*1000). And I test my code on a real device.
However I notice, when the phone is charging (AC or USB) , it works well, but if the phone is disconnected, the Runnable r will only run once every 20min or even worse, several hours. Thanks for any help!
Below is the code:
private final Runnable listen_to_server = new Runnable(){
public void run(){
new Thread() {
public void run() {
try {
handler.sendMessage(handler.obtainMessage(PING_SERVER,"listen"));
synchronized (listen) {
listen.wait();
}
handler.postDelayed(listen_to_server, 50000);
}
catch (Exception e) {
e.printStackTrace();
}
}
}.start();
}
};
In the Handler:
handler=new Handler() {
#Override
public void handleMessage(final Message msg) {
switch (msg.what) {
case PING_SERVER:
String type = (String) msg.obj;
add_log("ping server"); // print to screen and Log.d
...
...
}
}
}
Referring to this link i think the problem might be, as they said:
Android is not a real-time operating system. All postDelayed()
guarantees is that it will be at least the number of milliseconds
specified. Beyond that will be dependent primarily on what the main
application thread is doing (if you are tying it up, it cannot process
the Runnable), and secondarily on what else is going on the device
(services run with background priority and therefore get less CPU time
than does the foreground).
How to fix this? in this post the solution was the following:
You'll probably have better luck using the AlarmManager for such a
long delay. Handler is best for ticks and timeouts while your app is
in the foreground.
so try implement an AlarmManager looking at the doc.
Hope i helped :)
I am making modifications in a previous project, which is basically developed for kids and teenagers. It blocks text messaging, emails, calls and internet when a user drives at speed greater than 16 MPH. The service is activated through SMS. Modification is required as the app doesn't works on Android v4.1 and above. I have seen an app on PlayStore named Safely Go. This app has a button on click of which, status changes to driving. In this mode, if a user clicks on dialer, messaging, settings or browser, their own activity opens up, instead of default behavior. In short, in this mode a user cannot access anything except those applications, which the user has chosen to use during driving.
As per my requirement, the service will be activated through SMS. Once service gets activated, and user's speed reaches more than 16 MPH, I want that when user clicks on Dialer, my activity should open which will show a warning message, instead of opening dial pad.
I am not getting how can I accomplish this feature. As a service has no direct interaction with user, so the android system cannot catch key event in service. Or, is there any other way to do this.
I simply want to know how can I override default behavior dialer. If someone has any information please share, as it would be of great help for me.
In the app that I mentioned in my question no default behavior was overridden. That was my mistake, I took it in wrong way. The same functionality can be achieved using Handler. In my app I used a service which starts on click of a button. Inside the service,, I had used handler. Below, I am posting the code snippet.
public class DialerService extends Service {
ActivityManager am;
List<RunningAppProcessInfo> mAppProcessInfosList;
private Runnable myRunnable;
boolean threadDone = true;
Handler mHandler;
boolean isLockedAppRunning = false;
#Override
public IBinder onBind(Intent arg0) {
return null;
}
public void onCreate() {
am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
mAppProcessInfosList = new ArrayList<ActivityManager.RunningAppProcessInfo>();
mHandler = new Handler();
Log.v("Dialer Service", "onCreate called");
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
myRunnable = new Runnable() {
#Override
public void run() {
isRestrictedAppRunning();
}
};
new Thread(new Runnable() {
public void run() {
while (threadDone) {
try {
mHandler.post(myRunnable);
} catch (Exception e) {
}
}
}
}).start();
return START_STICKY;
}
private void isRestrictedAppRunning() {
mAppProcessInfosList = am.getRunningAppProcesses();
for (int i = 0; i < mAppProcessInfosList.size(); i++) {
if (mAppProcessInfosList.get(i).processName
.equals("com.android.phone")
|| mAppProcessInfosList.get(i).processName
.equals("com.android.email")
|| mAppProcessInfosList.get(i).processName
.equals("com.android.mms")) {
isLockedAppRunning = true;
Intent dialogIntent = new Intent(getBaseContext(),
TestActivity.class);
dialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
getApplication().startActivity(dialogIntent);
}
}
}
#Override
public void onDestroy() {
super.onDestroy();
this.threadDone = false;
}
}
This code is from my dummy app. There are lot more conditions in actual app and still lots of things need to implemented. But, what I needed actually will work in this way.
I have to run a bit of code in the background every one second, the code will call a webservice which searches a database and returns a value to the application. My question is which method would be the most effective to do this? I have read up on Timers, Threads, AsyncTask and Services and each seem to have their pros and cons. Please can someone tell me which would be the best to use considering execution time and battery life.
Thanks
Update:
I decided to use Aysnc task to run my code in the background while using a TimeTask to trigger the AsyncTask at regular intervals. This way the operation is destroyed when I leave that particular activity
You should use the service to do the background operation but in your case you want to run code in 1 sec here is the example of service using handler it call in every 1 sec.
public class YourService extends Service {
private static final String TAG = "Your Service";
private final Handler handler = new Handler(){
#Override
public void handleMessage(Message msg) {
}
};
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
// Toast.makeText(this, "My Service Created", Toast.LENGTH_LONG).show();
Log.d(TAG, "onCreate");
}
#Override
public void onDestroy() {
super.onDestroy();
// Toast.makeText(this, "My Service Stopped", Toast.LENGTH_LONG).show();
handler.removeCallbacks(sendUpdatesToUI);
}
private Runnable sendUpdatesToUI = new Runnable() {
public void run() {
/// Any thing you want to do put the code here like web service procees it will run in ever 1 second
handler.postDelayed(this, 1000); // 1 seconds
}
};
#Override
public void onStart(Intent intent, int startid) {
handler.removeCallbacks(sendUpdatesToUI);
handler.postDelayed(sendUpdatesToUI, 1000);//1 second
Log.d(TAG, "onStart");
}
}
and service can't run every time android idle the service within 3 or 4 hr i suggested you to use the foreground service to use your process long running.
For operations like this I tend to use a Service component. for the task itself i use an AsyncTask which will wait a set time before it repeats itself (using a while loop).
You will have to create a new Thread so that the call don't lock up the device if the call takes longer than expected. The AsyncTask is an easy way to use multithreading, but it lacks the functionality of repeating tasks. I would say that you are best of either using a Timer or the newer ScheduledExecutorService.
If you chose to use the Timer you create a TimerTask that you can hand it. The ScheduledExecutorService takes a Runnable instead.
You might want to wrap the thread in a Service (The Service does not provide a new Thread), but this is not always necessary depending on your needs.
As suggested in comment, you can also use the Handler.postDelayed(). Although you still need to create a new thread and then call Looper.prepare() on it:
class LooperThread extends Thread {
public Handler mHandler;
public void run() {
Looper.prepare();
mHandler = new Handler() {
public void handleMessage(Message msg) {
// process incoming messages here
}
};
Looper.loop();
}
}
(Code from Looper docs)
Also; calls to a webservice every second seems way too frequent, especially if the user is on a slow connection or there are data that needs to be transferred, try to reduce the calls as much as possible.
I think it's not only one solution, so it's up to you. You can try start thread with this run method:
private final int spleeptime = 1000;
public boolean running;
#Override
public void run() {
while (running) {
try {
int waited = 0;
while ((waited < spleeptime)) {
sleep(100);
waited += 100;
}
} catch (InterruptedException e) {
} finally {
// your code here
}
}
}