studio - servicio de notificaciones android
Android: mantener el servicio en ejecuciĆ³n cuando se elimina la aplicaciĆ³n (6)
En su servicio, agregue el siguiente código.
@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);
}
Quiero mantener un IntentService
ejecutándose en segundo plano, incluso cuando la aplicación está IntentService
. Y por "muerto" me refiero a presionar el botón de inicio durante mucho tiempo -> ver todas las aplicaciones en ejecución -> deslizar mi aplicación a un lado -> aplicación eliminada O presionar el botón Atrás durante mucho tiempo -> aplicación eliminada
Mi código es el siguiente. En mi MainActivity:
Intent intent = new Intent(this, MyService.class);
this.startService(intent);
En mi MyService:
public class MyService extends IntentService {
@Override
protected void onHandleIntent(Intent intent) {
System.out.println("MyService started");
run();
}
private void run() {
while (true){
System.out.println("MyService still running");
doSomething();
waitSomeTime();
}
}
}
Veo que el servicio se está ejecutando cuando la aplicación está abierta . Todavía se está ejecutando cuando minimizo la aplicación a través del botón de inicio. Todavía se está ejecutando cuando cierro la aplicación con el botón de retroceso. Pero se detendrá si lo mato como se mencionó anteriormente. ¿Cómo puedo solucionar esto?
La razón de esto es que está intentando usar un IntentService. Aquí está la línea de los documentos API
El IntentService hace lo siguiente:
Detiene el servicio después de que se hayan manejado todas las solicitudes de inicio, por lo que nunca tendrá que llamar a stopSelf ().
Por lo tanto, si desea que su servicio se ejecute indefinidamente, le sugiero que amplíe la clase de Servicio en su lugar. Sin embargo, esto no garantiza que su servicio se ejecute indefinidamente. Su servicio todavía tendrá la posibilidad de ser destruido por el kernel en un estado de poca memoria si es de baja prioridad. Así que tiene dos opciones:
1) Manténgalo funcionando en primer plano llamando al método startForeground()
.
2) Reinicie el servicio si muere. Aquí hay una parte del ejemplo de los documentos donde se habla de reiniciar el servicio después de que se cancele.
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
// For each start request, send a message to start a job and deliver the
// start ID so we know which request we''re stopping when we finish the job
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
mServiceHandler.sendMessage(msg);
// If we get killed, after returning from here, restart
return START_STICKY;
}
Puede usar android:stopWithTask="false"
en el manifiesto como se android:stopWithTask="false"
continuación. Esto significa que incluso si el usuario mata la aplicación al eliminarlo de la lista de tareas, su servicio no se detendrá.
<service android:name=".service.StickyService"
android:stopWithTask="false"/>
Si su aplicación inicia su Servicio, en realidad su servicio se está ejecutando en el proceso principal. por lo tanto, cuando la aplicación muere, el servicio también se detendrá. Entonces, lo que puede hacer es enviar la transmisión desde el método onTaskRemoved
de su servicio de la siguiente manera:
Intent intent = new Intent("com.android.ServiceStopped");
sendBroadcast(intent);
y tener un receptor de difusión que volverá a iniciar un servicio. Lo he intentado. El servicio se reinicia desde todo tipo de muertes.
el comando onstart interno pone START_STICKY
... Este servicio no se eliminará a menos que esté realizando demasiadas tareas y el núcleo quiera matarlo por ello ...
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i("LocalService", "Received start id " + startId + ": " + intent);
// We want this service to continue running until it is explicitly
// stopped, so return sticky.
return START_STICKY;
}
Todas las respuestas parecen correctas, así que seguiré y daré una respuesta completa aquí.
En primer lugar, la forma más sencilla de hacer lo que está intentando hacer es iniciar una difusión en Android cuando la aplicación se elimina manualmente, y definir un BroadcastReceiver
personalizado para activar un reinicio del servicio después de eso.
Ahora vamos a saltar en el código.
Crea tu Servicio en YourService.java
Tenga en cuenta el método onCreate()
, donde estamos iniciando un servicio de primer plano de manera diferente para versiones de Build superiores a Android Oreo . Esto se debe a las estrictas políticas de notificación introducidas recientemente en las que tenemos que definir nuestro propio canal de notificación para mostrarlas correctamente.
El this.sendBroadcast(broadcastIntent);
en el método onDestroy()
está la declaración que envía de forma asíncrona una difusión con el nombre de la acción "restartservice"
. Usaremos esto más adelante como un desencadenante para reiniciar nuestro servicio.
Aquí hemos definido una tarea de temporizador simple, que imprime un valor de contador cada 1 segundo en el Log
y se incrementa cada vez que se imprime.
public class YourService extends Service {
public int counter=0;
@Override
public void onCreate() {
super.onCreate();
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.O)
startMyOwnForeground();
else
startForeground(1, new Notification());
}
@RequiresApi(Build.VERSION_CODES.O)
private void startMyOwnForeground()
{
String NOTIFICATION_CHANNEL_ID = "example.permanence";
String channelName = "Background Service";
NotificationChannel chan = new NotificationChannel(NOTIFICATION_CHANNEL_ID, channelName, NotificationManager.IMPORTANCE_NONE);
chan.setLightColor(Color.BLUE);
chan.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
assert manager != null;
manager.createNotificationChannel(chan);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID);
Notification notification = notificationBuilder.setOngoing(true)
.setContentTitle("App is running in background")
.setPriority(NotificationManager.IMPORTANCE_MIN)
.setCategory(Notification.CATEGORY_SERVICE)
.build();
startForeground(2, notification);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
super.onStartCommand(intent, flags, startId);
startTimer();
return START_STICKY;
}
@Override
public void onDestroy() {
super.onDestroy();
stoptimertask();
Intent broadcastIntent = new Intent();
broadcastIntent.setAction("restartservice");
broadcastIntent.setClass(this, Restarter.class);
this.sendBroadcast(broadcastIntent);
}
private Timer timer;
private TimerTask timerTask;
public void startTimer() {
timer = new Timer();
timerTask = new TimerTask() {
public void run() {
Log.i("Count", "========= "+ (counter++));
}
};
timer.schedule(timerTask, 1000, 1000); //
}
public void stoptimertask() {
if (timer != null) {
timer.cancel();
timer = null;
}
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
Cree un receptor de difusión para responder a sus difusiones personalizadas definidas en Restarter.java
Ahora se supone que la transmisión con el nombre de la acción "restartservice"
que acaba de definir en YourService.java
un método que reiniciará su servicio . Esto se hace utilizando BroadcastReceiver
en Android.
onRecieve()
método onRecieve()
incorporado en BroadcastReceiver
para agregar la declaración que reiniciará el servicio. El servicio de startService()
no funcionará según lo previsto en y sobre Android Oreo 8.1, ya que las políticas de fondo estrictas terminarán pronto el servicio después del reinicio una vez que se elimine la aplicación. Por lo tanto, utilizamos startForegroundService()
para versiones superiores y mostramos una notificación continua para mantener el servicio en ejecución.
public class Restarter extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Log.i("Broadcast Listened", "Service tried to stop");
Toast.makeText(context, "Service restarted", Toast.LENGTH_SHORT).show();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(new Intent(context, YourService.class));
} else {
context.startService(new Intent(context, YourService.class));
}
}
}
Defina su MainActivity.java
para llamar al servicio en el inicio de la aplicación.
Aquí definimos un método isMyServiceRunning()
separado para verificar el estado actual del servicio en segundo plano. Si el servicio no se está ejecutando, lo iniciamos usando startService()
.
Dado que la aplicación ya se está ejecutando en primer plano, no necesitamos iniciar el servicio como un servicio en primer plano para evitar que se finalice.
Tenga en cuenta que en onDestroy()
estamos llamando stopService()
a stopService()
, para que se invoque nuestro método anulado . Si esto no se hubiera hecho, entonces el servicio habría finalizado automáticamente después de que la aplicación se onDestroy()
sin invocar nuestro método onDestroy()
modificado en YourService.java
public class MainActivity extends AppCompatActivity {
Intent mServiceIntent;
private YourService mYourService;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mYourService = new YourService();
mServiceIntent = new Intent(this, mYourService.getClass());
if (!isMyServiceRunning(mYourService.getClass())) {
startService(mServiceIntent);
}
}
private boolean isMyServiceRunning(Class<?> serviceClass) {
ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
if (serviceClass.getName().equals(service.service.getClassName())) {
Log.i ("Service status", "Running");
return true;
}
}
Log.i ("Service status", "Not running");
return false;
}
@Override
protected void onDestroy() {
stopService(mServiceIntent);
super.onDestroy();
}
}
Finalmente registralos en tu AndroidManifest.xml
Las tres clases anteriores deben registrarse por separado en AndroidManifest.xml
.
Tenga en cuenta que definimos un intent-filter
con el nombre de la acción como "restartservice"
en el que Restarter.java
está registrado como receiver
. Esto garantiza que se llame a nuestro BroadcastReciever
personalizado cada vez que el sistema encuentre una difusión con el nombre de acción dado.
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<receiver
android:name="Restarter"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="restartservice" />
</intent-filter>
</receiver>
<activity android:name="MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name="YourService"
android:enabled="true" >
</service>
</application>
Esto ahora debería reiniciar su servicio nuevamente si la aplicación se eliminó del administrador de tareas. Este servicio continuará ejecutándose en segundo plano siempre que el usuario no Force Stop
la Force Stop
la aplicación desde la Configuración de la aplicación .