Based on my limited knowledge, you need to use AlarmManager
, WakefulBroadcastReceiver
and create a Service
or IntentService
for all these background task. Read more about it here. I have a service that I used for my Firebase messaging app and it works fine even after user killed the app. In my case, I connect to Firebase at fixed period using the following service.
First, I have the following class to set the AlarmManager
.
public class FirebaseHandler {
private Context context;
private static final long FIREBASE_ALARM_CYCLE_TIME = 300000;
// constructors
public FirebaseHandler(Context context) {
this.context = context;
}
// set alarm
public void setAlarm() {
// create pending intent
Intent intent = new Intent(context, AlarmReceiver.class);
intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
final PendingIntent pendingIntent = PendingIntent.getBroadcast(
context,
AlarmReceiver.ALARM_REQUEST_CODE,
intent,
PendingIntent.FLAG_UPDATE_CURRENT);
// create alarm
AlarmManager alarm = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarm.setInexactRepeating(
AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime() + FIREBASE_ALARM_CYCLE_TIME,
FIREBASE_ALARM_CYCLE_TIME,
pendingIntent);
}
}
I just initiate this by calling new FirebaseHandler(getApplicationContext()).setAlarm()
somewhere in the activity. The AlarmReceiver
class is as below.
public class AlarmReceiver extends WakefulBroadcastReceiver {
public static final int ALARM_REQUEST_CODE = 12345;
@Override
public void onReceive(Context context, Intent wakefulIntent) {
Intent intent = new Intent(context, FirebaseAlarmService.class);
startWakefulService(context, intent);
}
}
The FirebaseAlarmService
class is as below.
public class FirebaseAlarmService extends Service {
private static final String CLASSNAME = FirebaseAlarmService.class.getSimpleName();
private HandlerThread handlerThread;
// onStartCommand
@Override
public int onStartCommand(final Intent intent, int flags, final int startId) {
// start a new thread
// this depends on your need. I need to do a continuous operation for some time
// you can use IntentService too
handlerThread = new HandlerThread(CLASSNAME);
handlerThread.start();
Handler handler = new Handler(handlerThread.getLooper());
Runnable runnable = new Runnable() {
@Override
public void run() {
// all your codes here for the background task...
// remember to release the wake lock and stop service
AlarmReceiver.completeWakefulIntent(wakefulIntent);
stopSelf();
}
return START_NOT_STICKY;
}
// this is the part that does the trick
@Override
public void onTaskRemoved(Intent rootIntent) {
super.onTaskRemoved(rootIntent);
// when the service is killed, start it again
new FirebaseHandler(getApplicationContext()).setAlarm();
}
// onDestroy
@Override
public void onDestroy() {
super.onDestroy();
// make sure to quit the thread
handlerThread.quit();
}
}
In summary, FirebaseHandler
sets an AlarmManager
, which will call WakefulBroadcastReceiver
periodically, which will starts a Service
periodically. When the service is killed, the service itself will start the AlarmManager
again onTaskRemoved
.
In the AndroidManifest
, you'll need to add in the following permission for wake lock.
<uses-permission android:name="android.permission.WAKE_LOCK" />
Also remember to add the receiver.
<receiver
android:name=".receiver.AlarmReceiver"
android:process=":remote" />
BONUS: You might want to start the service once the phone is rebooted or the app is updated via PlayStore. Create another WakefulBroadcastReceiver
.
public class BootBroadcastReceiver extends WakefulBroadcastReceiver {
@Override
public void onReceive(Context context, Intent wakefulIntent) {
// check if user is login, then set the alarm
if (isLogin) {
new FirebaseHandler(context).setAlarm();
}
// remove wake lock
WakefulBroadcastReceiver.completeWakefulIntent(wakefulIntent);
}
}
In the AndroidManifest
, add the required permission.
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
Add the intent filter.
<receiver android:name=".receiver.BootBroadcastReceiver">
<intent-filter>
<!-- get notified on reboot -->
<action android:name="android.intent.action.BOOT_COMPLETED" />
<!-- get notified on app updated -->
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
</intent-filter>
</receiver>
TIPS: Sometimes you'll find that the above method will not work. This is mostly not your fault. It is the phone's security settings that caused this, especially Mi phones. User will need to "trust" the app and enable "auto-start" in the phone settings, then everything should work just fine.