java - google - proveedor de calendario android
Cómo el calendario de Google o Instagram hacen que sus aplicaciones funcionen todo el tiempo, incluso después de que se detenga la fuerza (6)
Bueno, para empezar, tienen un sistema de planificación de trabajos que programa los trabajos para que se ejecuten, estos trabajos se pueden detener, iniciar o reanudar. Implementan un mecanismo para detectar fallas, piense en el java-script donde, si una aplicación falla, se puede reiniciar (nodemon o para siempre), en android hay servicios que pueden iniciarse o reanudarse. Existe un comportamiento de servicio especial para reiniciar los servicios bloqueados. .
START_REDELIVER_INTENT- le dice al sistema que reinicie el servicio después de la falla y que vuelva a entregar los intentos que estaban presentes en el momento de la falla.
La versión de Android 5.0 Lollipop (API 21) introduce una API del programador de trabajos a través de la clase JobScheduler. Esta API permite realizar trabajos por lotes cuando el dispositivo tiene más recursos disponibles. En general, esta API se puede utilizar para programar todo lo que no es crítico para el usuario.
También puede combinar alarmas, receptores de difusión y subprocesos junto con programación reactiva para realizar el trabajo. Las alarmas (basadas en la clase AlarmManager) le permiten realizar operaciones basadas en el tiempo fuera de la vida útil de su aplicación. Por ejemplo, podría usar una alarma para iniciar una operación de larga duración, como iniciar un servicio una vez al día para descargar un pronóstico del tiempo.
Puede adjuntar observables a ciertas tareas que realizan operaciones específicas. Puede implementar i / o asíncronas, operaciones computacionales o incluso flujos de datos "infinitos" al diseñar su propio observable.
Parece que forzar la detención debería evitar que la aplicación se ejecute e incluso deshabilitar todas las alarmas de la aplicación. Sin embargo, encontré que la notificación en el calendario de Google todavía se muestra bien incluso después de la detención forzada, y todo el tiempo veo que se ejecuta la aplicación de Instagram, incluso cuando la mato, simplemente se reinicia automáticamente y está nuevamente allí.
Entonces, ¿cuál es una manera de hacer que la aplicación se ejecute constantemente? Estoy haciendo la aplicación con recordatorios y necesito mostrar notificaciones en un tiempo específico, sin importar cómo se cerró la aplicación anteriormente.
De acuerdo con mi conocimiento limitado, necesita usar AlarmManager
, WakefulBroadcastReceiver
y crear un Service
o IntentService
para todas estas tareas en segundo plano. Lea más sobre esto here . Tengo un servicio que utilicé para mi aplicación de mensajería Firebase y funciona bien incluso después de que el usuario eliminó la aplicación. En mi caso, me conecto a Firebase en un período fijo utilizando el siguiente servicio.
Primero, tengo la siguiente clase para configurar el 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);
}
}
Acabo de iniciar esto llamando al new FirebaseHandler(getApplicationContext()).setAlarm()
en algún lugar de la actividad. La clase AlarmReceiver
es la siguiente.
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);
}
}
La clase FirebaseAlarmService
es la siguiente.
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();
}
}
En resumen, FirebaseHandler
establece un AlarmManager
, que llamará a WakefulBroadcastReceiver
periódicamente, lo que iniciará un Service
periódicamente. Cuando AlarmManager
el servicio, el servicio iniciará nuevamente el AlarmManager
onTaskRemoved
.
En AndroidManifest
, deberá agregar el siguiente permiso para el bloqueo de activación.
<uses-permission android:name="android.permission.WAKE_LOCK" />
También recuerda añadir el receptor.
<receiver
android:name=".receiver.AlarmReceiver"
android:process=":remote" />
BONIFICACIÓN: es posible que desee iniciar el servicio una vez que se reinicie el teléfono o se actualice la aplicación a través de PlayStore. Crea otro 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);
}
}
En el AndroidManifest
, agregue el permiso requerido.
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
Añade el filtro de intención.
<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>
CONSEJOS: A veces encontrará que el método anterior no funcionará. En general, esto no es tu culpa. Es la configuración de seguridad del teléfono la que causó esto, especialmente los teléfonos Mi. El usuario deberá "confiar" en la aplicación y habilitar el "inicio automático" en la configuración del teléfono, entonces todo debería funcionar bien.
La visualización de notificaciones es una funcionalidad común de los Servicios de Android y ya hay muchas preguntas sobre cómo evitar que los Servicios se detengan o que se cierren.
Las preguntas relevantes se enumeran a continuación:
Android: ¿Cómo reiniciar automáticamente la aplicación después de que se haya "forzado a cerrar"?
Cómo evitar que el servicio de Android se mate (Servicio con notificación)
¿Cómo reiniciar un servicio en Android?
Mantener el servicio en funcionamiento
Para resumirlos, el primer paso es implementar un BroadcastReceiver
que escuche a BOOT_COMPLETED
. Esto significará que su servicio se activará cuando se encienda el dispositivo Android. Para hacer esto, pones el siguiente código en el manifiesto:
<receiver android:name=".MyBootCompletedReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>
Asegúrese también de incluir el permiso de arranque completado:
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
Luego implementa el BroadcastReceiver:
public class MyBootCompletedReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Intent myService = new Intent(context, MyLongRunningService.class);
context.startService(myService);
}
}
Cuando inicie el Servicio, puede devolver START_STICKY
que le indicará al sistema operativo Android que reinicie el servicio si el sistema operativo lo aplasta en condiciones de recursos limitados:
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;
}
}
En general, los servicios iniciados por aplicaciones serán detenidos por el sistema operativo Android cuando el dispositivo tenga pocos recursos. La devolución de START_STICKY
le indica al sistema operativo que reinicie el servicio si es necesario detenerlo. Por lo tanto, al garantizar que el Servicio se inicie cuando se encienda el dispositivo y al solicitar que se reinicie si se detiene en condiciones de bajos recursos, se ha creado un Servicio que se ejecuta constantemente.
Este es el patrón más básico para garantizar que su Servicio (y, por lo tanto, la Aplicación) se ejecute todo el tiempo, lo cual era su requisito.
Hay medidas adicionales además de lo que puede tomar que se describen en las otras respuestas aquí, pero deben considerarse como un complemento a la implementación de un Servicio que comienza en BOOT_COMPLETED
y que las solicitudes se reinician si se detiene en condiciones de bajos recursos. .
Si inicia un servicio en la Clase de Aplicación, su servicio siempre se ejecutará, aunque si un usuario termina o fuerza la detención del administrador de tareas, se ejecutará nuevamente.
Para crear un servicio específicamente en Android studio, haga clic con el botón derecho en la aplicación de Project Explorer y luego en Nuevo> Servicio> Servicio
Crear un servicio:
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);
}
}
Ahora cree una clase de aplicación e inicie el servicio en la clase de aplicación
public class App extends Application {
@Override
public void onCreate() {
super.onCreate();
startService(new Intent(this, ServiceName.class));
}
}
Solo para agregar con la respuesta de Sagar Damani, se requiere una llamada de servicio para ejecutar su operación larga en segundo plano, pero también se ejecutan en procesos separados. Cuando declare un servicio o una actividad en androidMenifes.xml, también agregue el atributo android: process = "". Por lo tanto, siempre será persistente si el usuario mata / fuerza la actividad.
Veo dos opciones allí.
La primera es manejar cualquier excepción a través de Thread.setDefaultUncaughtExceptionHandler()
que se envía con Java, no es del SDK de Android.
Para ser conciso, haga que su clase de Application
personalizada implemente UncaughtExceptionHandler
y la registre como oyente ante cualquier excepción en la aplicación. Tan pronto como ocurra un bloqueo, se devolverá la devolución de llamada desde donde puede retrasar un trabajo en el futuro cercano (por ejemplo, generar AlarmManager
para iniciar la aplicación nuevamente en 50 ms).
La segunda opción es que puede iniciar sus componentes en diferentes procesos. De los docs :
android: proceso
El nombre del proceso donde se va a ejecutar el servicio. Normalmente, todos los componentes de una aplicación se ejecutan en el proceso predeterminado creado para la aplicación. Tiene el mismo nombre que el paquete de la aplicación. El atributo de proceso del elemento puede establecer un valor predeterminado diferente para todos los componentes. Pero los componentes pueden anular el valor predeterminado con su propio atributo de proceso , lo que le permite distribuir su aplicación en múltiples procesos.
Un bloqueo que ocurre en un proceso diferente no hará que el proceso que hospeda su IU se bloquee.