I want to make an app in which my audio profile mode change according to location. For this purpose I always need to check location in background. How can I do this in background? Where to right my actual location getting and comparison code in Service class and when to start my service class?
Below is an example of IntentService that restarts itself every 5 minutes.
public class MyIntentService extends IntentService {
int updateVal;
public MyIntentService() {
super("MyIntentService");
}
#Override
protected void onHandleIntent(Intent intent) {
// your code here. Request location updates here.
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onDestroy() {
//minutes after which the service should restart
updateVal = 5;
AlarmManager alarm = (AlarmManager) getSystemService(ALARM_SERVICE);
//This is to incorporate Doze Mode compatibility on Android M and above.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M)
alarm.setAndAllowWhileIdle(
alarm.RTC_WAKEUP,
System.currentTimeMillis() + (1000 * 60 * updateVal),
PendingIntent.getService(this, 0, new Intent(this, MyIntentService.class), 0)
);
//For all other versions.
else
alarm.set(
alarm.RTC_WAKEUP,
System.currentTimeMillis() + (1000 * 60 * updateVal),
PendingIntent.getService(this, 0, new Intent(this, MyIntentService.class), 0)
);
}
}
In your main activity, type this code to start the service.
startService(new Intent(this, MyIntentService.class));
You have to implement LocationListener and get the location updates which I haven't added into the code.
If you really wish to start a service that never stops, you need to extend Service class instead of IntentService class. It has been very well explained in the Android Developers guide: http://developer.android.com/guide/components/services.html
Use service that return "START_STICKY" in onStartCommand() function. Your service will be restarted again after it was killed by system. However sometimes , it will not be restarted. To make your service 100% alive, use foreground service. Anyway foreground service requires a notification that always shown.
Related
I have an android service to fetch data from the web that runs every fifteen minutes
public class SparkService extends Service {
Handler handler;
public SparkService() {
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.e("Starting Sevice", "Starting Service Successfully.");
if (handler == null) {
handler = new Handler(new Handler.Callback() {
#Override
public boolean handleMessage(Message msg) {
fetchDataFromServer();
handler.removeMessages(120);
handler.sendEmptyMessageDelayed(120, 15 * 60 * 1000);
return true;
}
});
}
handler.sendEmptyMessageDelayed(120, 15 * 60 * 1000);
return Service.START_STICKY;
}
}
I have found the service to be unreliable at times and seems like it's not being called if the app is inactive for a certain period of time. I want to replace the service with an AlarmManager service instead. My app is currently in production. Can I just delete the SparkService class and add another Alarm service class without affecting existing users who update the app? Or would I have to stop this SparkService in my app update so the app can function properly?
Your app is your entry point. So if it's killed that means all services related to its process will also be killed, like if you kill the svchost.exe process in Windows all sub processes like Windows update service will be stopped too and will not be running again until you launch the update manager.
The same goes for your app: the only way that a Service won't be stopped by killing your app (and I'm not sure about that but it can be) is if the Service is created with its own process using a special tag in the Manifest.
I think in your case you didn't set that tag so the Service will be only scheduled once your app is launched after the update and in that case the Service will behave according to the new code.
To answer your first question even if you delete the service from your update users with the old version will not be affected until they update there version with the new one
Now for using Alarm manger to trigger update from your backend as you said it's a good practice as the alarm manager have different set that you can use depending or your need below a short example how to use it
// Get alarm manager instance
AlarmManager alarmManager = (AlarmManager)getApplicationContext().getSystemService(Context.ALARM_SERVICE);
Calendar calendar;
Intent intent;
PendingIntent pendingIntent;
// Schedule
intent = new Intent(getApplicationContext(), YourCustomBroadcastReceiver.class);
pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0, intent, 0);
calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.add(Calendar.SECOND, 1); // first time
alarmManager.setRepeating(
AlarmManager.RTC_WAKEUP,
calendar.getTimeInMillis(),
60*5*1000,//Each five minutes
pendingIntent
);
And in your broadcast receiver
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
public class YourBroadcastReceiver extends BroadcastReceiver{
publicYourBroadcastReceiver() {}
#Override
public void onReceive(Context context, Intent intent) {
Intent serviceIntent = new Intent(context.getApplicationContext(),YourService.class);
context.startService(serviceIntent);
}
}
And here for more details about alarm manager
http://developer.android.com/training/scheduling/alarms.html
At point A in my application I start my service and expect the service get closed from point B. However, there might be few scenarios that point B doesn't ask service to get closed. In this case I want the service close itself after fixed amount of time.
I have written following code into my Service class and expect the service gets closed after 10 seconds from launch time (It will be 45min in the future but I don't want to stay that long for test).
public class ChatService extends Service implements ITCPConnection
{
private static final int SERVICE_LIFE_TIME = 10 * 1000; // In millis
private AlarmReceiver mAlarmReceiver;
private AlarmManager alarmMgr;
private PendingIntent alarmIntent;
#Override
public void onCreate()
{
super.onCreate();
//
mAlarmReceiver = new AlarmReceiver();
registerReceiver(mAlarmReceiver, new IntentFilter());
//
Intent intent = new Intent(this, AlarmReceiver.class);
alarmIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
alarmMgr = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
alarmMgr.set(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + SERVICE_LIFE_TIME, alarmIntent);
}
#Override
public void onDestroy()
{
super.onDestroy();
Log.e(TAG, "onDestroy()");
// Unregister receiver
if (mAlarmReceiver != null)
{
unregisterReceiver(mAlarmReceiver);
}
disconnect();
}
public void disconnect()
{
// If the alarm has been set, cancel it.
if (alarmMgr!= null)
{
alarmMgr.cancel(alarmIntent);
}
...
Log.e(TAG, "disconnect()");
}
/*****************
* Alarm Receiver
*****************/
private static class AlarmReceiver extends BroadcastReceiver
{
#Override
public void onReceive(Context context, Intent intent)
{
Log.e(TAG, "Stop service from AlarmReceiver");
context.stopService(intent);
}
}
}
My problem is AlarmReceiver.onReceive() never gets called and therefore my service will be alive indefinitely.
What you are trying to do is to targeting a broadcast receiver explicitly.
According to this, it cannot be done over a dinamically created (i.e. not declared into the manifest) broadcast receiver, because the os would not know how to resolve it.
To check if this is the root of the problem, you can go with the implicit way and set an action inside the intent and by filtering it in the IntentFilter.
Anyway, using the post delayed can be seen as a valid alternative, since you expect the service to be shut down naturally or still be around to intercept the delayed event.
Another (unrelated) thing is that you are calling
context.stopService(intent);
by using the broadcast intent and not the intent that started the service. You could simply call stopSelf().
I have got an intentservice class called by alarmmanager every 5 seconds. Alarmmanager calls intentservice and it works fine. But when it calls, it creates new intentservice. I just want to call intentService's onHandleIntent method not want to create new one. Here is my code:
IntentService class:
public class MyIntentService extends IntentService {
private static final String serviceName = "MyIntentService";
public MyIntentService() {
super(serviceName);
}
public void onCreate() {
super.onCreate();
Log.d("Servis", "onCreate()"); //this is called every 5 seconds too
}
#Override
protected void onHandleIntent(Intent intent) {
//do something
}
}
Setting alarmManager for IntentService
public void setAlarm(View v)
{
Calendar cal = Calendar.getInstance();
AlarmManager am =(AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
long interval = 1000 * 5;
Intent serviceIntent = new Intent(context, MyIntentService.class);
PendingIntent servicePendingIntent =
PendingIntent.getService(context, 12345, serviceIntent,PendingIntent.FLAG_CANCEL_CURRENT);
am.setRepeating(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(),interval, servicePendingIntent
);
}
I have got an intentservice class called by alarmmanager every 5 seconds.
That will not work on Android 5.1 and higher, where the minimum setRepeating() period is 60 seconds. Also, bear in mind that on Android 6.0+, Doze mode and app standby mode mean that you will not get control anywhere near that often for much of the day.
But when it calls, it creates new intentservice.
That is the point behind IntentService. An IntentService is destroyed once onHandleIntent() ends.
I just want to call intentService's onHandleIntent method not want to create new one.
Then do not use IntentService. Use Service, overriding onStartCommand() instead of onHandleIntent(), and do your own background threading logic. Be sure to stop the service (e.g., stopSelf()) when it is no longer needed.
I'm trying to make a Service, wake up and call itself again after one minute (in this example, I know its bad for battery).
Here is part of the code:
public class SpeechService extends Service {
#Override
public void onCreate() {
super.onCreate();
setUpNextAlarm();
}
public void setUpNextAlarm(){
Intent intent = new Intent(SpeechService.this, this.getClass());
PendingIntent pendingIntent = PendingIntent.getService(this, 0, intent, 0);
long currentTimeMillis = System.currentTimeMillis();
long nextUpdateTimeMillis = currentTimeMillis + 1 * DateUtils.MINUTE_IN_MILLIS;
// Schedule the alarm!
AlarmManager am = (AlarmManager)ContextManager.getApplicationContext().getSystemService(Context.ALARM_SERVICE);
am.set(AlarmManager.RTC_WAKEUP,nextUpdateTimeMillis, pendingIntent);
Log.e("test","I am back!");
}
protected void onHandleIntent(Intent intent)
{
Log.e("test","I am back!");
setUpNextAlarm();
}
}
As you can see I'm calling setUpNextAlarm on service create, I see the log at the end, but then the service is never being called again. I have tried this in an IndentService, it works but I need it to work in a normal Service :(.
Thank you
Use
PendingIntent.getService
not
PendingIntent.getBroadcast
You are getting a Broadcast Intent.
I just ended up using a Service and an IntentService. The IntentService was using the AlarmManager and then it was calling the Service.
I am working on an app that will relay information about its location to a remote server. I am intending to do it by doing a simple HTTP post to the web-server and all is simple and fine.
But according to the spec, the app needs to execute itself from time to time, lets say once in every 30 mins. Be independent of the interface, meaning which it needs to run even if the app is closed.
I looked around and found out that Android Services is what needs to be used. What could I use to implement such a system. Will the service (or other mechanism) restart when the phone restarts?
Thanks in advance.
Create a Service to send your information to your server. Presumably, you've got that under control.
Your Service should be started by an alarm triggered by the AlarmManager, where you can specify an interval. Unless you have to report your data exactly every 30 minutes, you probably want the inexact alarm so you can save some battery life.
Finally, you can register your app to get the bootup broadcast by setting up a BroadcastReceiver like so:
public class BootReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED)) {
// Register your reporting alarms here.
}
}
}
You'll need to add the following permission to your AndroidManifest.xml for that to work. Don't forget to register your alarms when you run the app normally, or they'll only be registered when the device boots up.
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
Here is a semi-different way to keep the service going forever. There is ways to kill it in code if you'd wish
Background Service:
package com.ex.ample;
import android.app.Service;
import android.content.*;
import android.os.*;
import android.widget.Toast;
public class BackgroundService extends Service {
public Context context = this;
public Handler handler = null;
public static Runnable runnable = null;
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onCreate() {
Toast.makeText(this, "Service created!", Toast.LENGTH_LONG).show();
handler = new Handler();
runnable = new Runnable() {
public void run() {
Toast.makeText(context, "Service is still running", Toast.LENGTH_LONG).show();
handler.postDelayed(runnable, 10000);
}
};
handler.postDelayed(runnable, 15000);
}
#Override
public void onDestroy() {
/* IF YOU WANT THIS SERVICE KILLED WITH THE APP THEN UNCOMMENT THE FOLLOWING LINE */
//handler.removeCallbacks(runnable);
Toast.makeText(this, "Service stopped", Toast.LENGTH_LONG).show();
}
#Override
public void onStart(Intent intent, int startid) {
Toast.makeText(this, "Service started by user.", Toast.LENGTH_LONG).show();
}
}
Here is how you start it from your main activity or wherever you wish:
startService(new Intent(this, BackgroundService.class));
onDestroy() will get called when the application gets closed or killed but the runnable just starts it right back up.
I hope this helps someone out.
The reason why some people do this is because of corporate applications where in some instances the users/employees must not be able to stop certain things :)
http://i.imgur.com/1vCnYJW.png
EDIT
Since Android O (8.0) you have to use JobManager for scheduled tasks. There is a library called Android-Job by Evernote which will make periodic background work a breeze on all Android versions. I have also made a Xamarin Binding of this library.
Then all you need to do is the following:
In your application class:
public class MyApp extends Application {
#Override
public void onCreate() {
super.onCreate();
JobManager.create(this).addJobCreator(new MyJobCreator());
}
}
Create the following two classes YourJobCreator and YourSyncJob(Where all the work will be done. Android allocates time for all the background jobs to be run. For android versions < 8.0 it will still run with an Alarm manager and background service as per normal)
public class MyJobCreator implements JobCreator {
#Override
#Nullable
public Job create(#NonNull String tag) {
switch (tag) {
case MySyncJob.TAG:
return new MySyncJob();
default:
return null;
}
}
}
public class MySyncJob extends Job {
public static final String TAG = "my_job_tag";
#Override
#NonNull
protected Result onRunJob(Params params) {
//
// run your job here
//
//
return Result.SUCCESS;
}
public static void scheduleJob() {
new JobRequest.Builder(MySyncJob.TAG)
.setExecutionWindow(30_000L, 40_000L) //Every 30 seconds for 40 seconds
.build()
.schedule();
}
}
You should schedule your service with alarm manager, first create the pending intent of service:
Intent ii = new Intent(getApplicationContext(), MyService.class);
PendingIntent pii = PendingIntent.getService(getApplicationContext(), 2222, ii,
PendingIntent.FLAG_CANCEL_CURRENT);
Then schedule it using alarm manager:
//getting current time and add 5 seconds to it
Calendar cal = Calendar.getInstance();
cal.add(Calendar.SECOND, 5);
//registering our pending intent with alarmmanager
AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
am.set(AlarmManager.RTC_WAKEUP,cal.getTimeInMillis(), pi);
this will launch your service after 5 seconds of current time. You can make your alarm repeating.
You can use Alarm Manager to start Service at specified time and then repeat alarm in specified interval. When alarm goes on you can start service and connect to server and make what you want