How Google calendar or Instagram make their apps all the time up and running even after force stop

前端 未结 6 1630
無奈伤痛
無奈伤痛 2021-02-05 09:24

Looks like force stop should prevent app from running and it\'s even disable all app\'s alarms. However I found that notification in Google Calendar still shown fine even after

6条回答
  •  萌比男神i
    2021-02-05 09:59

    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.

    
    

    Also remember to add the receiver.

    
    

    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.

    
    

    Add the intent filter.

    
        
            
            
            
            
        
    
    

    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.

提交回复
热议问题