I'm trying to use a background service so that I can check for new content on the app every 30 minutes and notify the user with a notification if there is any new content. The problem is that that service does not seem that it is starting at all. I'm not entirely sure what I have wrong.
I've followed this article to implement a notification service and this question as well - Trying to start a service on boot on Android.
AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
<activity
android:name="com.____.MainActivity"
android:label="#string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver android:name="com.____.BootReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<service android:name="com.____.NotificationService"/>
</application>
The BootReceiver should start the Service
public class BootReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
Intent startServiceIntent = new Intent(context, NotificationService.class);
context.startService(startServiceIntent);
}
}
The service NotificationService right now is set to display a simple notification
public class NotificationService extends Service {
private WakeLock mWakeLock;
/**
* Simply return null, since our Service will not be communicating with
* any other components. It just does its work silently.
*/
#Override
public IBinder onBind(Intent intent) {
return null;
}
/**
* This is where we initialize. We call this when onStart/onStartCommand is
* called by the system. We won't do anything with the intent here, and you
* probably won't, either.
*/
private void handleIntent(Intent intent) {
// obtain the wake lock
PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "My App");
mWakeLock.acquire();
// check the global background data setting
ConnectivityManager cm = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
if (cm.getActiveNetworkInfo() == null) {
stopSelf();
return;
}
// do the actual work, in a separate thread
new PollTask().execute();
}
private class PollTask extends AsyncTask<Void, Void, Void> {
/**
* This is where YOU do YOUR work. There's nothing for me to write here
* you have to fill this in. Make your HTTP request(s) or whatever it is
* you have to do to get your updates in here, because this is run in a
* separate thread
*/
#SuppressLint("NewApi") #Override
protected Void doInBackground(Void... params) {
// do stuff!
// get last added date time of offer
// every 60 minutes check for new offers
Notification notification = new Notification.Builder(getApplicationContext())
.setContentText("New Content Available")
.setSmallIcon(R.drawable.ic_launcher)
.setWhen(0)
.build();
return null;
}
/**
* In here you should interpret whatever you fetched in doInBackground
* and push any notifications you need to the status bar, using the
* NotificationManager. I will not cover this here, go check the docs on
* NotificationManager.
*
* What you HAVE to do is call stopSelf() after you've pushed your
* notification(s). This will:
* 1) Kill the service so it doesn't waste precious resources
* 2) Call onDestroy() which will release the wake lock, so the device
* can go to sleep again and save precious battery.
*/
#Override
protected void onPostExecute(Void result) {
// handle your data
stopSelf();
}
}
/**
* This is deprecated, but you have to implement it if you're planning on
* supporting devices with an API level lower than 5 (Android 2.0).
*/
#Override
public void onStart(Intent intent, int startId) {
handleIntent(intent);
}
/**
* This is called on 2.0+ (API level 5 or higher). Returning
* START_NOT_STICKY tells the system to not restart the service if it is
* killed because of poor resource (memory/cpu) conditions.
*/
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
handleIntent(intent);
return START_NOT_STICKY;
}
/**
* In onDestroy() we release our wake lock. This ensures that whenever the
* Service stops (killed for resources, stopSelf() called, etc.), the wake
* lock will be released.
*/
public void onDestroy() {
super.onDestroy();
mWakeLock.release();
}
}
Debugging the app with breakpoints in both BootRecevier and NotificationService are never triggered. Maybe I'm misunderstanding the way Background Services work.
UPDATE
I found this article about why the BroadcastRecevier is not being called. One of the mentioned points is PendingIntent requestCode missing
I've updated the BootRecevier as follows to test out the service...called every 1 minute:
#Override
public void onReceive(Context context, Intent intent) {
int minutes = 1;
AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent i = new Intent(context, NotificationService.class);
PendingIntent pi = PendingIntent.getService(context, 54321, i, 0);
am.cancel(pi);
if (minutes > 0) {
am.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime() + minutes*60*1000,
minutes*60*1000, pi);
}
}
Changing 0 to a 'unique' requestCode 54321 somehow started triggering the service. The issue is that the service does not carry on working when the app is closed...not sure if that's a completely different thing altogether.
UPDATE 2
I've updated the doInBackground method in the NotificationService, using this example to display notifications:
#SuppressLint("NewApi") #Override
protected Void doInBackground(Void... params) {
Context mContext = getApplicationContext();
// invoke default notification service
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(mContext);
mBuilder.setContentTitle("[Notification Title]");
mBuilder.setContentText("[Notification Text]");
mBuilder.setTicker(mContext.getString(R.string.app_name));
mBuilder.setSmallIcon(R.drawable.ic_launcher);
Intent resultIntent = new Intent(mContext, MainActivity.class);
TaskStackBuilder stackBuilder = TaskStackBuilder.create(mContext);
stackBuilder.addParentStack(MainActivity.class);
stackBuilder.addNextIntent(resultIntent);
PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_ONE_SHOT);
mBuilder.setContentIntent(resultPendingIntent);
NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.notify(1, mBuilder.build());
return null;
}
I've tested it on an emulator and the Background Service works even when the app is closed. However, testing the app on an actual device did not display any notifications.
You need to declare the permission to receive the intent in your manifest.
AndroidManifest.xml
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
http://developer.android.com/reference/android/Manifest.permission.html#RECEIVE_BOOT_COMPLETED
You will only receive notifications of the device booting after the user has started your application at least once.
I happened to have the same issue recently, and I am using a 5.1.1 android smartphone. The way I solved this issue was using the ic_launcher in mipmap to replace the ic_launcher in drawable when called setSmallIcon().
However, for the exact reason, I guess it has something to do with the difference between mipmap and drawable.
Ref: mipmap vs drawable folders
Related
In my application, I use location based service in background. So I need to restart my service when it gets destroyed.
But I got this message in logcat
Spurious death for ProcessRecord{320afaf6 20614:com.odoo.crm:my_odoo_gps_service/u0a391}, curProc for 20614: null
My service onTaskRemoved
#Override
public void onTaskRemoved(Intent rootIntent) {
System.out.println("onTaskRemoved called");
Intent restartServiceIntent = new Intent(App.getAppContext(), this.getClass());
restartServiceIntent.setPackage(getPackageName());
PendingIntent restartServicePendingIntent =
PendingIntent.getService(App.getAppContext(), 1, restartServiceIntent,
PendingIntent.FLAG_ONE_SHOT);
AlarmManager alarmService =
(AlarmManager) App.getAppContext().getSystemService(Context.ALARM_SERVICE);
alarmService.set(
AlarmManager.ELAPSED_REALTIME,
SystemClock.elapsedRealtime() + 1000,
restartServicePendingIntent);
}
My service onDestroy
#Override
public void onDestroy() {
System.out.println("destroy service");
super.onDestroy();
wakeLock.release();
}
My service onStartCommand
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return Service.START_STICKY;
}
I don`t know what is the error. I searched both in google & stackoverflow.
All of them refer Service.START_STICKY. but I already used it.
Same service restart works in KitKat, but with some delay(~5 mins).
Any help is appreciated.
You can restart it by using a BroadcasteReceiver which handles the broadcast sent from onDestroy() of your service.
How to do this:
StickyService.java
public class StickyService extends Service
{
#Override
public IBinder onBind(Intent arg0) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
#Override
public void onTaskRemoved(Intent rootIntent) {
super.onTaskRemoved(rootIntent);
sendBroadcast(new Intent("IWillStartAuto"));
}
#Override
public void onDestroy() {
super.onDestroy();
sendBroadcast(new Intent("IWillStartAuto"));
}
}
RestartServiceReceiver.java
public class RestartServiceReceiver extends BroadcastReceiver
{
#Override
public void onReceive(Context context, Intent intent) {
context.startService(new Intent(context.getApplicationContext(), StickyService.class));
}
}
Declare the components in manifest file:
<service android:name=".StickyService" >
</service>
<receiver android:name=".RestartServiceReceiver" >
<intent-filter>
<action android:name="IWillStartAuto" >
</action>
</intent-filter>
</receiver>
Hope this will help you.
Your code in onTaskRemoved is preventing the system to run the killProcess commands. The delay on Kitkat is caused by using alarmService.set, which is inexact from API 19. Use setExact instead.
If you have a service that you want to keep alive, it is recommended that you attach a notification to it and make it foreground. That way the likeliness of it being killed would be lowered.
import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.os.Environment;
import android.os.IBinder;
import android.support.v7.app.NotificationCompat;
import java.io.File;
import java.io.IOException;
import activity.MainActivity;
import activity.R;
import fragment.MainFragment;
public class MyService extends Service {
public static final int NOTIFICATION_CODE = 1;
#Override
public void onCreate() {
super.onCreate();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
startForeground(NOTIFICATION_CODE, getNotification());
return START_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onDestroy() {
stopForeground(true);
super.onDestroy();
}
#Override
public boolean stopService(Intent name) {
return super.stopService(name);
}
/**
* Create and return a simple notification.
*/
private Notification getNotification() {
Notification notification;
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
builder.setColor(getResources()
.getColor(R.color.material_deep_teal_500))
.setAutoCancel(true);
notification = builder.build();
notification.flags = Notification.FLAG_FOREGROUND_SERVICE | Notification.FLAG_AUTO_CANCEL;
return notification;
}
}
You can modify this code to accomodate your needs but this is the basic structure to start foreground service. Which restarts if gets killed.
how you check issocketalive that socket is connected or not ?
if sockettimeoutexception is generated then try to on set getinputstream and getoutputstream.
other issue that may be socket not closed properly.
So if possible then put your socket code here
this worked for me
Add this attribute in android:allowBackup="false" in manifest file in application tag.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application
android:allowBackup="false"
tools:replace="android:allowBackup">
</application>
</manifest>
The idea of having a service ALWAYS running in background in Android is just wrong 99% of the times.
The system need to "shut down" CPU, and switch to a low battery usage profile.
You are saying you have a location based service. I assume you are using Google Play Services FusedLocationProvider, if not you should.
The FusedLocationProvider allow you to register for location changes using a PendingIntent. Meaning your services doesn't need to run all the time, it just need to register for location changes and then react when a new location come and do its stuff.
See the FusedLocationProviderApi official documentation.
To start listening for location updates
connect to the GoogleClient using the LocationServices.API API
Build your LocationRequest according to your needs (see the doc)
Call requestLocationUpdates() using the PendingIntent version
To stop listening
connect to the GoogleClient using the LocationServices.API API
Call removeLocationUpdates() using the same PendingIntent
Your PendingIntent can launch another service to handle the new location.
For example doing this from a service:
public void startMonitoringLocation(Context context) {
GoogleApiClient client = new GoogleApiClient.Builder(context)
.addApi(LocationServices.API)
.build()
ConnectionResult connectionResult = mApiClient.blockingConnect();
if (connectionResult.isSuccess()) {
LocationServices.FusedLocationApi
.requestLocationUpdates(client, buildLocationRequest(), buildPendingIntent(context));
} else {
handleConnectionFailed(context);
}
}
Then the service can immediately stop.
The first time this code run it WILL fail. The connection to the google client usually require the user to take some actions. The ConnectionResult.hasResolution() method will return true if this is the case. Otherwise the reason is something else and you can't recover from it. Meaning the only thing you can do is inform the user the feature will not work or have a nice fallback.
The ConnectionResult.getResolution() give you a PendingIntent you need to use an Activity and startIntentSenderForResult() method on the Activity to resolve this intent. So you would create a Notification starting your Activity to resolve that, and in the end call your Service again.
I usually just start an Activity dedicated to do all the work. It's lot easier but you don't want to call connectBlocking() in it. Check out this on how to do it.
You may ask why not requesting location updates directly in the Activity. That's actually perfectly fine, unless you need the location monitor to automatically start with the device, even if the user didn't explicitly opened the App.
<receiver android:name=".BootCompletedBroadcastReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
This way you can just run your service to connect and request location updates when the device is rebooted.
Example on how you can build your location request:
public LocationRequest buildLocationRequest() {
LocationRequest locRequest = LocationRequest.create();
// Use high accuracy
locRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
// how often do you need to check for the location
// (this is an indication, it's not exact)
locRequest.setInterval(REQUIRED_INTERVAL_SEC * 1000);
// if others services requires the location more often
// you can still receive those updates, if you do not want
// too many consider setting this lower limit
locRequest.setFastestInterval(FASTEST_INTERVAL_SEC * 1000);
// do you care if the user moved 1 meter? or if he move 50? 1000?
// this is, again, an indication
locRequest.setSmallestDisplacement(SMALLEST_DISPLACEMENT_METERS);
return locRequest;
}
And your pending intent:
public PendingIntent buildPendingIntent(Context context) {
Intent intent = new Intent(context, LocationUpdateHandlerService.class);
intent.setAction(ACTION_LOCATION_UPDATE);
intent.setPackage(context.getPackageName());
return PendingIntent.getService(context, REQUEST_CODE, intent, PendingIntent.FLAG_CANCEL_CURRENT);
}
Your LocationUpdateHandlerService can be an IntentService if you need to do work in background:
#Override
protected void onHandleIntent(Intent intent) {
if (intent != null) {
Bundle extras = intent.getExtras();
if (extras != null && extras.containsKey(FusedLocationProviderApi.KEY_LOCATION_CHANGED)) {
Location location = extras.getParcelable(FusedLocationProviderApi.KEY_LOCATION_CHANGED);
handleLocationChanged(location);
} else {
Log.w(TAG, "Didn't receive any location update in the receiver");
}
}
}
But can also be a Broadcast or anything that suits you.
Finally I achieved with help of Evernote JobService
Github link - https://github.com/evernote/android-job
Step 1: Add evernote jobservice dependency
implementation 'com.evernote:android-job:1.3.0-alpha03'
Step 2: Create DemoJobCreator.java class
public class DemoJobCreator implements JobCreator {
#Override
#Nullable
public Job create(#NonNull String tag) {
switch (tag) {
case DemoSyncJob.TAG:
return new DemoSyncJob();
default:
return null;
}
}
}
Step 3: Create DemoSyncJob.java class
public class DemoSyncJob extends Job {
public static final String TAG = ">>>> job_demo_tag";
#Override
#NonNull
protected Result onRunJob(Params params) {
// run your job here
Log.d(TAG, "onRunJob: ");
if(!isMyServiceRunning(this.getContext(), TestService.class)){
Intent intent=new Intent(context,TestService.class);
context.startService(intent);
}
scheduleJob();
return Job.Result.SUCCESS;
}
public static void scheduleJob() {
new JobRequest.Builder(DemoSyncJob.TAG)
.setExecutionWindow(2_000L, 2_000L)
//.setPeriodic(900000) -> recommended. but it will work after 15 min (if you used this no need scheduleJob(); inside onRunJob();)
.build()
.schedule();
}
public static boolean isMyServiceRunning(Context context, Class<?> serviceClass) {
try {
ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if (serviceClass.getName().equals(service.service.getClassName())) {
return true;
}
}
}catch (Exception e){
Log.e(TAG, "isMyServiceRunning: ",e );
}
return false;
}
}
Step 4: In your Application file (If not available create it) add following line in onCreate()
JobManager.create(this).addJobCreator(new DemoJobCreator());
Step 5: Finally start JobService in your Activity
DemoSyncJob.scheduleJob();
This JobService will check service running or not (every 2 second) If service not running it will restart the service.
Disclaimer : This may be not right solution. But it will 100% working.
I hope it helps atleast anyone in future.
I am new to this part of android, and here I aim to use alarm manager to run a code snippet every 2 minute which will poll a server (using the website's api) and based on the returned JSON generate notification.
After a looking up the web I thought one of the best option in my case will be using intent service and android.
Manifest of Services and Recievers
<service
android:name=".NotifyService"
android:enabled="true"
android:exported="false" >
</service>
<receiver
android:name=".TheReceiver"
android:enabled="true"
android:exported="true" >
</receiver>
<receiver
android:name=".OnOffReceiver"
android:enabled="true"
android:exported="true" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>
Part in the flash screen activity where I call the intent service which is responsible for polling for notification:
Intent msgIntent = new Intent(this, NotifyService.class);
startService(msgIntent);
The receiver to start the alarm on device start:
public class OnOffReceiver extends BroadcastReceiver
{
private AlarmManager alarmMgr;
private PendingIntent alarmIntent;
public OnOffReceiver(){}
#Override
public void onReceive(Context context, Intent intent)
{
Intent service = new Intent(context, NotifyService.class);
service.setAction(NotifyService.CREATE);
context.startService(service);
}
}
The IntentService Class
public class NotifyService extends IntentService
{
public NotifyService()
{
super("NotifyService");
}
public static final int STATUS_RUNNING = 0;
public static final int STATUS_FINISHED = 1;
public static final int STATUS_ERROR = 2;
#Override
protected void onHandleIntent(Intent intent)
{
if (intent != null)
{
final String action = intent.getAction();
}
StartStuff();
}
public void StartStuff()
{
Intent intent = new Intent(this, TheReceiver.class);
PendingIntent pend_intent = PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,1200,1200, pend_intent);
//1200ms to make it easier to test
}
}
The receiver class which sets notification, for testing pupose I am not doing any network related work here just making a simple notification to check if the app is running in all situations
public class TheReceiver extends BroadcastReceiver
{
public TheReceiver(){}
#Override
public void onReceive(Context context, Intent intent)
{
Toast.makeText(context, " Success ", Toast.LENGTH_SHORT).show();
Log.d("Notification", "The Receiver Successful");
showNotification(context);
}
private void showNotification(Context context)
{
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(context).setContentTitle("My notification").setContentText("Hello World!");
mBuilder.setDefaults(Notification.DEFAULT_SOUND);
mBuilder.setAutoCancel(true);
NotificationManager mNotificationManager =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.notify(1, mBuilder.build());
}
}
However the notification come only when the app is running or in the recent apps tray.
It does not start notifying when the phone reboots, nor does it notify after the app is removes from the recent apps tray.
The app needs Notify users like other apps (like gmail, whatsapp) do, even if they are swiped out of the recent apps tray.
Timeliness and punctuality are not very big issue as delay up to 5 to 10 minutes are tolerable. (I intend to poll ever 2 minutes though.)
Where am I going wrong? Also, is there a better way to go about the problem?
To keep a receiver active after closing the app is to use
android:process=":remote"
in the manifest file for the receiver that needs to be kept alive.
<receiver
android:name=".TheAlarmReceiver"
android:process=":remote">
</receiver>
in the manifest for the receiver (TheReceiver in this case) that we need to keep active after the app closes.
P.S. : I also changed the way I use IntentsService and AlarmManager for the application, as my previous(above) implementation is not a very good way to go around it.
If an App is killed from recent apps or from "force stop" it won't restart by itself. The user has to start the app again in order to make it run again. There is no way to prevent this. It's just the way android works.
However there is a way to make your app run oon boot. Check out this link.
I am starting a service (or re-starting the running service) when an activity is launched, using :
Intent intent = new Intent(this, MyService.class);
startService(intent);
Later on based on certain actions, the same activity binds to the service using
bindService(new Intent(this, MyService.class), mConnection, Context.BIND_AUTO_CREATE);
And when the activity is destroyed, I call
unbindService(mConnection);
Earlier, the service used to restart when I killed the same activity/application from the application tray and showed the "message 1 process 1 service running" under running apps.
Now, the service does not restart on killing the same activity/application.
And I get the message "0 process 1 service running", which means the service is actually not running.
The service does not restart on application being closed. My application consists of one activity. Also the service is successfully started when launched after a system boot.
Why does the process of the service gets killed when I start it using startService() ??
edit
The service used to re-start earlier after i closed the app from the application tray. But now suddenly with the SAME code, it doesn't. It happens with other apps too when i close them. eg.
Here is a workaround I came across and works well for re-starting a service if its process is killed on closing the application. In your service, add the following code.
I came across this workaround in this thread.
#Override
public void onTaskRemoved(Intent rootIntent){
Intent restartServiceIntent = new Intent(getApplicationContext(), this.getClass());
restartServiceIntent.setPackage(getPackageName());
PendingIntent restartServicePendingIntent = PendingIntent.getService(getApplicationContext(), 1, restartServiceIntent, PendingIntent.FLAG_ONE_SHOT);
AlarmManager alarmService = (AlarmManager) getApplicationContext().getSystemService(Context.ALARM_SERVICE);
alarmService.set(
AlarmManager.ELAPSED_REALTIME,
SystemClock.elapsedRealtime() + 1000,
restartServicePendingIntent);
super.onTaskRemoved(rootIntent);
}
Seems to be a bug that the process of the application is killed. There is no point for a service to run if its process is killed.
Please be aware of that: onDestroy is not always called. You should not put code that way.
When activity forced closed or closed by system abnormally, onDestroy is not getting called.
Unfortunately, this is a complicated problem due to the way Android works. There are a number of strategies that each work around different parts of the problem. For best results, combine multiple strategies together.
Note that some of these strategies may no longer be necessary in more recent Android versions.
1. Start an activity
What to do
Taken from Foreground service killed when receiving broadcast after acitivty swiped away in task list:
In the foreground service:
#Override
public void onTaskRemoved( Intent rootIntent ) {
Intent intent = new Intent( this, DummyActivity.class );
intent.addFlags( Intent.FLAG_ACTIVITY_NEW_TASK );
startActivity( intent );
}
In the manifest:
<activity
android:name=".DummyActivity"
android:theme="#android:style/Theme.NoDisplay"
android:enabled="true"
android:allowTaskReparenting="true"
android:noHistory="true"
android:excludeFromRecents="true"
android:alwaysRetainTaskState="false"
android:stateNotNeeded="true"
android:clearTaskOnLaunch="true"
android:finishOnTaskLaunch="true"
/>
(If your service is in a different process then set this activity's process to the same one.)
In DummyActivity.java:
public class DummyActivity extends Activity {
#Override
public void onCreate( Bundle icicle ) {
super.onCreate( icicle );
finish();
}
}
Side effects
Causes the recents activity to close. Normally, swiping away an app doesn't close the recents activity.
Disadvantages
This only takes effect when the dummy activity starts, which may take half a second or more, so this still leaves the service open to being killed for a bit.
Explanation
When you remove/swipe your app away, a flag called waitingToKill is set. While this flag is set, Android may kill the process at any point in the future, such as when you receive a broadcast. Starting an activity clears this flag.
2. Spam a BroadcastReceiver with foreground broadcasts
What to do
Merge this into your service code:
if (Build.VERSION.SDK_INT >= 16) {
Intent intent = new Intent(this, DummyReceiver.class);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
//This seems to be timing-related; the more times we do this,
//the less likely the process gets killed
for (int i = 0; i < 50; ++i)
sendBroadcast(intent);
}
Create a dummy broadcast receiver:
public class DummyReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {}
}
Add the receiver to your manifest:
<receiver android:name=".DummyReceiver" />
Side effects
May cause a slight (~250ms) delay/hang when the task is removed from the recents screen.
Disadvantages
This only keeps the process alive while it is receiving the broadcasts. the waitingToKill flag is still set, so the process may still be killed afterwards, such as when a broadcast is received.
Explanation
If your process isn't running in foreground priority, Android will try to kill it immediately. Receiving foreground broadcasts temporarily prevents this, resulting in the waitingToKill flag being set instead.
3. Don't bind to services
Binding to a service seems to increase the likelihood of the service's process being killed immediately when a task is removed.
I know this question is old but I recently encountered this problem and suddenly my service get stopped on closing app. Earlier it was working fine. This problem wasted my lot of time. To others who have similar problem make sure that YOUR BACKGROUND DATA RESTRICTION IS OFF.
This was the problem I had and it actually makes sense as when background data is Restricted background process won't run.
onDestroy is not always called. The Main problem in your case is ur unable to start the service when app closed,that time android OS(In Some OS) will kill the service, If you are not able to restart the service then call a alarm manger to start the reciver like this,
Manifest is,
<service
android:name=".BackgroundService"
android:description="#string/app_name"
android:enabled="true"
android:label="Notification" />
<receiver android:name="AlarmReceiver">
<intent-filter>
<action android:name="REFRESH_THIS" />
</intent-filter>
</receiver>
IN Main Activty start alarm manger in this way,
String alarm = Context.ALARM_SERVICE;
AlarmManager am = (AlarmManager) getSystemService(alarm);
Intent intent = new Intent("REFRESH_THIS");
PendingIntent pi = PendingIntent.getBroadcast(this, 123456789, intent, 0);
int type = AlarmManager.RTC_WAKEUP;
long interval = 1000 * 50;
am.setInexactRepeating(type, System.currentTimeMillis(), interval, pi);
this will call reciver and reciver is,
public class AlarmReceiver extends BroadcastReceiver {
Context context;
#Override
public void onReceive(Context context, Intent intent) {
this.context = context;
System.out.println("Alarma Reciver Called");
if (isMyServiceRunning(this.context, BackgroundService.class)) {
System.out.println("alredy running no need to start again");
} else {
Intent background = new Intent(context, BackgroundService.class);
context.startService(background);
}
}
public static boolean isMyServiceRunning(Context context, Class<?> serviceClass) {
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningServiceInfo> services = activityManager.getRunningServices(Integer.MAX_VALUE);
if (services != null) {
for (int i = 0; i < services.size(); i++) {
if ((serviceClass.getName()).equals(services.get(i).service.getClassName()) && services.get(i).pid != 0) {
return true;
}
}
}
return false;
}
}
And this Alaram reciver calls once when android app is opened and when app is closed.SO the service is like this,
public class BackgroundService extends Service {
private String LOG_TAG = null;
#Override
public void onCreate() {
super.onCreate();
LOG_TAG = "app_name";
Log.i(LOG_TAG, "service created");
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(LOG_TAG, "In onStartCommand");
//ur actual code
return START_STICKY;
}
#Override
public IBinder onBind(Intent intent) {
// Wont be called as service is not bound
Log.i(LOG_TAG, "In onBind");
return null;
}
#TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
#Override
public void onTaskRemoved(Intent rootIntent) {
super.onTaskRemoved(rootIntent);
Log.i(LOG_TAG, "In onTaskRemoved");
}
#Override
public void onDestroy() {
super.onDestroy();
Log.i(LOG_TAG, "In onDestroyed");
}
}
when there is no binding to a service or well established foreground then android system recognize the service as unused overloading service that should be shut down. Here is the best way to maintain your service even if the app is closed: AlarmManager or Service
I have tried various ways to achieve this, but my service eventually gets killed.
I want to use AlarmManager to trigger a class every one hour. Even if the device is sleeping, it should sent a flashing LED alert, vibration or sound. In any case, it should run forever.
I have noticed that Whatsapp is always running, even though I kill all the running apps and clear the memory, put the device to sleep, and still Whatsapp receive messages and alerts me. How are they doing it? I want to do the same with my app.
NOTE: NOW THIS ANSWER IS ONLY VALID FOR ANDROID 7 AND BELOW. SINCE ANDROID 8 GOOGLE HAS CHANGED HOW BACKGROUND TASKS ARE HANDLED
Since I posted this question, I have implemented two different approaches to this solution into multiple apps.
APPROACH 1
This extract is from an app where I use push notifications, which need instant wake up calls for the device. Here what I do is
use WAKE_LOCK permission and
use a Wakelocker abstract class
use it in an Activity as needed:
Manifest:
<uses-permission android:name="android.permission.WAKE_LOCK" />
WakeLocker class:
public abstract class WakeLocker {
private static PowerManager.WakeLock wakeLock;
public static void acquire(Context context) {
if (wakeLock != null) wakeLock.release();
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
wakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK |
PowerManager.ACQUIRE_CAUSES_WAKEUP |
PowerManager.ON_AFTER_RELEASE, "WakeLock");
wakeLock.acquire();
}
public static void release() {
if (wakeLock != null) wakeLock.release(); wakeLock = null;
}
}
Activity class example:
private final BroadcastReceiver receiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
// Waking up mobile if it is sleeping
WakeLocker.acquire(getApplicationContext());
// do something
WakeLocker.release();
}
APPROACH 2
Best when you want to give Android control over wake up, and can live with periodically waking up your code. Simply use an AlarmManager to invoke a Service class at regular intervals. Here is some code from my LifeLog24 app:
MainActivity
Intent ll24 = new Intent(context, AlarmReceiverLifeLog.class);
PendingIntent recurringLl24 = PendingIntent.getBroadcast(context, 0, ll24, PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager alarms = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
alarms.setRepeating(AlarmManager.RTC_WAKEUP, first_log.getTime(), AlarmManager.INTERVAL_HOUR, recurringLl24); // Log repetition
Alarm Class
public class AlarmReceiverLifeLog extends BroadcastReceiver {
private static final String TAG = "LL24";
static Context context;
#Override
public void onReceive(Context context, Intent intent) {
Log.v(TAG, "Alarm for LifeLog...");
Intent ll24Service = new Intent(context, LifeLogService.class);
context.startService(ll24Service);
}
}
and LifeLogService.class is where I do my stuff. Alarm wakes up every hour in this case and triggers the BroadcastReceiver which in return runs the service. There is more to it, to make sure service is not run twice and so on, but you get the point how it is done. And AlarmManager is actually the best way to do it since you don't worry about battery usage, etc. and Android takes care of waking up your Service at regular intervals.
It is very simple.
steps:
1.create a Service class.
2.create a BroadcastReceiver class
3.call BroadReceiver in onDestroy method of service
4.In onReceive method of BroadReceiver class start service once again.
Here's the code
Manifest file:`
<application
android:allowBackup="true"
android:icon="#mipmap/ic_launcher"
android:label="#string/app_name"
android:roundIcon="#mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="#style/AppTheme">
<activity android:name=".LauncherActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name=".utilities.NotificationService"
android:enabled="true">
</service>
<receiver
android:name=".utilities.RestartService"
android:enabled="true"
android:exported="true"
android:label="RestartServiceWhenStopped"
android:permission="android.permission.RECEIVE_BOOT_COMPLETED">
<intent-filter>
<action android:name="RestartService" />
</intent-filter>
</receiver>
</application>
`
Service class
public class NotificationService extends Service {
public NotificationService() {
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
return START_STICKY;
}
#Override
public void onDestroy() {
super.onDestroy();
Intent restartService = new Intent("RestartService");
sendBroadcast(restartService);
}
}
BroadcastReceiver class
public class RestartService extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
context.startService(new Intent(context,NotificationService.class));
}
}
Follow these easy steps to keep servce alive forever in android device.
1. Call a service by using alarm manager.
2. return START_STICKY in onStart method.
3. In on destroy call the alarm manager and restart service by using startService method.
4.(Optional)Repeat the point number 3 in onTaskRemoved method.
Request partial WakeLock.
<uses-permission android:name="android.permission.WAKE_LOCK" />
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK, "My Tag");
mWakeLock.acquire();
onStartCommand retrun START_STICKY :
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
return START_STICKY;
}
You can use a function startForeground(int, Notification) see here and here
A started service can use the startForeground(int, Notification) API
to put the service in a foreground state, where the system considers
it to be something the user is actively aware of and thus not a
candidate for killing when low on memory. (It is still theoretically
possible for the service to be killed under extreme memory pressure
from the current foreground application, but in practice this should
not be a concern.)
I've been struggling with this problem for days. I've also checked the documentation and several topics but didn't find any solution / explanation.
I am testing my application on LG p500 but I did a few test on Droid too and I get the same result.
My application uses AlarmHandler to schedule alarm. The application works correctly on the emulator and also on the device until the device has enough free memory.
When I start several other applications on the device and the memory is low the alarm will not fire anymore. As soon as I stop the "other" application the alarm works fine again.
Let me report the test and the result.
I set an alarm on my application 10 minute later.
I start several application (browser, google map, gmail, K9Mail,....)
I start the catlog to see the log of my application
Wait 15 minute without working on the phone
After 10 minutes the alarm should be fired but nothing happen until I wakeup my phone pressing a button
When I wake-up my phone the alarm immediatly fires and all the notificatin happen.
I stop the "other" application I previously started (browser, google map,...)
Set again an alarm 10 minute later
I start the catlog to see the log of my application
Wait without working on the phone
10 minutes later the alarm fires and I get notified.
I did this test several time and I get the same result.
Then I tried to set an alarm using the "Catch" application I previously downloaded from the market and I get the same behaviour so it looks like this is not a problem of my application.
Looking at the log of my application I do not see any error / exception but it looks like that when the system is low on memory something happen and the broadcast receiver does not start until the phone is waked up throught the keyboard. As soon as I wake-up the phone the receiver start and all the notification happen.
Here the code I used:
The Receiver:
public class NotificationReceiver extends BroadcastReceiver
{
public static final String LOG_TAG = "YAAS - Notification Receiver";
#Override
public void onReceive(Context context, Intent intent)
{
ScheduleActivityService.acquireStaticLock(context);
Log.i(LOG_TAG, "Received alarm - id: " + intent.getIntExtra("id", -1));
Intent intent2 = new Intent(context, ScheduleActivityService.class);
intent2.putExtra("id", intent.getIntExtra("id", -1));
context.startService(intent2);
}
}
The Service
public class ScheduleActivityService extends Service
{
public static final String LOCK_NAME_STATIC="it.hp.yaas.AppService.Static";
public static final String LOG_TAG = "YAAS - ActivityService";
private static PowerManager.WakeLock lockStatic = null;
private final IBinder mBinder = new LocalBinder();
public class LocalBinder extends Binder
{
public ScheduleActivityService getService()
{
return ScheduleActivityService.this;
}
}
#Override
public IBinder onBind(Intent intent)
{
return mBinder;
}
public static void acquireStaticLock(Context context) {
getLock(context).acquire();
}
synchronized private static PowerManager.WakeLock getLock(Context context)
{
if (lockStatic == null)
{
PowerManager mgr = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
lockStatic = mgr.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, LOCK_NAME_STATIC);
lockStatic.setReferenceCounted(true);
}
return(lockStatic);
}
/**
* This method is called when an alarm fires that is its alarm time is reached.
* The system assume that the alarm fired match the alarm time of the first
* activity.
* #param intent intent fired
* #param flag
* #param startId
*/
#Override
public int onStartCommand(Intent intent, int flag, int startId)
{
super.onStartCommand(intent, flag, startId);
try {
Log.i(LOG_TAG, "Alarm fired: " + startId + " - id: " + intent.getIntExtra("id", -1));
AlarmHandler.getInstance().onAlarmFired(intent.getIntExtra("id", -1));
}
finally { getLock(this).release(); }
return START_STICKY;
}
#Override
public void onDestroy()
{
super.onDestroy();
Log.i(LOG_TAG, "Destroy");
}
}
An piece of code from AlarmHandler, the routine called to schedule the alarm:
public synchronized void onAlarmFired(int alarmId)
{
scheduledAlarmId = -1;
Alarm alarmFired = pop();
if (alarmFired == null) return;
Log.i(LOG_TAG, "onAlarmFired (Alarm: " + alarmFired + ") at (time: " + Utilities.convertDate(new Date(), "HH:mm:ss") + ")");
notifyAlarmListener(alarmFired);
if (alarmFired.reschedule(null) != null) add(alarmFired);
Alarm alarm = peek();
if (alarm != null && scheduledAlarmId != alarm.getId()) scheduleEvent(alarm);
}
/**
* Schedule an alarm through AlarmManager that trigger next activity notification
* #param alarm alarm to be scheduled
*/
private void scheduleEvent(Alarm alarm)
{
Log.i(LOG_TAG, "scheduleEvent - (Alarm: " + alarm + ")");
Intent intent = new Intent(context, NotificationReceiver.class);
intent.putExtra("id", alarm.getId());
// In reality, you would want to have a static variable for the request code instead of 192837
PendingIntent sender = PendingIntent.getBroadcast(context, 192837, intent, PendingIntent.FLAG_UPDATE_CURRENT);
// Get the AlarmManager service
AlarmManager am = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
am.set(AlarmManager.RTC_WAKEUP, alarm.getTime().getTime(), sender);
scheduledAlarmId = alarm.getId();
}
And finally this is a piece of Manifest file:
<activity android:name=".ListActivity"
android:label="#string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".EditActivity"/>
<activity android:name=".SettingsActivity"/>
<service android:name="ScheduleActivityService"
android:label="YAAS Service"/>
<receiver android:name="NotificationReceiver" />
Are you sure your process doesn't get killed when you start all those applications? If it does, the alarms you set will die with it. It's not exactly clear who and when schedules the alarm in your code, but if it's the service, since it's sticky, it will eventually gets re-started, and you will get an alarm at some point (when you wake the device).
An easy way to check what alarms are registered at different points of your testing:
# adb shell dumpsys alarm
My code is very similar to yours on an alarm app that I wrote and use regularly. I haven't been able to reproduce the problem that you describe. I can't seem to get my phone to a state of extremely low memory. I opened every app I have installed and still have 260M free on my HTC Rezound.
As a safeguard in my app I used alarmmanager.setRepeating() instead of .set(). I set the repeat interval to 20 seconds. I passed the alarm ID as an intent extra just as you have. When my service starts it immediately cancels the pending intent using the alarm ID. My logic here is that if for any reason my alarm fails it will continue to try every 20 seconds until it succeeds.
In your code is AlarmManager.set(), which is not guaranteed to fire at the time you specify. It may fire 30 minutes or even 6 hours later, which I've seen happen on devices like the Xiaomi POCO F1.
Instead use AlarmManager.setExact() to schedule your code to run at a specific time.
Android 12 introduces an exact alarms permisison. If you don't want to deal with that, you can instead use AlarmManager.setWindow() with a small window like 15 minutes.