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

前端 未结 6 1522
無奈伤痛
無奈伤痛 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条回答
  • 2021-02-05 09:47

    If you start a service in Application Class than your service will be always running even though if a user terminates or force stop from task manager, it will run again.

    To create service specifically in Android studio Right click on app from Project Explorer and then New > Service > Service

    Create a service:

    public class ServiceName extends Service {
    
        @Nullable
        @Override
        public IBinder onBind(Intent intent) {
        return null;
      }
    
        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
          // do your jobs here
          return super.onStartCommand(intent, flags, startId);
            }
        } 
    

    Now create an Application Class and start Service in Application Class

    public class App extends Application {
    
        @Override
        public void onCreate() {
            super.onCreate();
    
            startService(new Intent(this, ServiceName.class));
        }
    }  
    
    0 讨论(0)
  • 2021-02-05 09:52

    Displaying notifications is a common functionality of Android Services and there are already many questions on how to prevent Services from being stopped/or from force close.

    The relevant questions are listed below:

    Android: How to auto-restart application after it's been "force closed"?

    How to prevent android service getting killed (Service with notification)

    How to restart a service in android?

    Keep Service running

    Restart the service even if app is force-stopped and Keep running service in background even after closing the app How?

    To summarise them, the first step is to implement a BroadcastReceiver that listens for BOOT_COMPLETED. This will mean that your service becomes active when the Android device is turned on. To do this, you put the following code in the manifest:

    <receiver android:name=".MyBootCompletedReceiver">
        <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED"/>
        </intent-filter>
    </receiver>
    

    Make sure also to include the completed boot permission:

    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
    

    Then implement the BroadcastReceiver:

    public class MyBootCompletedReceiver extends BroadcastReceiver {
    
        @Override
        public void onReceive(Context context, Intent intent) {
            Intent myService = new Intent(context, MyLongRunningService.class);
            context.startService(myService);
        }
    }
    

    When you start the Service, you can return START_STICKY which will tell the Android OS to restart the service if it is squashed by the OS in conditions of limited resources:

    public class MyLongRunningService extends Service {
    
        @Override
        public void onCreate() {
            super.onCreate();
            //inject dependencies if necessary
        }
    
        public int onStartCommand(Intent intent, int flags, int startId) {
            //setup, then
            return START_STICKY;
        }
    }
    

    In general, Services launched by apps will be stopped by the Android OS when the device is low on resources. Returning START_STICKY tells the OS to restart the Service if it has to be stopped. Hence, by ensuring the Service is started when the device is turned on and by requesting it to be restarted if it is stopped under conditions of low resource, you have made a Service that runs constantly.

    This is the most basic pattern for ensuring your Service (and hence Application) is running all the time, which was your requirement.

    There are additional measures on top of that which you can take which are outlined in the other answers here, but they should be seen as a supplement to implementing a Service that starts on BOOT_COMPLETED and that requests restart if it is stopped in conditions of low resources.

    0 讨论(0)
  • 2021-02-05 09:53

    I see two options there.

    First one is to handle any exception via Thread.setDefaultUncaughtExceptionHandler() that is shipping with Java, it's not from Android SDK.

    To be concise, you make your custom Application class implement UncaughtExceptionHandler and register it as a listener to any exception in the application. As soon as some crash happens this callback will be fired from where you can delay a job to happen in near future (e.g. spawn AlarmManager to launch the app again in 50ms).


    The second option is that you can launch your components in different processes. From docs:

    android:process

    The name of the process where the service is to run. Normally, all components of an application run in the default process created for the application. It has the same name as the application package. The element's process attribute can set a different default for all components. But component can override the default with its own process attribute, allowing you to spread your application across multiple processes.

    A crash that happens on different process won't make the process that hosts your UI to crash.

    0 讨论(0)
  • 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.

    <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.

    0 讨论(0)
  • 2021-02-05 10:08

    Well for starters they have a job scheduler system that schedules jobs to executed, these jobs can be stopped , started or resumed. They implement a mechanism to detect crash, think java-script where if a application crashes it can be restarted (nodemon or forever) , in android there are services, which can be started or resume, There is this special service behavior to restart crashed services.

    START_REDELIVER_INTENT- tells the system to restart the service after the crash and also redeliver the intents that were present at the time of crash.

    The Android 5.0 Lollipop (API 21) release introduces a job scheduler API via the JobScheduler class. This API allows to batch jobs when the device has more resources available. In general this API can be used to schedule everything that is not time critical for the user.

    You can also mix Alarms, broadcast receivers and threading coupled with reactive programming to perform the job.Alarms (based on the AlarmManager class) give you a way to perform time-based operations outside the lifetime of your application. For example, you could use an alarm to initiate a long-running operation, such as starting a service once a day to download a weather forecast.

    You can attach observables to certain tasks performing specific operations, You can implement asynchronous i/o, computational operations, or even “infinite” streams of data by designing your own Observable.

    0 讨论(0)
  • 2021-02-05 10:13

    Just to add with Sagar Damani's answer, A service call is required to run your long operation in background but they also run in sepearate processes. When you declare a service or an activity in the androidMenifes.xml, also add the android:process="" attribute. Therefore, it will always be persistent if the user kills/force stop the activity.

    0 讨论(0)
提交回复
热议问题