I have a Service that uses a custom Connection class (extends thread) to a hardware controller. When the User prefers, I wish to maintain this connection on a permanent basis. I already have the code to handle when the Android device loses its internet connection, switches between wi-fi, etc.
In order to stay connected, the controller requires that you speak to it within every 5 minutes. I currently, within the Connection class start a thread that runs in a while(), and checks the system time and the last time it communicated, and when > 4 minutes it requests a status. For some reason, at different times the communication doesn't occur in time. i.e., occurs after 5 minutes. The Service doesn't die, as far as I can tell but the "Ping" to the controller is late. This doesn't happen when I have the phone plugged into the charger (or debugger). Additionally, the behavior is the same when I move the Service to the foreground.
Does the phone slow down it's processor when it goes to sleep?
Is there a better way?
I'm thinking it's the AlarmManger, but I'm having trouble getting it to work with an inner-class, within the Service. I tried using the API demos as a starting point, but I can't seem to figure out how to get the Broadcast receiver registered. I am trying to register the receiver programmatically, with no changes to the manifest.
public class DeviceConnectionService extends Service {
#Override
public void onCreate() {
Intent intent = new Intent(this, PingConnection.class);
intent.setAction("KEEP_CONNECTION_ALIVE");
PendingIntent sender = PendingIntent.getBroadcast(this,
0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
// We want the alarm to go off 30 seconds from now.
long firstTime = SystemClock.elapsedRealtime();
firstTime += 15*1000;
// Schedule the alarm!
AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
am.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
firstTime, 15*1000, sender);
// register to listen to the Alarm Manager
if (mPingConnectionReceiver == null) {
mPingConnectionReceiver = new PingConnection();
getApplicationContext().registerReceiver(mPingConnectionReceiver,
new IntentFilter("KEEP_CONNECTION_ALIVE"));
}
}
// ...
public class PingConnection extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if (dBug) Log.i("PingConnection", "Pinging Controller");
// do real work here
}
}
}
Does the phone slow down it's processor when it goes to sleep?
The phone shuts down its processor when it goes to sleep. That is the definition of "sleep".
I'm thinking it's the AlarmManger, but I'm having trouble getting it to work with an inner-class, within the Service. I tried using the API demos as a starting point, but I can't seem to figure out how to get the Broadcast receiver registered. I am trying to register the receiver programatically, with no changes to the manifest.
That is an unusual approach for AlarmManager. That being said, since you declined to describe "having trouble" in any detail, it is difficult to help you.
Get rid of getApplicationContext() (you don't need it and really don't want it in this case). I would register the receiver before touching AlarmManager. Before you go to production, please choose an action name that has your package name in it (e.g., com.something.myapp.KEEP_CONNECTION_ALIVE).
Beyond that, check LogCat for warnings.
UPDATE
In your LogCat, you should have a warning from AlarmManager complaining about not being able to talk to your BroadcastReceiver.
Replace:
Intent intent = new Intent(this, PingConnection.class);
intent.setAction("KEEP_CONNECTION_ALIVE");
with:
Intent intent = new Intent("KEEP_CONNECTION_ALIVE");
and you may have better luck.
you can't register AlarmManager in a Service.
All you can do is declare it as global in the Manifest.xml.
You can start the alarm from service in this way, by declaring it in Manifest.xml
If you have a remote service and you close the launcher activity, the AlarmManager will still run, but don't forget to stop it on onDestroy() method of the service.
I've tried to register only in the Service the AlarmManager as I didn't used it for the main activity, but no success!
It didn't work as registering as a normal BroadCastReceiver.
that's how the things are, you have to declare it in Manifest.xml as global
I know it's late, but maybe it's useful for someone else.
You can register it, the problem is when the Intent tries to call it.
Instead of calling it like this:
Intent intent = new Intent(this, PingConnection.class);
Create an empty intent and add an action you are going to listen to:
Intent intent = new Intent();
intent.setAction("value you want to register");
Then create the pending intent and send the broadcast like you have it.
Create an attribute for the receiver so you can access it in the whole class and unregister if necessary (if the pendingIntent is also an attribute you can unregister any time):
private PingConnection pingConnection = new PingConnection();
Register it like this:
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("the value you used before");
getApplicationContext().registerReceiver(pingConnection, filter);
Now you won't get any errors, and the class is not static, and it's an inner class.
Related
Update
I gave up using ACTION_TIME_TICK because when click the physical button of a locked phone, it will also sent ACTION_TIME_TICK even the phone is still locked. That may make trouble if I keep pressing the key to wake up the phone.
Edit
The original purpose of this question:
Update widget text every 60s(not TextClock)?
Can I use ACTION_TIME_TICK to update android time widget?
I find an app called KWGT(Kustom Widget) and seems like using background?foreground service to update every minute. Its service(shows notification, so I' m sure it is service) lives longer than mine(alarm + foreground service).
How do KWGT do this? Its help site says: Kustom was using close to 0 resources when it was allowed to ignore battery optimization (because targeting old releases) and new version acts like the same.Does it mean alarm using AlarmManager.RTC mode?
I have an Android AppWidget that shows HH:mm, update every 60 seconds, it works but still has some problem. Here is what I’ ve got:
I’ ve tried TextClock, it seems good, but I still need to update some other text when time ticks, like “count down: 3 min”、“count down: 2 min”.
I use the AlarmManager, send broadcast every minute to update my widget. I ‘m a little worry about my device’ s battery. Another problem is, when I change my device’ s time, TextClock get the new time immediately, but my widget text not.
I know how TextClock do it: intent ACTION_TIME_TICK, ACTION_TIME_CHANGED, ACTION_TIMEZONE_CHANGED sent by the system. The annotation says: “You cannot receive this through components declared in manifests, only by explicitly registering for it with Context.registerReceiver()”.
So, should I register that receiver in a service? I think it will not work very long because my process can not run forever. How those system time widgets do this? What' s the best way in 2021? My minSdk is API 26, if there is some better way after it, I can change to higher version.
My Alarm:
fun startAlarm() {
val calendar: Calendar = Calendar.getInstance()
calendar.set(Calendar.MILLISECOND, 0)
calendar.set(Calendar.SECOND, 0)
calendar.add(Calendar.MINUTE, 1)
val alarmIntent = Intent(context, MyWidget::class.java)
alarmIntent.action = MyWidget.ACTION_AUTO_UPDATE
val pendingIntent = PendingIntent.getBroadcast(
context,
ALARM_ID,
alarmIntent,
PendingIntent.FLAG_CANCEL_CURRENT
)
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
// RTC does not wake the device up
alarmManager.setRepeating(
AlarmManager.RTC,
calendar.timeInMillis,
INTERVAL_MILLIS.toLong(), // Value will be forced up to 60000 as of Android 5.1
pendingIntent
)
}
fun registerReceiver says:
Note: this method cannot be called from a BroadcastReceiver component; that is, from a BroadcastReceiver that is declared in an application's manifest. It is okay, however, to call this method from another BroadcastReceiver that has itself been registered at run time with registerReceiver, since the lifetime of such a registered BroadcastReceiver is tied to the object that registered it
What does this mean? Can I regeister it in class MyWidget : AppWidgetProvider()? What is the correct context?
Here is the receiver register in TextClock:
// OK, this is gross but needed. This class is supported by the
// remote views mechanism and as a part of that the remote views
// can be inflated by a context for another user without the app
// having interact users permission - just for loading resources.
// For example, when adding widgets from a managed profile to the
// home screen. Therefore, we register the receiver as the user
// the app is running as not the one the context is for.
getContext().registerReceiverAsUser(
mIntentReceiver,
android.os.Process.myUserHandle(),
filter,
null,
getHandler()
);
And registerReceiverAsUser is a function with #UnsupportedAppUsage so I think it can not help me.
Thanks a lot.
The best approach is to use TextClock or AnalogClock if you can. These update each minute (or second for AnalogClock on Android 12) without your application's process needing to run.
ACTION_TIME_TICK is only sent to registered receivers, which means you won't receive it when your application isn't running. The only plausible way to keep your app running would be to use a foreground service, requiring showing a notification to the user.
You can override listed method in the class which extends from AppWidgetProvider like this:
#Override
public void onReceive(Context context, Intent intent)
{
super.onReceive(context, intent);
Log.d("TAG", "mythou--------->onReceive Reason: " + intent.getAction());
}
#Override
public void onEnabled(Context context) {
// Enter relevant functionality for when the first widget is created
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_TIME_TICK);
filter.addAction(Intent.ACTION_TIME_CHANGED);
context.getApplicationContext().registerReceiver(this, filter);
}
I have to develop a simple application that every 5 seconds fetch a json from a server. I know that this will drain the battery but since I don't have control on the server, this is the only solution I have at the moment, so stay with me. (It's a demo app, the final release will be totally different)
What I did:
I created an IntentService and in his onHandleIntent I do the http request and manage the response asynchronously. Reading the docs, the IntentService is preferred to the Service. At the end, as seen in other topics, I use the AlarmManager class to recreate the intentService. That's the method:
private void repeat() {
lastTrigger = System.currentTimeMillis();
alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
Intent serviceIntent = new Intent(this, StatusPollingService.class);
PendingIntent pendingIntent = PendingIntent.getService(this, 0, serviceIntent, PendingIntent.FLAG_UPDATE_CURRENT);
alarmManager.set(AlarmManager.RTC_WAKEUP, lastTrigger + UPDATE_INTERVAL, pendingIntent);
}
In my onCreate in my first Activity, I simply start the service:
serviceIntent = new Intent(BaseActivity.this, StatusPollingService.class);
startService(serviceIntent);
My issues:
When I close my application (using back button), the intentService continues to do the requests and recreates itself (as expected), so I have to kill it manually
I'd like to stop to do polling every time my app goes in background and restart when goes in foreground, to limit the battery drain
My other issues:
I implemented a class that triggers a listener when the app goes in background/foreground. I implemented the interface in my BaseActivity and in my IntentService. When the app goes in background, a boolean became false and I don't execute the repeat() method. When I go in foreground, in my method onBecameForeground I simply create the service:
serviceIntent = new Intent(BaseActivity.this, StatusPollingService.class);
#Override
public void onBecameForeground() {
startService(serviceIntent);
}
Going in background there's no problem, but going in foreground multiple intentService are created, one intentService per every Activity I have in that moment (I see it in the log).
My questions:
Is this the best way to do this job? How could I resolve these problems?
If this is the best (or the less bad) way, how can I create a single instance of the IntentService?
Thank you so much for your help
If you only need it in the foreground, then I'd not bother with the alarms. Just post intents every 5 seconds to your intent service using Handler.
Supposedly there should be only one instance of the intent service, so if you post multiple intents for processing, they will get queued and handleIntent will be called in your intent service for each intent. Note that, however, that if your service is processing intents fast enough, then it may finish (and destroyed) before you post another intent - so you'll see multiple instances of the service being created. But there will only be one instance at a time.
Edit: to expand a bit, you will need the alarms when (and if) you'll be polling the server in background, so don't through that code away ;) Handler will keep "freezing up" in background as the OS doesn't count the time while the device was sleeping for the Handler postponed execution.
i'm creating an alarm application, and this is the method to run the alarm :
public void startAlarm(int minuteToStart)
{
Toast.makeText(context, "Alarm Start in " + formatTime(minuteToStart), Toast.LENGTH_SHORT).show();
Calendar cal = Calendar.getInstance();
cal.add(Calendar.MINUTE, minuteToStart);
Intent intent = new Intent(context, AlarmActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(context, idPendingIntent, intent, PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager alarmManager = (AlarmManager)context.getSystemService(ALARM_SERVICE);
alarmManager.set(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), pendingIntent);
}
And it run this activity after given specific time:
public class AlarmActivity extends Activity {
......
}
It works, but i see people are using BroadcastReceiver, am i doing it wrong? should i use BroadcastReceiver too? I've been searching about BroadcastReceiver but i don't get what difference it will make with my application.
Thanks.
In the general case, A--C's answer would be correct.
However, you are using RTC_WAKEUP as the alarm type. The only guarantee that we have with _WAKEUP alarms is if we use a BroadcastReceiver, then Android will ensure that the device will stay awake long enough for us to execute onReceive(). Any other type of PendingIntent -- activity or service -- has no guarantee, and it is very possible for the device to fall back asleep before the startActivity() or startService() actually occurs.
You can use AlarmManager with whatever PendingIntent is capable of (Activity, service, Receiver), though, it is usually used with Receivers - taks executing in the future usually are small and don't need an Activity to run in since the user doesn't need something popping up.
A Receiver isn't an Activity, so it does not have a UI and it has a processing time limit of about 10 seconds, so make sure to be quick. If you require a UI to be shown at a specific time, stick with an Activity, but usually this isn't the case unless it's something like an Alarm Clock app that the user has to see). If you have something like a small behind the scenes operation, go for a Receiver. The Receiver's onReceive() gets a Context passed to it so it can do anything a Context can.
Just keep in mind you will have to change the PendingIntent.getActivity() call to whatever else you decide to use if it's not going to be an Activity.
So it all depends on what you want to do.
You don't have to use a BroadcastReceiver. It's just generally frowned upon (in most cases) to steal focus and launch an Activity from the background without user interaction. There are certainly valid use cases though. If you intend to launch an Activity immediately anyway, doing that directly instead of via BroadcastReceiver is perfectly valid.
I have a question regarding Android widgets and especially Android services. Here is what I want to do: On the phone the user should be able to add my widget to his homescreen. The widget gets its data from network.
After reading some tutrials about this, I found out that the best approach would be to create a service that gets the data from network (TCP sockets) and then updates the widget constantly. That is done with ScheduledExecutorService which executes a Runnable in a certain interval.
The Runnable will connect to a server, get the data and should update the widget).
My problem is now, that the widget don't need to be updated when the screen is off and so the service should not run because of battery drain. My question is: How to do it the best way?
I found 2 approaches which would more or less do what I want:
When the first instance of the widget is added to homescreen, it will register a Broadcast Receiver that receives the ACTION_SCREEN_ON and ACTION_SCREEN_OFF intent action from Android OS.
If ACTION_SCREEN_ON is fired, it will start the updater service, otherwise it will stop it. But I'm really unsure if that's a good approach because of the broadcast receiver lifetime.
In the updater service's Runnable, which is executed periodically by the ScheduledExecutorService and actually does the network stuff, I check via PowerManager.isScreenOn(), if the screen is on. If yes, I execute the network code, otherwise not. But what is when the device is in standby? Is this code executed then? What about battery drain here?
Is there maybe a "best practice" approach for what I want to do? I've read alot about the AlarmManager and that it is a very powerful tool. Can you schedule tasks with this in the way, that they are only executed when the display is on?
Thanks for your help.
Best Regards
NiThDi
Your first solution (widget handling screen ON and OFF broadcasts) to the problem is the way to go. Start a new IntentService for a background communication to your application server. Once it finished, send your custom "Completed" broadcast with results and then handle it in your widget.
As an additional benefit it would allow multiple instances of your widget to work from one "source" and would not consume any resources in case user did not add any widgets.
UPDATE As it is impossible to use screen ON/OFF broadcasts with a widget, I would probably use this (AlarmManager with an ELAPSED_REALTIME (not ELAPSED_REALTIME_WAKEUP) alarm) to schedule an (intent) service run. This way your service will not be scheduled when screen is off, but will run periodically when it is on. And if the delay between screen ON and OFF is more than the period it will run immediately on screen coming back ON.
As for your question about broadcast receivers lifetime, as said before, use IntentService for a long running task and then broadcast your results back from it. When you starting a service that call does not block so broadcast receiver will finish in a timely matter.
UPDATE 2 According to this screen OFF does not mean device is sleeping. You already have a solution to this issue by using PowerManager.isScreenOn().
The ELAPSED_REALTIME approach could work I think, but strangly it is not for a small test app I created.
Basically the test app is a widget and a IntentService. The widget only shows the current time, while the Intent Service gets the current time and sends a broadcast which the widget consumes in the onReceive() method and updates itself. The widget is of course a registered receiver for the custom broadcast action.
In the onEnabled() method in the widget, I initialize the alarm for AlarmManager. Now some code:
Widget onEnabled():
#Override
public void onEnabled(Context c) {
super.onEnabled(c);
Intent intent = new Intent(c.getApplicationContext(), SimpleIntentService.class);
PendingIntent intentExecuted = PendingIntent.getService(c.getApplicationContext(), 45451894, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarmManager = (AlarmManager) c.getApplicationContext().getSystemService(Context.ALARM_SERVICE);
alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME, 0, 3000, intentExecuted);
}
Widget onReceive():
#Override
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);
if (intent.getAction().equals(ACTION_RESP)) {
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
ComponentName thisWidget = new ComponentName(context.getApplicationContext(), Widget.class);
int[] allWidgetIds = appWidgetManager.getAppWidgetIds(thisWidget);
for (int appWidgetId : allWidgetIds) {
RemoteViews remoteViews = new RemoteViews(context.getApplicationContext().getPackageName(), R.layout.widget);
String s = "";
if (intent.hasExtra("msg")) s = intent.getStringExtra("msg");
// Set the text
remoteViews.setTextViewText(R.id.textView1, s);
appWidgetManager.updateAppWidget(allWidgetIds, remoteViews);
}
}
}
SimpleIntentService onHandleIntent():
#Override
protected void onHandleIntent(Intent intent) {
Log.w("TestService", "SimpleIntentService onHandleIntent called.");
String msg = new Date().toGMTString();
Intent broadcastIntent = new Intent();
broadcastIntent.setAction(Widget.ACTION_RESP);
broadcastIntent.addCategory(Intent.CATEGORY_DEFAULT);
broadcastIntent.putExtra("msg", msg);
sendBroadcast(broadcastIntent);
}
So, I have tested this on my Nexus 4 running Android 4.2.1 and on an emulator with Android 4.0.4. In both cases the SimpleIntentService gets created and onHandleIntent() gets called every 3 seconds, even when I manually turn the display off.
I have absolutely no idea why the AlarmManager still schedules the alarm, do you have?!
Thank you!
I have a service which sends notifications every 2 minutes..
When the phone gets locked no new notifications are received and only when I unlock my phone I receive notifications )
Whether there is any way to change it..?
Best solution for running some task every 2 minutes is AlarmManager And do not use wakelocks except you don't need to hold device in work mode (like mp3 player for example), because it will spend battery.
UPDATE FOR ENYONE WHO TO LAZY FOR READING MANUAL
For using AlarmManager you need broadcast receiver at first.
public class ExampleReceiver extends BroadcastReceiver
{
public void onReceive(Context context, Intent intent)
{
// this method will be called by AlarmManager. But be carefull it has timeout
// if your task need more time, you should run thread from there with wakelocks
}
}
Then you can set task to AlarmManager.
public class ExampleActivity extends Activity
{
#Override
public void onCreate(Bundle savedInstanceState)
{
Intent intent = new Intent(this,ExampleReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
AlarmManager am = (AlarmManager)getSystemService(ALARM_SERVICE);
am.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,SystemClock.elapsedRealtime()+INTERVAL,pendingIntent);
}
}
This example run task once after INTERVAL. And it will wakeup phone for this task. You can set repeating tasks due AlarmManager, or it possible better to set new task just from receiver
When phone is locked, the device goes to deep-sleep mode and it will not handle any wait/sleep timer related delays.
Try using AlarmManager instead for getting notification every 2 mins. Or may be hold WAKE LOCKS (though might result in battery drain and not recommended)
You might be able to accomplish your goal using the PowerManager API:
http://developer.android.com/reference/android/os/PowerManager.html
You can use this to wake the phone up briefly to handle your notification. Make sure you're familiar with how the PowerManager API works, though - it's easy to write your code in a way that drains the battery.