ForegroundService on android Oreo gets killed - android

I'm trying to build up a service which requests the device location every minute.
I need this to work in the background even when the application is closed. So far I managed to make it work on devices which have a pre-Oreo android OS but now I'm testing the service on android Oreo device and is not working when I close or put the application in background.
In my research I found that for Oreo devices, a Foreground Service with an ongoing notification should be used to achieve this so to start with I've implemented a simple Foreground Service like the below which shows an ongoing notification while the app is started and is removed when the app is stopped.
public class MyForegroundService extends Service {
private static String TAG = MyForegroundService.class.getSimpleName();
private static final String CHANNEL_ID = "channel_01";
private static final int NOTIFICATION_ID = 12345678;
private NotificationManager mNotificationManager;
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
public MyForegroundService() {
super();
}
#Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate");
mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
// Android O requires a Notification Channel.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
CharSequence name = getString(R.string.app_name);
// Create the channel for the notification
NotificationChannel mChannel = new NotificationChannel(CHANNEL_ID, name, NotificationManager.IMPORTANCE_DEFAULT);
// Set the Notification Channel for the Notification Manager.
mNotificationManager.createNotificationChannel(mChannel);
}
startForeground(NOTIFICATION_ID, getNotification());
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand");
return super.onStartCommand(intent, flags, startId);
}
#Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "onDestroy");
stopForeground(true);
}
private Notification getNotification() {
// Get the application name from the Settings
String appName = PrefApp.getSettings(getApplicationContext()).getAppConfigs().getAppName();
String applicationKey = PrefApp.getSettings(getApplicationContext()).getAppConfigs().getAppKey();
NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
.setContentTitle(appName)
.setContentText("Services are running")
.setOngoing(true)
.setPriority(Notification.PRIORITY_HIGH)
.setSmallIcon(R.mipmap.ic_notification)
.setWhen(System.currentTimeMillis());
// Set the Channel ID for Android O.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
builder.setChannelId(CHANNEL_ID); // Channel ID
}
return builder.build();
}
}
I am starting and stopping the above service using the below functions.
public void startMyForegroundService() {
Log.d(TAG, "Start Foreground Service");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(new Intent(getApplicationContext(), MyForegroundService.class));
} else {
startService(new Intent(getApplicationContext(), MyForegroundService.class));
}
}
public void stopMyForegroundService() {
Log.d(TAG, "Stop Foreground Service");
stopService(new Intent(getApplicationContext(), MyForegroundService.class));
}
I'm testing the above service and for some reason the service gets killed after about 30 mins from when I start it. Can anyone tell me if I'm doing something wrong or possibly guide me for a solution which can work for me?
Note: I've followed this tutorial and tested their application as well and that it still not working. The service is being killed after some time.
Basically my goal is to implement a service which can run in the background (even when the application is closed) and get location updates every minutes.

You can use firebase job dispatcher for background service.
Code:
Add this dependencies:
implementation 'com.firebase:firebase-jobdispatcher:0.8.5'
public class MyJobService extends JobService
{
private static final String TAG = MyJobService.class.getSimpleName();
#Override
public boolean onStartJob(JobParameters job)
{
Log.e(TAG, "onStartJob: my job service class is called.");
// enter the task you want to perform.
return false;
}
#Override
public boolean onStopJob(JobParameters job)
{
return false;
}
}
Create a job in the activity, you do it the way you used to do for background services.
/**
* 2018 September 27 - Thursday - 06:36 PM
* create job method
*
* this method will create job
**/
private static Job createJob(FirebaseJobDispatcher dispatcher)
{
return dispatcher.newJobBuilder()
//persist the task across boots
.setLifetime(Lifetime.FOREVER)
//.setLifetime(Lifetime.UNTIL_NEXT_BOOT)
//call this service when the criteria are met.
.setService(MyJobService.class)
//unique id of the task
.setTag("TAGOFTHEJOB")
//don't overwrite an existing job with the same tag
.setReplaceCurrent(false)
// We are mentioning that the job is periodic.
.setRecurring(true)
// Run every 30 min from now. You can modify it to your use.
.setTrigger(Trigger.executionWindow(1800, 1800))
// retry with exponential backoff
.setRetryStrategy(RetryStrategy.DEFAULT_LINEAR)
//.setRetryStrategy(RetryStrategy.DEFAULT_EXPONENTIAL)
//Run this job only when the network is available.
.setConstraints(Constraint.ON_ANY_NETWORK)
.build();
}
/**
* 2018 September 27 - Thursday - 06:42 PM
* cancel job method
*
* this method will cancel the job USE THIS WHEN YOU DON'T WANT TO USE THE SERVICE ANYMORE.
**/
private void cancelJob(Context context)
{
FirebaseJobDispatcher dispatcher = new FirebaseJobDispatcher(new GooglePlayDriver(context));
//Cancel all the jobs for this package
dispatcher.cancelAll();
// Cancel the job for this tag
dispatcher.cancel("TAGOFTHEJOB");
}

Related

Foreground notification still appearing after Intent service destroyed

I have an IntentService and inside this instant service, in onCreate, I call the method startforeground(). I see then the notification when the intentService is created. However, when the IntentService is destroyed (going to onDestroy), I can see the notification for few seconds after that the service is destroyed. Why is that?
This is the code of the IntentService:
public class USFIntentService extends IntentService {
private static final String TAG = "USFIntentService";
private static final int USF_NOTIFICATION_ID = 262276;
private static final String USF_NOTIFICATION_CHANNEL_ID = "USF_NOTIFICATION_CHANNEL";
public USFIntentService() {
super("USFIntentService");
}
#Override
public void onCreate() {
super.onCreate();
Log.i(TAG,"in onCreate");
startUsfForegroundService();
}
#Override
public void onDestroy() {
super.onDestroy();
Log.i(TAG,"in onDestroy");
}
private void startUsfForegroundService() {
// Define notification channel
CharSequence name = getString(R.string.channel_name);
String description = getString(R.string.channel_description);
int importance = NotificationManager.IMPORTANCE_DEFAULT;
NotificationChannel channel =
new NotificationChannel(USF_NOTIFICATION_CHANNEL_ID, name, importance);
channel.setDescription(description);
NotificationManager notificationManager = getSystemService(NotificationManager.class);
notificationManager.createNotificationChannel(channel);
// Build notification to be used for the foreground service.
Notification notification =
new Notification.Builder(this, USF_NOTIFICATION_CHANNEL_ID)
.setContentTitle(getText(R.string.notification_title))
.setContentText(getText(R.string.notification_message))
.setSmallIcon(R.drawable.usf_notification_icon)
.build();
// Set the service as a foreground service.
startForeground(USF_NOTIFICATION_ID, notification);
}
#Override
protected void onHandleIntent(Intent intent) {
Log.i(TAG, "onHandleIntent");
if (intent != null) {
doStuff();
}
Log.i(TAG,"End of onHandleIntent");
}
}
I call this service like this:
Intent startServiceIntent = new Intent(intent);
startServiceIntent.setComponent(new ComponentName(context, USFIntentService.class));
context.startForegroundService(startServiceIntent);
Try to call Service#stopForeground after your job is done to remove it
You can call stopForeground(true) when you finish doing the stuff. So that your service gets immediately removed from foreground state and the parameter true ensures that the notification will be removed.
If STOP_FOREGROUND_REMOVE is supplied, the service's associated notification will be cancelled immediately.
If STOP_FOREGROUND_DETACH is supplied, the service's association with the notification will be severed. If the notification had not yet been shown, due to foreground-service notification deferral policy, it is immediately posted when stopForeground(STOP_FOREGROUND_DETACH) is called. In all cases, the notification remains shown even after this service is stopped fully and destroyed.
stopForeground(STOP_FOREGROUND_REMOVE) // remove with notification
stopForeground(STOP_FOREGROUND_DETACH) // remove only intent and not notification

Android: Stop Foreground Service causing Application crash

Prerequisites:
As a part of the requirement for my application, I need to make sure that the application won't be closed (killed) by the Android system while in background. For this purpose I implemented Foreground service, even though I don't do any actual process in background, just maintaining the state of the application. Everything works just fine, except one thing which is not fully clear to me how to fix.
The issue:
Sometimes (only once, for now), I receive this exception:
android.app.RemoteServiceException: Context.startForegroundService() did not then call Service.startForeground():
This exception is thrown when I'm trying to stop the foreground service while it wasn't actually started.
So, my question is - is there is a way to stop foreground service properly, making sure that it is not running before actually stopping it?
What I found at the moment is that I can have static instance for my service and compare to null before stopping service, or get the list of all services currently running. But all these look like some "hack" workarounds.
Here some code:
MyForegroundService:
public class ForegroundService extends Service {
public static final int NOTIFICATION_ID = 1;
public static final String CHANNEL_ID = "SessionForegroundServiceChannel";
public static final String ACTION_FOREGROUND_START = "ACTION_FOREGROUND_START";
public static final String ACTION_FOREGROUND_STOP = "ACTION_FOREGROUND_STOP";
public static void startForegroundService(Context context) {
Intent intent = new Intent(context, ForegroundService.class);
intent.setAction(ForegroundService.ACTION_FOREGROUND_START);
ContextCompat.startForegroundService(context, intent);
}
public static void stopForegroundService(Context context) {
Intent intent = new Intent(context, ForegroundService.class);
intent.setAction(ForegroundService.ACTION_FOREGROUND_STOP);
ContextCompat.startForegroundService(context, intent);
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (ACTION_FOREGROUND_START.equals(intent.getAction())) {
createNotificationChannel();
Intent stopForegroundIntent = new Intent(this, ForegroundServiceBroadcastReceiver.class);
PendingIntent pendingLogoutIntent = PendingIntent.getBroadcast(this,
0, stopForegroundIntent, PendingIntent.FLAG_UPDATE_CURRENT);
Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O
? null
: getString(R.string.app_short_name))
.setContentText(getString(R.string.foreground_description))
.setColor(getResources().getColor(R.color.color))
.setSmallIcon(R.drawable.ic_notification)
.addAction(R.drawable.ic_logout, getString(R.string.logout), pendingLogoutIntent)
.build();
startForeground(NOTIFICATION_ID, notification);
} else if (ACTION_FOREGROUND_STOP.equals(intent.getAction())) {
stopForeground(true);
stopSelf();
}
return START_STICKY;
}
private void createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel serviceChannel = new NotificationChannel(
CHANNEL_ID,
getString(R.string.app_name),
NotificationManager.IMPORTANCE_LOW
);
NotificationManager manager = getSystemService(NotificationManager.class);
manager.createNotificationChannel(serviceChannel);
}
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
<service
android:name=".ui.ForegroundService"
android:exported="false"
android:stopWithTask="true"/>
I also have BroadcastReceiver and EventBus to listen to some events and stop foreground depending on those events.
Can you please help me, guys?
Let me add more details to what #Pawel commented:
You get this exception if you don't call Service.startForeground within 3 seconds of calling Context.startForegroundService that's all there's to it.
Here is how the complete solution will look like:
When it comes to the case when you need to stop a foreground service you need to do the following (pseudo code):
if (action == START_FOREGROUND) {
...
startForeground(NOTIFICATION_ID, notification);
} else if (action == STOP_FOREGROUND) {
startForeground(NOTIFICATION_ID, closeNotification); //in case it wasn't started before
stopForeground(true);
stopSelf();
}
Even though it is not obvious, and any documentation don't directly say that when you need to stop foreground you need to start foreground before stopping it (if it wasn't started).
Thanks #Pawel for the hint.

Foreground Service getting Crashed and restart when User killed the app (Nougat)

I am facing a trouble here where i want a foreground service should run untill its task get completed and once task get completed foreground service should stop. But the problem here is even if its Foreground Service the Service is getting killed when user close the app only in Nougat Version. I have checked Marshamallow,Oreo, Android pie version the scenario is working fine. But In Nougat and MI phone this scenario is not working correctly.
Below is Service code which i am running as a demo
public class MyForeGroundService extends Service {
private static final String TAG_FOREGROUND_SERVICE = "FOREGROUND_SERVICE";
public static final String ACTION_START_FOREGROUND_SERVICE = "ACTION_START_FOREGROUND_SERVICE";
public static final String ACTION_STOP_FOREGROUND_SERVICE = "ACTION_STOP_FOREGROUND_SERVICE";
public static final String PRIMARY_CHANNEL = "default";
public MyForeGroundService() {
}
#Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
#Override
public void onCreate() {
super.onCreate();
Log.d(TAG_FOREGROUND_SERVICE, "My foreground service onCreate().");
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if(intent != null)
{
String action = intent.getAction();
switch (action)
{
case ACTION_START_FOREGROUND_SERVICE:
startForegroundService();
Toast.makeText(getApplicationContext(), "Foreground service is started.", Toast.LENGTH_LONG).show();
break;
case ACTION_STOP_FOREGROUND_SERVICE:
stopForegroundService();
Toast.makeText(getApplicationContext(), "Foreground service is stopped.", Toast.LENGTH_LONG).show();
break;
}
}
return super.onStartCommand(intent, flags, startId);
}
/* Used to build and start foreground service. */
private void startForegroundService()
{
NotificationCompat.Builder mBuilder = notificationBuilder();
// Start foreground service.
startForeground(1, mBuilder.build());
Handler handler = new Handler();
handler.postDelayed(new Runnable() {
#Override
public void run() {
Intent intent = new Intent();
intent.setAction("SERVICE_CONNECTED");
LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);
}
},10000);
}
private void stopForegroundService()
{
Log.d(TAG_FOREGROUND_SERVICE, "Stop foreground service.");
// Stop foreground service and remove the notification.
stopForeground(true);
// Stop the foreground service.
stopSelf();
}
public NotificationCompat.Builder notificationBuilder(){
NotificationCompat.Builder mBuilder= new NotificationCompat.Builder(this, PRIMARY_CHANNEL)
.setContentTitle("Dummy Title")
.setContentText("Dummy Message")
.setStyle(new NotificationCompat.BigTextStyle()
.bigText("Big text Message"
))
.setPriority(NotificationCompat.PRIORITY_DEFAULT);
if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
mBuilder.setSmallIcon(R.drawable.app_icon_white);
mBuilder.setColor(getResources().getColor(R.color.theme_color));
} else {
mBuilder.setSmallIcon(R.drawable.app_icon_white);
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
CharSequence name = getResources().getString(R.string.feroz_channel_name);
String description = getResources().getString(R.string.feroz_channel_description);
int importance = NotificationManager.IMPORTANCE_DEFAULT;
NotificationChannel channel = new NotificationChannel(PRIMARY_CHANNEL, name, importance);
channel.enableLights(true);
channel.setLightColor(getResources().getColor(R.color.theme_color));
channel.enableVibration(true);
channel.setDescription(description);
NotificationManager notificationManager1 = getSystemService(NotificationManager.class);
notificationManager1.createNotificationChannel(channel);
}
Intent stopIntent = new Intent(this, MyForeGroundService.class);
stopIntent.setAction(ACTION_STOP_FOREGROUND_SERVICE);
PendingIntent stopPlayIntent = PendingIntent.getService(this, 0, stopIntent, 0);
mBuilder.addAction(R.drawable.ic_launcher_new,"STOP",stopPlayIntent);
Intent intent = new Intent(this, CreateForegroundServiceActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
mBuilder.setContentIntent(pendingIntent);
mBuilder.setUsesChronometer(true);
return mBuilder;
}
}
Below is code which start the service from activity
startServiceButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(CreateForegroundServiceActivity.this, MyForeGroundService.class);
intent.setAction(MyForeGroundService.ACTION_START_FOREGROUND_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(intent);
} else {
startService(intent);
}
}
});
Button stopServiceButton = (Button)findViewById(R.id.stop_foreground_service_button);
stopServiceButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(CreateForegroundServiceActivity.this, MyForeGroundService.class);
intent.setAction(MyForeGroundService.ACTION_STOP_FOREGROUND_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(intent);
} else {
startService(intent);
}
}
});
I think startForeGroundService works well but startService is not working as expected. The behaviour is so confusing, When i kill the app foreground service gets restarted , but if i open the app and press a button to stop the service i can see intent sent to the Service. But still the service is running.
PS: I want a service which run for like 10 Mins irrespective of App is in Background or not. It shouldn't get Restarted at any cost. For e.g if i am playing a music from background its should play till end. it should stop as soon as music ended. I dont want it to be restart
Below is logs when i start the service on button click and killed the app :
2019-02-15 13:35:56.091 11120-11120/sample.androido.com.myapplication D/MyForeGroundService: My foreground service onCreate().
2019-02-15 13:36:01.956 1459-3975/? W/ActivityManager: Scheduling restart of crashed service sample.androido.com.myapplication/.services.MyForeGroundService in 1000ms
2019-02-15 13:36:02.975 1459-1509/? I/ActivityManager: Start proc 11170:sample.androido.com.myapplication/u0a154 for service sample.androido.com.myapplication/.services.MyForeGroundService
2019-02-15 13:36:03.674 11170-11170/? D/MyForeGroundService: My foreground service onCreate().
From the official Android documentation for startForegroundService() method.
Note that calling this method does not put the service in the started state itself, even though the name sounds like it. You must always call ContextWrapper.startService(Intent) first to tell the system it should keep the service running, and then use this method to tell it to keep it running harder.
According to the above statement, you should call startService() first and then startForegroundService()

How to make a periodic background work (15 mins periodic interval) to run indefinitely on Oreo and later?

After Doze Mode was introduced in Android 8, Android has enforced many restrictions on background work which makes the app behave in an unintended way.
In one of my apps , I used to fetch Battery Level every 15 mins and fire an alarm if battery level was above the desired battery level set by the user in my app. I used the following code to accomplish this task.
set up an repeating alarm using AlarmManagerApi :
public static void setAlarm(final Context context) {
final AlarmManager manager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
if (manager != null) {
manager.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, 0, ALARM_INTERVAL,
getAlarmPendingIntent(context));
}
}
where getAlarmPendingIntent method is as follows:
private static PendingIntent getAlarmPendingIntent(final Context context) {
return PendingIntent.getBroadcast(context, REQUEST_CODE, new Intent(context, AlarmReceiver.class), PendingIntent.FLAG_UPDATE_CURRENT);
}
Setup a BroadcastReciever to recieve alarms: It starts a service in the foreground to do the required background work
public class AlarmReceiver extends BroadcastReceiver {
#Override
public void onReceive(final Context context, final Intent intent) {
if (context == null || intent == null) {
return;
}
if (intent.getAction() == null) {
Intent monitorIntent = new Intent(context, TaskService.class);
monitorIntent.putExtra(YourService.BATTERY_UPDATE, true);
ContextCompat.startForegroundService(context, monitorIntent);
}
}
}
TaskService(Service doing some desired task ) is as follows
public class TaskService extends Service {
private static final String CHANNEL_ID = "channel_01";
private static final int NOTIFICATION_ID = 12345678;
#Override
public void onCreate() {
super.onCreate();
createNotificationChannel()
}
public void createNotificationChannel(){
// create notification channel for notifications
NotificationManager mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
CharSequence name = getString(R.string.app_name);
NotificationChannel mChannel =
new NotificationChannel(CHANNEL_ID, name, NotificationManager.IMPORTANCE_DEFAULT);
if (mNotificationManager != null) {
mNotificationManager.createNotificationChannel(mChannel);
}
}
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
startForeground(NOTIFICATION_ID, getNotification());
BatteryCheckAsync batteryCheckAsync = new BatteryCheckAsync();
batteryCheckAsync.execute();
return START_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
private Notification getNotification() {
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentText(text)
.setContentTitle("fetching battery level")
.setOngoing(true)
.setPriority(Notification.PRIORITY_LOW)
.setSmallIcon(R.mipmap.ic_launcher)
.setWhen(System.currentTimeMillis());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
builder.setChannelId(CHANNEL_ID);
}
return builder.build();
}
private class BatteryCheckAsync extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... arg0) {
IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
Intent batteryStatus = YourService.this.registerReceiver(null, ifilter);
int level = batteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
int scale = batteryStatus.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
Log.i("Battery Charge Level",String.valueOf((level / (float) scale)));
return null;
}
protected void onPostExecute() {
YourService.this.stopSelf();
}
}
}
BatteryCheckAsync task gets battery level from background and shows current battery level in the logs .
Now, if I run the app and swipe the app off my Recent App List and turn off the screen of the device .
The app prints the logs with battery level in android versions before Android 8 . But after android 8 , this stopped working within minutes of swiping the app off my recent app list and turning off the screen of device. The above behaviour was expected as Doze mode was introduced in android and alarms are deferred until next maintenance window .
=================================================================
From the android documentation on https://developer.android.com/training/monitoring-device-state/doze-standby,
I found it documented that
Standard AlarmManager alarms (including setExact() and setWindow())
are deferred to the next maintenance window.
If you need to set alarms that fire while in Doze, use
setAndAllowWhileIdle() or setExactAndAllowWhileIdle().
Alarms set with setAlarmClock() continue to fire normally — the system exits Doze
shortly before those alarms fire.
So, I modified my code as follows now :
public static void setAlarm(final Context context) {
final AlarmManager manager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
if (manager == null) {
return;
}
int SDK_INT = Build.VERSION.SDK_INT;
if (SDK_INT < Build.VERSION_CODES.KITKAT) {
manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, ALARM_INTERVAL, getAlarmPendingIntent(context));
} else if (SDK_INT < Build.VERSION_CODES.M) {
manager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, ALARM_INTERVAL, getAlarmPendingIntent(context));
} else {
manager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, ALARM_INTERVAL, getAlarmPendingIntent(context));
}
}
rather than setting a repeating alarm , I set a One time exact alarm and scheduling the next one myself when handling each alarm delivery in the onRecieve method of broadcast reciever by running the above setAlarm() method again.
Now, if I run the app and swipe the app off my Recent App List on OnePlus 3 and turn off the screen of the device , the app prints logs for about 40-45 mins and then suddenly the alarms and foreground service stops running.
Does any one have a solution for guaranteed execution of periodic background task for indefinite period of time in Android Oreo and above? I am Testing it on OnePlus Devices with Android Oreo or above installed.
Try to change
public class AlarmReceiver extends WakefulBroadcastReceiver {
//logic
}
Also change event for alarm Manager
alarmManager.set(AlarmManager.RTC_WAKEUP,...)
As a result you will wake up device from doze mode and execute your work.
You may also take advantage of WorkManager i.e:
class SampleWorker : Worker(){
override fun doWork(): Result {
// do some stuff
return Result.SUCCESS
}
}
and use it like this:
val recurringWork: PeriodicWorkRequest =
PeriodicWorkRequest.Builder(SampleWorker::class.java, 15, TimeUnit.MINUTES).build()
WorkManager.getInstance()?.enqueue(recurringWork)
for more info visit https://developer.android.com/topic/libraries/architecture/workmanager/basics

Background service is not working in Oreo

I want to run my app in background if I kill the app instance also. But after I kill my app the service also stops working. Here is my code please any one help me to solve my issue.
I followed this link for running in the background but it is not working if I remove the instance. Please can any one show me how to run a background service if the instance is removed also?
This is my MainActivity
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ctx = this;
setContentView(R.layout.activity_main);
Intent alarmIntent = new Intent(MainActivity.this, AlarmReceiver.class);
pendingIntent = PendingIntent.getBroadcast(MainActivity.this, ALARM_REQUEST_CODE, alarmIntent, 0);
mSensorService = new SensorService(getCtx());
mServiceIntent = new Intent(getCtx(), mSensorService.getClass());
if (!isMyServiceRunning(mSensorService.getClass())) {
startService(mServiceIntent);
}
}
Ths is my service class
public class SensorService extends Service{
public int counter=0;
public SensorService(Context applicationContext) {
super();
Log.i("HERE", "here I am!");
}
public SensorService() {
}
#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();
Log.i("EXIT", "ondestroy!");
Intent broadcastIntent = new Intent("uk.ac.shef.oak.ActivityRecognition.RestartSensor");
sendBroadcast(broadcastIntent);
}
private Timer timer;
private TimerTask timerTask;
long oldTime=0;
public void startTimer() {
//set a new Timer
timer = new Timer();
//initialize the TimerTask's job
initializeTimerTask();
//schedule the timer, to wake up every 1 second
timer.schedule(timerTask, 1000, 1000); //
}
/**
* it sets the timer to print the counter every x seconds
*/
public void initializeTimerTask() {
timerTask = new TimerTask() {
public void run() {
Log.i("in timer", "in timer ++++ "+ (counter++));
}
};
}
/**
* not needed
*/
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
Its a long story. I have gone through it. Still implemented it. Now my service runs on every boot_complete event and keeps running all the time ( with a notification ).
Official Documentation:
Big NO. Google android developer documentation is poor, with no proper sample example too. It is theoretical and just theoretical. Keep reading if interested
https://developer.android.com/about/versions/oreo/background
Synopsis 1:
You can only receive BOOT_COMPLETE and only few broadcasts in traditional receiver. Rest all broadcast receivers you need to implement runtime in a service by registering them through a code from service which always runs.
Synopsis 2:
Again, you can not have always running processes in or above 8.0 (Oreo)...
To achieve always running process... Create a Intentservice with proper notification of type ongoing and make OnStartCommand START_STICKY and register receiver with code in OnCreate
How to implement it :
I have implemented it take reference from here :
Oreo: Broadcast receiver Not working
Now Your Question : I want to run my app in background if it kills the
app instance also.
With the help of above implementation link of my own you can achieve it
*Terms and conditions
You device must have proper android operating system released and burnt as it is.
Yes, I am using android :
No... You are Using Funtouch OS : VIVO ( By modifying Android)
There are many devices in market COLOR OS : OPPO ( By modifying Android)
....
....
Already google has made it complicated... version by version....
With no proper documentation and sample codes....
And Now Independent mobile device manufacturers making a lot of
changes to allow only selective applications run in background
like WhatsApp, Facebook, Google Twitter Instagram
Now you will ask a developer question If these app runs in background then I can make my app run in background too....
No... They are OS based modifications to check if a service is from allowed vendors then only it can be alive there in background. If they will not allow these vendors then no one take phones which does not run these famous social apps.
Hushhhhhhhh.......
You need to create ForegroundService in order continue processing when your app is killed, as follows:
public class SensorService extends Service{
private PowerManager.WakeLock wakeLock;
#Override
public void onCreate() {
super.onCreate();
//wake lock is need to keep timer alive when device goes to sleep mode
final PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "PARTIAL_WAKE_LOCK_TAG");
createNotificationChannel(this);
Notification notification = new NotificationCompat.Builder(this, "NOTIFICATION_CHANNEL").setSmallIcon
(<icon>).setContentTitle("Title")
.setContentText("Content").build();
startForeground(1001, notification);
}
#Override
public void onDestroy() {
super.onDestroy();
if (wakeLock.isHeld()) {
wakeLock.release();
}
}
public void createNotificationChannel() {
// Create the NotificationChannel, but only on API 26+ because
// the NotificationChannel class is new and not in the support library
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
CharSequence name = "Channel name";
String description = "Description";
int importance = NotificationManager.IMPORTANCE_DEFAULT;
NotificationChannel channel = new NotificationChannel("NOTIFICATION_CHANNEL", name, importance);
channel.setDescription(description);
NotificationManager notificationManager = getApplicationContext().getSystemService(NotificationManager.class);
notificationManager.createNotificationChannel(channel);
}
}
}
To start the service:
Intent i = new Intent(context, SensorService.class);
ContextCompat.startForegroundService(context, i)
Note:
You cannot run service endlessly with this approach. During doze mode if OS recognizes it as CPU intensive then your Service will be terminated.
You need to call stopSelf() when your Timer task has been executed successfully.
Oreo Introduced
new Concept PIP (Picture in Picture Mode )
and it have categories services control by making channels and priority to them.you have to change the code just for oreo to create notifications and services
read about google developers documentation carefully here
https://developer.android.com/guide/topics/ui/notifiers/notifications
both java and kotlin code is available here to create notification in oreo
https://developer.android.com/training/notify-user/build-notification
it was my effort to find the solution after searching and sharing with you.
here is some sample code :
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this, CHANNEL_ID)
.setSmallIcon(R.drawable.notification_icon)
.setContentTitle("My notification")
.setContentText("Much longer text that cannot fit one line...")
.setStyle(new NotificationCompat.BigTextStyle()
.bigText("Much longer text that cannot fit one line..."))
.setPriority(NotificationCompat.PRIORITY_DEFAULT);
for creating channels write this code:
private void createNotificationChannel() {
// Create the NotificationChannel, but only on API 26+ because
// the NotificationChannel class is new and not in the support library
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
CharSequence name = getString(R.string.channel_name);
String description = getString(R.string.channel_description);
int importance = NotificationManager.IMPORTANCE_DEFAULT;
NotificationChannel channel = new NotificationChannel(CHANNEL_ID, name, importance);
channel.setDescription(description);
// Register the channel with the system; you can't change the importance
// or other notification behaviors after this
NotificationManager notificationManager = getSystemService(NotificationManager.class);
notificationManager.createNotificationChannel(channel);
}
}
you can see full detials for push notifications and sending messages by clicking on the above links.

Categories

Resources