Sleep VS alarmManager.set VS alarmManager.setRepeat VS Handler - java

I'm looking for the most efficient way to perform a simple task. As a new android developer, I'm not too sure which of these strategies would be best for my app in terms of memory efficiency. I imagine some of these approaches might cause threading issues that I'm unaware of.
All three solutions are currently behaving as desired.
It's a really simple app. The idea is that my MainActivity starts an IntentService which will be running in the background after the app is opened. All the functionality I need right now is for a notification to be created at random intervals throughout the day(about an hour apart), indefinitely, until stopped by the user. The notification is made in a simple void method, displaying notification as text and vibrating the phone once.
My MainActivity starts the IntentService:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent(this, NotificationService.class);
startService(intent);
}
}
My IntentService is very simple. It is called NotificationService, extends IntentService, and only overrides the onHandleIntent method. The constructor is empty except for super("Service"). The question comes in how to cause the notifications to pop up throughout the day in the background in the most efficient way. This in my implementation is done in the onHandleIntent method for all three methods.
Method One:
#Override
protected void onHandleIntent(Intent intent) {
makeNotification();
AlarmManager alarm = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
PendingIntent pintent = PendingIntent.getService(
getApplicationContext(), 0, intent, 0);
alarm.cancel(pintent);
alarm.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime()
+ 60000 * 60, pintent);
}
Note that with this, the user would have to uninstall the app to get the notifications to stop, which is not desirable (although I think I could just add a button or something that would cancel the intent)
Method Two:
protected void onHandleIntent(Intent intent) {
makeNotification();
AlarmManager alarm = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
PendingIntent pintent = PendingIntent.getService(
getApplicationContext(), 0, intent, 0);
alarm.cancel(pintent);
alarm.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime()
+ 60*1000, 60000*60 ,pintent);
}
Method three:
#Override
protected void onHandleIntent(Intent intent) {
makeNotification();
try {
sleep(60000 * 60);
startService(intent);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Can someone please help me with deciding the pros/cons of these three methods? Im not sure I understand which one would be ideal, although they all three give me proper functionality. As a side note, in my research I've noticed a "Handler" class which may also be useful here.

All the functionality I need right now is for a notification to be created at random intervals throughout the day(about an hour apart), indefinitely, until stopped by the user.
The AlarmManager, and potentially JobScheduler, are the only viable options.
The idea is that my MainActivity starts an IntentService which will be running in the background after the app is opened
Not really. An IntentService will only be running for as long as it takes to complete onHandleIntent() (including executing it N times if N commands are sent to it in rapid succession). An IntentService can run for a little while, but it is designed to handle some sort of business logic transaction. It is not designed to run indefinitely, and doing so would be bad for the user anyway.
Can someone please help me with deciding the pros/cons of these three methods?
Option three is unusable. First, it will not be reliable, as it stops working once your process is terminated. Second, it ties up a hunk of system RAM for no good reason, RAM that the user could put to more productive use. Only have a service running when it is actively delivering value to the user. Watching the clock tick is not actively delivering value to the user.
I've noticed a "Handler" class which may also be useful here
No, as it will suffer from the same problems as option three.
With regards to your two AlarmManager options, it boils down to whether you want regularly-occurring alarms (setRepeating()) or irregularly-occurring alarms (set()).
If you go the setRepeating() option, move the AlarmManager code out of the service and into the activity. There is no point — and definite costs — to calling setRepeating() on every alarm. After all, the point behind setRepeating() is that it knows to repeat, so you do not need to tell it on every occurrence "oh, hey, I know that I told you the last 1,337 times that you should repeat, but, um, don't forget to repeat, m'kay?".
With the set() option, since you are specifically not asking for the alarms to repeat, you would continue to schedule them in the service (or perhaps once from the activity, then the rest from the service), more or less as you have it.

Related

onTaskRemoved not getting called if the activity started by the service is removed

I am using oreo 8.1.0.
I am facing a weird problem whose solution I can't find on stackoverflow, hence I am writing this question. I know one solution that through foreground service, I can implement it but I don't find notification user friendly in my application context.
I will describe my problem using two cases.
Case 1:
When user opens my app by clicking on the icon and removes it from recent apps, then service automatically restarts. This is fine.
Case 2:
Here my app is closed and is not in recent apps.
When user copies a text, then my service starts one of the activity of my app but when he removes it from the recent apps, then my service gets stopped permanently.
So my problem lies in second case,I don't want my service to get killed. Even if it gets killed I want it to restart.
I tried all the methods mentioned on the stackoverflow like using START_STICKY and onTaskRemoved but I am not able to make it work.
Even I tried killing my activity whenever user clicks on on recent app button and remove it from the recent apps programmatically but this also did not work.
Though this restarts the service even in second case when user kills my app using the back button.
This part of the code is from the activity which opens when user copies some text.
#Override
public void finish() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
super.finishAndRemoveTask();
} else {
super.finish();
}
}
This part of the code is from the service that starts the activity.
#Override
public void onTaskRemoved(Intent rootIntent){
Log.d("testing 12","onTaskRemoved");
Intent restartServiceTask = new Intent(getApplicationContext(),CBWatcherService.class);
restartServiceTask.setPackage(getPackageName());
PendingIntent restartPendingIntent =PendingIntent.getService(getApplicationContext(), 1,restartServiceTask, PendingIntent.FLAG_ONE_SHOT);
AlarmManager myAlarmService = (AlarmManager) getApplicationContext().getSystemService(this.ALARM_SERVICE);
myAlarmService.set(
AlarmManager.ELAPSED_REALTIME,
SystemClock.elapsedRealtime() + 1000,
restartPendingIntent);
super.onTaskRemoved(rootIntent);
}
#Override
public int onStartCommand(Intent intent,int flag,int startId){
super.onStartCommand(intent, flag, startId);
return START_STICKY;
}
EDIT 1
I just need the service to remain alive.Here are the things that will not hinder the user experience in my app case
1. Killing the activity by yourself programitically when onPause is called so that service does not get killed is acceptable if you make it happen
2. Showing the notification for few second is acceptable
3. Restarting the service is acceptable
From Android Oreo there is a paradigm shift to limit background service execution. It was a frequent criticism affecting battery life, performance and security. As posted in the comments above there are alternative approaches such as JobScheduler.
Refactoring services to the JobScheduler pattern can be quite involved. I advise you look into changing your IntentService to a JobintentService from the Android support v4 library. It uses JobScheduler on Oreo and later targets but reverts to the older IntentService design on older devices. You just need to replace your override of onHandleIntent with onHandleWork in your Service implementation.
Add
android:permission="android.permission.BIND_JOB_SERVICE"
to the service declaration in your AndroidManifest.xml. It can also be useful to add
<uses-permission android:name=”android.permission.WAKE_LOCK” />
But, saying all that, reading through your question, it seems to me your basic complaint is with the cosmetic issue of foreground services requiring a notification. In my view, your solution is either to simply use Context.startForegroundService, or make sure your service is bound. Either way there is visible content to the user that the service is running. You will find it an uphill struggle to try to subvert Android design decisions; better to embrace it - they are wanting you to show these notifications from now on.
Anyway updating your code for newer Android targets is still good practice even if you can't completely avoid those notification icons.
This was the problem
Whenever my service used to start the activity, my service used to get destroyed after the activity used to get started.
Why onTaskRemoved was not working?
The thing I was doing was not working because my service used to get destroyed and hence it was not active to listen to onTaskRemoved.
How I solved it?
The solution to this problem was putting a check in the activity(started by service) to check whether the service is alive or not. As in my case service was getting destroyed. Hence I need to start the service again if the service is not alive.
It even fits the OREO design pattern as we can start the background service when the app is in foreground and the service will stay alive even if the activity gets destroyed.
On Oreo though after sometime service gets destroyed but this is a different problem.
More Info

Q: Android Dev - Single Background Timer in Service plus Action After

Hello,
I've done some research, but to no avail. Also my previous question didn't work as planned.
So here is exactly what I want to do:
1.) On my MainActivity, the User enters a Numerical value in an EditText.
2.) When they press an "OK" Button, it starts a Service to run a Timer for how long they entered.
3.) When the the Timer time runs out, the Service completes some additional Actions.
Questions:
1.) Can I simply use the Button to start the Service via an Intent, then in the Service put the code?
1b.) Can't figure out exactly how to get the User input from the EditText, from within Service.
2.) Can I run the "additional Actions" when the Timer runs out from WITHIN the Service?
2b.) Or, when the Timer is up, do I need to fire a BroadcastReceiver to complete the Actions?
3.) Do I need to add anything special such as Intent-Filters in my Manifest?
Basically, I'm having trouble figuring out what code goes where.
Currently I'm using a combination of Alarm Manager, PendingIntent, and Intent.
Any help would be greatly appreciated, as I've been trying to do this correctly for nearly a month.
I may be somewhat new to programming Android, but I'm eager to learn, so thank you in advance!
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void timerButtonClick(View view) {
EditText timerValue = (EditText) findViewById(R.id.timerValue);
assert timerValue != null;
int timerInt = Integer.parseInt(timerValue.getText().toString());
AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
Intent timerIntent = new Intent(this, TimerService.class);
PendingIntent timerPendingIntent = PendingIntent
.getService(this.getApplicationContext(), 234324243, timerIntent, 0);
alarmManager.set(AlarmManager.RTC,
System.currentTimeMillis() + (timerInt * 1000), timerPendingIntent);
finish();
}
}
NOTE: Keep in mind, I'm trying to put the Timer INSIDE my Service, NOT in my Main Activity.. So I assume much of this Code needs to be moved to my Service. But again, not sure exactly how to do this.
Thanks!
1.) Yes you can (see here).
1b.) No you can not. Service has no UI interactions at all, but you can create an activity for "after actions". (Edit: But You can pass EditText data from MainActivity to Service using Intent Extras) (see starting activity from service.)
2.) You can do almost anything or "additional Actions" when the Timer runs out from within the Service, without UI. (see services tutorial)
2b.) No, you dont need that.
3.) It depends on your timer logic, intent-filters are only needed when you are receiving any broadcasts. If you write timer logic by simply taking difference between time, there is no need, but if you do it with something like ACTION_TICK logic which triggers after each minute, you do need to declare intent filter. (see declare action tick intent filter)
You will also need to declare service in AndroidManifest.xml. If your java class name is MyBackgroundService, simply put this in AndroidManifest.xml:
<service android:name=".MyBackgroundService">
</service>
Do ask me if anything remains unclear.
Update:
I myself am in learning phase and one thing I learned is to look at the official docs first. There is a class CountDownTimer in android, which gives you countdown implementation.
use onFinish() of this class to perform whatever action you want to perform.
first argument of constructor is how long you want to run countdown, second is how much time you want for tick() event to occur.
start countdown by calling .start() method.
Update:
For putting extras and start service:
Intent serviceIntent = new Intent(this, MyBackgroundService.class);
String time = editText.getText().toString();
serviceIntent.putExtra("TIME_INTERVAL", time);
startService(serviceIntent);
No need for pending intent. You can start with this:
Create an activity that has : EditText and a Button.
Create onClick button listener code, which will first check the numeric input is correct or not (number format, negative value etc.).
If the input is valid you create an Intent for Service class, add data to intent using intent.putExtra("key","numericValue")
Start service (calling start service method) with intent.
In service class receive data from intent, then do calculation in time difference code.
In service, start a while loop that will check time difference at each loop, and when desired time is elapsed start an activity that will do your action after.
Please note that service class has a bit different implementation than activity class.
If your minimum time difference is in minutes, you can use action tick logic link above to see how you implement a service that can run your logic code every minute. In your case logic is also given in above links (time difference). Also I have provided how you retrieve data from intents.
Please try to dig into some code, then comeback here if you get stuck, I (and we the SO community) will surely help, if you are still stuck, I can only write whole codes on weekends, so you will have to try yourself until then.

Timing for Android GPS logging is inaccurate

In my app I am trying to get the users current location and log it. The user can select an interval. Right now the logging is not happening consistently. Sometimes the logs are just off by a couple seconds and sometimes they are off by a few hours. Also if it is not logging and you turn the GPS off/on then it will start working again. The entire app is based around the idea that you will only be able to get your location using GPS when you use it. I have a few theories as to why this may be.
I am getting the logs by using AlarmManager setExact inside a broadcast receiver. I know that this is not guaranteed to be perfectly accurate and could possible account for a few seconds here and there.
I also know that the GPS can take some time to acquire. Is there a normal range for this time. I could see this taking up to a few minutes possibly but several hours seems like a lot.
I don't know a lot about loopers and am having some difficulty understanding them. I was wondering if the looper in the requestSingleUpdate could have anything to do with it
locationManager.requestSingleUpdate(LocationManager.GPS_PROVIDER, this, Looper.myLooper());
I know loopers process tasks in the background using a queue and I was't sure if other tasks could get stuck on the queue in front of it causing delays.
My last theory is that it has something to do with not timing out while searching for the GPS signal. If I am searching for a signal every 10 seconds (the fastest allowed) but the phone cannot find a signal the AlarmManager will fire again and I will have two services trying to get a signal. I don't really understand how the services work under the hood so I don't know if this is a possibility or not.
If anyone has any ideas/resources and could point me in the right direction I would really appreciate it.
Here is the code. I can include more if that would be helpful.
#Override
public void onReceive(Context context, Intent intent) {
SharedPreferences pref = context.getSharedPreferences(SettingsActivity.PREFERENCES, Context.MODE_PRIVATE);
if(!pref.getBoolean(SettingsActivity.ARG_TRACK, true)){
return;
}
alarmMgr = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent alarmIntent = new Intent(context.getApplicationContext(), AlarmReceiver.class);
pendingIntent = PendingIntent.getBroadcast(context, 0, alarmIntent, 0);
long interval = pref.getLong(SettingsActivity.ARG_TRACKER_INTERVAL, 15000);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
alarmMgr.setExact(AlarmManager.RTC_WAKEUP,System.currentTimeMillis()+interval, pendingIntent);
}else{
alarmMgr.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis(), interval, pendingIntent);
}
context.startService(new Intent(context.getApplicationContext(), AlarmService.class));
}
Inside my service I am calling LocationManager requestSingleUpdate()
locationManager.requestSingleUpdate(LocationManager.GPS_PROVIDER, this, Looper.myLooper());
In my app I am trying to get the users current location and log it. The user can select an interval. Right now the logging is not happening consistently. Sometimes the logs are just off by a couple seconds and sometimes they are off by a few hours. Also if it is not logging and you turn the GPS off/on then it will start working again. The entire app is based around the idea that you will only be able to get your location using GPS when you use it. I have a few theories as to why this may be.
If i'm not wroing, in your scenario, the interval is not period of location provider. It's directly period of alarm. The story like that
User selects an interval(period), for example 10 minutes.
After 10 minutes(approximately), device wakes up and makes location request to receive single location.
In that case you are facing the gps provider's lag. So, each time when wake up, you are waiting the gps provider to be warm. This is why the logs aren't consistently.
I am getting the logs by using AlarmManager setExact inside a broadcast receiver. I know that this is not guaranteed to be perfectly accurate and could possible account for a few seconds here and there.
You are using exact settings for wake up. This is not actual reason of long lag.
I also know that the GPS can take some time to acquire. Is there a normal range for this time. I could see this taking up to a few minutes possibly but several hours seems like a lot.
You have to wait until GPS provider to be warm. This warming time could change by where you stay at this moment. If you are inside a building, takes long
I don't know a lot about loopers and am having some difficulty understanding them. I was wondering if the looper in the requestSingleUpdate could have anything to do with it
Simply, when you pass a thread's looper here, onLocationChanged() method will be used the looper. But you are already making single request. No more update will be fired. (In your scenario, each wake up is one single location request)
My last theory is that it has something to do with not timing out while searching for the GPS signal. If I am searching for a signal every 10 seconds (the fastest allowed) but the phone cannot find a signal the AlarmManager will fire again and I will have two services trying to get a signal. I don't really understand how the services work under the hood so I don't know if this is a possibility or not.
This is a problem of your scenario. If you set alarm with short period, it's so normal the next wake up could happen. You can follow below things
Due to wait until gps warm, make long alarm period at least 5 minutes
Make timeout scenario, for example wait 1 minute or more until receiving location.
If cant receive location in time, shutdown everything and wait the next wakeup.

How to restrict the Android application to run only a certain time period?

I want to create an application that runs for a certain time interval.
[EDITED] By certain time interval, I mean if a user downloads an application from the play store. He can review all the features and functionality for a fixed period of time let say X hours only. After that, we can put any kind restriction (any UI/Navigation restriction so that user cannot review the features).
[The Problem]
what is the easiest as well as an efficient way of doing it? Like I don't think these solutions will work:
Calendar c = Calendar.getInstance();
int seconds = c.get(Calendar.SECOND);
Or
Time now = new Time();
now.setToNow();
What is the best way of putting restrictions (application side or server side as I have seen in some of the game application, where a synchronization is made between a server and application so that user cannot simply change the time and access the features).
So if you want to run your application for a certain period of time in the first run you need to keep a track if its a first run. So you might take set a preference variable so that it can tell you that if its a first run or not.
Now set a CountDownTimer and set it to run for an hour and when the timer is finished, close the Activity by calling finish() to it.
SharedPreferences pref = getSharedPreferences("ApplicationTag", Activity.MODE_PRIVATE);
if (pref.contains("FIRST_LAUNCH")){
finish(); // As you want, it can't be used if the application launches second time.
} else {
// Set the first launch flag to true.
pref.edit.putBoolean("FIRST_LAUNCH", true).commit();
// Start the count down timer
new CountDownTimer(3600000, 1000) {
public void onTick(long millisRemaining) {
// Do nothing
}
public void onFinish() {
// Finish the Activity
finish();
}
}.start();
So you might thinking that what if I switch between Activities. So in that case, you make create a BaseActivity class which will be extended by other Activities in your application. Place the CountDownTimer code there and take a reference of the Context you're in then call context.finish() when the timer is finished.
So in case of you don't want to limit the user for first launch, you need to save the time somehow in your application. When the Activity is finished, you'll get the callback in onDestroy function. So override the onDestroy function and set some preference again with the remaining time in mili seconds. So that you can initialize the timer again when the application starts.
Hope that helps!
Try setting an Alarm for firing any activity after any amount of time you want. Like following:
calendar = Calendar.getInstance();
Intent intent = new Intent(this, ActivityYouWantToStart.class);
PendingIntent pIntent = PendingIntent.getBroadcast(this, 101, intent, 0);
AlarmManager alarm = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
calendar.add(Calendar.Hour,1);
alarm.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pIntent);
Call this code when user launches your code for first time and inside ActivityYouWantToStart.class do whatever you want to do after such amount of time.

Using Intent Service in combination with Alarm Manager

I have used the Alarm Manager to allow the user to schedule a certain task to repeat at a certain amount of time. In the context of the application, I have an Android game where people are able to schedule when to send their ships. The Alarm Manager is working fine, alarms get kicked off at the right time etc.
What happens when an alarm is fired off (usually every hour and a bit), is that my IntentService will start communicating with the server to send the ships. This action may take 1 minute, but it can last up to 10 minutes. Even this all works fine. Alarms get fired, ships get sent, everyone happy. The problem arises at night, I go to sleep and expect when waking up that my ships have been sent all night long. Nope. Logging & notifications show that the Alarms are fired correctly, but it appears that the IntentService is killed when it's communicating with the server.
Possible cause for this is that I'm not looking at the game every once in a while like I do when I'm awake, and thus keep some form of process running which prevents the IntentService from being garbage collected.
I've already tried a LOT of things to fix this. I used to work with a BroadCastReceiver, I used to spawn ASyncTasks in the IntentService, but I've since refactored my code to not use those things anymore as they're bad practice for Services.
I have also checked this resource: http://techtej.blogspot.com.es/2011/03/android-thread-constructspart-4.html but I'm still not sure if what I'm doing is the correct thing to handle this situation. I have placed some extensive logging for the next night to review in the morning but I'd like you guys' opinion over this.
Oh I'm also requesting a WakeLock for the complete duration of the onHandleIntent function using:
PowerManager pm = (PowerManager) c.getSystemService(Context.POWER_SERVICE);
this.wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK | PowerManager.ON_AFTER_RELEASE, "MyApplicationWakeLock");
My IntentService:
public class ScheduledRequestService extends IntentService {
public ScheduledRequestService() {
super("ScheduledRequestService");
}
#Override
public int onStartCommand(Intent intent, int flags, int startID){
Logger.d("OnStartCommand!");
return super.onStartCommand(intent, flags, startID);
}
#Override
protected void onHandleIntent(Intent intent) {
ScheduledRequest request = (ScheduledRequest) intent.getExtras().getSerializable("request");
// This function will start a lot of client <-> server communication
request.onExecute(this, intent);
}
#Override
public void onDestroy(){
Logger.d("OnDestroy!");
super.onDestroy();
}
}
So again, my question is; am I structuring this correctly? Should I use a normal Service instead of an IntentService ? Can my IntentService get garbage collected while it is handling an Intent (I think I read that this is possible)?
Can my IntentService get garbage collected while it is handling an Intent (I think I read that this is possible)?
No, but your process can be terminated.
am I structuring this correctly?
Not if you are trying to use a _WAKEUP alarm. You need to set things up more carefully in that case, and I would recommend either WakefulBroadcastReceiver or my WakefulIntentService to handle that pattern.
Should I use a normal Service instead of an IntentService ?
No, an IntentService will be fine. You may need to consider making it a foreground service using startForeground().
Turns out the error was somewhere else than the garbage collector of Android. I was using cache which eventually led to me 'running out of ships' because on send time, it deducted the sent amount of ships from the pool. When they returned however, they were never added back to the cache. During the day I probably manually refreshed the cache or forced Android to clear it by using my phone otherwise which didn't cause the problem to arise (as much).

Categories

Resources