In my app I need to update an object if the user didn't refresh it for 30 days. I tried several things to approach this, but none of them worked
1. Using System.currentTimeMillies()
My first try was to just store the time when the object was updated and compare it to the current time. Everything worked fine, but the problem was, that the user could change the os time and then the time check would be useless...
2. Using AlarmManager
Here I had the same problem as above
3. Implementing a Service that contains a timer
Here I implemented a Service with a Timer that just counts up to 30 days. This seemed to be the best solution, but the Service stops when I close the app.
the onCreate and onStartCommand of my Service look like this (I just changed 30 days to 2 minutes for testing and it contains multiple timers for multiple objects):
#Override
public void onCreate() {
Log.i(TAG, "[onCreate]");
super.onCreate();
registerReceiver(new StopServiceReceiver(), new IntentFilter(STOP_SERVICE_REQUEST));
context = this;
//retrieveTimers();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent == null) {
Log.i(TAG, "[onStartCommand] intent = null");
return super.onStartCommand(intent, flags, startId);
}
//prevent starting 2 timers for the same id
String id = intent.getStringExtra(KEY_CARD_ID);
if (timerHashMap.containsKey(id)) {
Log.i(TAG, "[onStartCommand] timer already exists");
return super.onStartCommand(intent, flags, startId);
}
//create and start timer
Log.i(TAG, "[onStartCommand] schedule timer");
Timer timer = new Timer();
timer.schedule(new CounterTask(id), MAX_MILLIES_WITHOUT_UPDATE);
timerHashMap.put(id, timer);
// storeTimers();
return super.onStartCommand(intent, flags, startId);
}
I also tried to store the timer map in the shared preferences but later on I realized that thsi was kind of stupid because the timers should continue and serializing a timer to store it was also not possible (and as I said kind of stupid ;-))
Thanks for your help!
You can use JobScheduler (or some variations of it).
There are Minimum Latency and Periodic options to schedule the Job to run after 30 days.
Refer this article for more info: Scheduling jobs like a pro with JobScheduler
There is JobIntentService in the latest Support Library and Firebase JobDispatcher for backward compatibility.
Related
I have an android app and a server application written in Java. I basically want the app to connect to the server every few seconds to get the newest information, and if neccessary display a push notification, like a Messenger App. I'm new to this, and I've tried multiple ways, but nothing of what I tried seems to work.
I've used a Service which connects to the server every X seconds and gets the newest information from it. The service restarts when It gets destroyed, so it even runs in the background when the app is terminated, but after a while it just stops working and doesn't restart with the error message Not allowed to start service Intent {snip}: app is in background. I have no idea if this approach is even a good idea, and I've tried some other things too, but I don't get anywhere, so any advice on how an application like this should be done would be really helpful!
This is my Service class:
public int counter=0;
public ConnectionService(Context applicationContext) {
super();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
startTimer();
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
Intent broadcastIntent = new Intent(this, BootBroadcastReceiver.class);
sendBroadcast(broadcastIntent);
stoptimertask();
}
private Timer timer;
private TimerTask timerTask;
public void startTimer() {
timer = new Timer();
initializeTimerTask();
timer.schedule(timerTask, 1000, 1000); //
}
public void initializeTimerTask() {
timerTask = new TimerTask() {
public void run() {
counter++;
ConnectionManager.oneWayCall(new DebugPacket("Debug Packet: " + counter));
}
};
}
I don't neccessarily need help with this exact code, if apps like these should be done in a completely different way, please point me into the right direction.
now you can not run services in background forever . system will terminate it after sometime even if you use foreground service.
instead of fetching data every x seconds i would recommend ask your backend guy to send data in fcm notification if data is not large.
if you can wait for 15 minutes for fetching new data you can use workmanager.
there is one ugly way of keeping services alive for longer time i will suggest not to use it .
you can start service every x seconds when you receive notification your app is considered in foreground when you receive notification in this window you can start service. catch is you have to send notifications every x seconds.
I have two Application A and B.In app B I have a service that I can run it from app A. I want to send data to app B with intent but always my intent is null!
I run app B's service from app A with this Code:
try {
String packageName = "app_B_package";
String appService = packageName + ".activity.InternetService";
Intent start = new Intent();
start.setComponent(new ComponentName(packageName, appService));
start.putExtra("LAUNCHER_COMMAND_CLOSE" , true);
G.context.startService(start);
} catch (Exception e) {
e.printStackTrace();
}
But when service of app B will run the intent is null. This is onStart of the service in app B:
#Override
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
Log.i("LOGO_OFFICE_IN", "onStart");
if (intent != null) {
if (intent.getExtras().getBoolean("LAUNCHER_COMMAND_CLOSE")) {
Tools.clearApplicationData(InternetService.this);
new AppStatus(InternetService.this).isAppRunning(getPackageName(), true);
}
}
}
Why my intent is null all the time? I can't find it out.
Thank you for your help.
It looks like your service is type fire-and-forget - it does one quick thing and should quit immediately because it's done. Correct?
1. Don't leave your idle service running
Documentation says
If a component starts the service by calling startService() (which results in a call to onStartCommand()), the service continues to run until it stops itself with stopSelf() or another component stops it by calling stopService().
so after your workload is done call stopSelf().
When your service is not running there's nothing to restart.
2. Use correct start mode
Unless you stop it, your service is by default automatically restarted after it's killed by system (because system needed resources). The default mode is called START_STICKY and does this:
This mode makes sense for things that will be explicitly started and stopped to run for arbitrary periods of time, such as a service performing background music playback.
Since your service is a quick one-time job, it makes no sense for it do be restarted later at an arbitrary time.
To let Android know, you should return START_NOT_STICKY from onStartCommand.
3. Use current API
Don't use onStart, it was deprecated 9 years ago. It doesn't support start modes mentioned above. Implement onStartCommand instead. Your service would look like this:
#Override
public void onStartCommand(Intent intent, int flags, int startId) {
// No super call.
Log.i("LOGO_OFFICE_IN", "onStart");
// Intent cannot be null.
if (intent.getExtras().getBoolean("LAUNCHER_COMMAND_CLOSE")) {
Tools.clearApplicationData(InternetService.this);
new AppStatus(InternetService.this).isAppRunning(getPackageName(), true);
}
stopSelf(); // Work is done, stop service.
return START_NOT_STICKY; // Don't restart if killed.
}
Now that I think of it, only step 1 is absolutely necessary. Anyway, get into habit of using current APIs and finding out how things work.
I have a service that have an variable life time. It may execute from 5 minutes to 2 hours (for example). So I'm looking for the best approach to do that, and my service must achieve the following features:
Send (to my server) lat-long every 5 seconds and some extra information (string's, boolean's and int's)
I have tried a "normal" service and tried to do something like this to achieve this:
public class MyFiveSecondsService extends Service {
private Handler handler;
Runnable r = new Runnable() {
#Override
public void run() {
//here send my new data
}
};
public void onCreate(){
super.onCreate();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if(handler == null){
handler = new Handler();
}
handler.post(r);
return super.onStartCommand(intent, flags, startId);
}
}
Actually that code works, but I got some performance problems with that approach, so I tried to do something like this:
public class SendUniquePositionIntentService extends IntentService {
public SendUniquePositionIntentService() {
super("co.bomboapp.Service.IntentService.SendUniquePositionIntentService");
}
#Override
public void onCreate() {
super.onCreate();
}
#Override
protected void onHandleIntent(Intent intent) {
//do the logic here
}
}
public class MyFiveSecondsService extends Service {
private Handler handler;
Runnable r = new Runnable() {
#Override
public void run() {
//call my SendUniquePositionIntentService here
}
};
public void onCreate(){
super.onCreate();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if(handler == null){
handler = new Handler();
}
handler.post(r);
return super.onStartCommand(intent, flags, startId);
}
}
And that approach haven't worked, when I had closed the app any service kept running. So before start any other attempt to achieve this, I want some direction, what's the best approach to do this "infinite loop service" and keep the performance?
I'm using Android 4.1 as min API, and targeting 5.0 API.
My test device is a Nexus 5 running Android 6.
Right now I'm using parse.com as database.
"I have tried a "normal" service...but I got some performance problems"
By default, a service runs on the application's main thread, so when you create a handler with
public int onStartCommand(Intent intent, int flags, int startId) {
if(handler == null){
handler = new Handler();
}
...
}
the handler is associated with the main thread's Looper and all messages and runnables are delivered and later executed on the thread. That's the reason of the "performance problems". From the documentation:
Remember that if you do use a service, it still runs in your application's main thread by default...
Regarding the second approach and the part
"...when I had closed the app any service kept running"
you haven't mentioned how exactly you "close" the app, but what I can see is
public int onStartCommand(Intent intent, int flags, int startId) {
...
return super.onStartCommand(intent, flags, startId);
}
which means that if the system kills the service it, by default, will be recreated. So if "closing" your app means killing it, the following chain of actions takes place:
The system recreates MyFiveSecondsService,
onStartCommand() is called and the handler posts the runnable
within the run() method SendUniquePositionIntentService is started
From the documentation of onStartCommand():
the default implementation calls onStart(Intent, int) and returns either START_STICKY or START_STICKY_COMPATIBILITY.
Note that starting a service from another one (like starting SendUniquePositionIntentService from MyFiveSecondsService in your case) is redundant unless you intended to.
The final part of your question is confusing to me. On one hand it doesn't work for you because "...any service kept running" but, on the other hand, you'd like "do this "infinite loop service""...?
If you only need to send such information as "strings, booleans and ints" to a server (without any feedback to the component that started the service), I suppose it's simply enough for you to use IntentService. This is a "out-of-box" framework that does its work on a background thread (letting you avoid freezing the main thread) and stops itself once it's done. As an example you can use the documentation on IntentService - it's well written.
Also note that the behaviour of a service after killing it by the system depends on the flag returned by onStartCommand(). E.g. use START_NOT_STICKY to not recreate the service after killing the app or START_REDELIVER_INTENT to recreate it with the last Intent redelivered.
I am building an application using PhoneGap, which revolves around a timer I have created. I am struggling at the moment as I need a way of having the app open itself if the timer reaches zero. I have currently put in place a notification for when the timer runs out, and the user can launch the app from there. However I need a way of launching the app if the user "misses" the notification or something similar.
For example, when the timer on the local "timer" app on a mobile device runs out, it will open itself to notify the user that the time has ran out.
Any suggestion would be appreciated,
Thanks.
Just write the code for opening the launcher Activity instead of showing notification in your service class.When the timer runs out the launcher activity will start instead of notification.
the code will look similar to this:
public class AlarmService extends Service {
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
//calling Launcher Activity
Intent alarmIntent = new Intent(getBaseContext(), AlarmScreen.class);
alarmIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
alarmIntent.putExtras(intent);
getApplication().startActivity(LauncherActivity.class);
AlarmManagerHelper.setAlarms(this);
return super.onStartCommand(intent, flags, startId);
}
}
I hope you are satisfied with this answer.
The title may seem duplicate but the question not about how to make the request, im sending a HTTP Get request from my android application to a web server after a specified interval using a service, the problem is it is stopped after i perform any other action on the device like play a video. The service looks like
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
final Context ctx=this;
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
//perform GET here
}, 0, 5000);
return Service.START_STICKY;
}
any idea why such behaviour im experiencing even though im returning the Service.START_STICKY
Regards.
As mentioned in comment you can take the approach of PendingIntents and BroadcastReceiver in which you can leave a pending intent at specified time and register a receiver and in onreceive you can perform you operation whether to start service or hit a werbservice.
Please go through http://code.tutsplus.com/tutorials/android-fundamentals-scheduling-recurring-tasks--mobile-5788
also http://www.sitepoint.com/scheduling-background-tasks-android/ for better understanding
Try running your service in the 'foreground'. This way it is less likely to get killed.
Check out: http://developer.android.com/guide/components/services.html#Foreground