saber - Servicio de fondo que muere en Android
service android ejemplo (9)
Hemos desarrollado una aplicación de Android que implica un servicio en segundo plano. Para implementar este servicio en segundo plano hemos utilizado IntentService
. Queremos que la aplicación sondee el servidor cada 60 seconds
. Por lo tanto, en IntentService
, el servidor se sondea en un ciclo while. Al final del ciclo while hemos utilizado Thread.sleep(60000)
para que la siguiente iteración comience solo después de 60 segundos.
Pero en el Logcat
, veo que a veces le toma más de 5 minutos a la aplicación despertarse (salir de ese modo de suspensión y comenzar la siguiente iteración). Nunca es 1 minute
como queremos que sea.
¿Cuál es la razón para esto? ¿Deben implementarse los Servicios en segundo plano de una manera diferente?
Problema 2
Android mata este proceso en segundo plano (servicio intencionado) después de algún tiempo. No puedo decir exactamente cuándo. Pero a veces son horas y, a veces, días antes de que el servicio en segundo plano muera. Le agradecería que me dijera la razón de esto. Porque los Servicios no están destinados a ser asesinados. Están destinados a ejecutarse en segundo plano siempre que lo deseemos.
Código:
@Override
protected void onHandleIntent(Intent intent) {
boolean temp=true;
while(temp==true) {
try {
//connect to the server
//get the data and store it in the sqlite data base
}
catch(Exception e) {
Log.v("Exception", "in while loop : "+e.toString());
}
//Sleep for 60 seconds
Log.v("Sleeping", "Sleeping");
Thread.sleep(60000);
Log.v("Woke up", "Woke up");
//After this a value is extracted from a table
final Cursor cur=db.query("run_in_bg", null, null, null, null, null, null);
cur.moveToLast();
String present_value=cur.getString(0);
if(present_value==null) {
//Do nothing, let the while loop continue
}
else if( present_value.equals("false") || present_value.equals("False") ) {
//break out of the while loop
db.close();
temp=false;
Log.v("run_in_bg", "false");
Log.v("run_in_bg", "exiting while loop");
break;
}
}
}
Pero cada vez que se mata el servicio, sucede cuando el proceso está dormido. El último registro dice: Sleeping : Sleeping
. ¿Por qué se mata el servicio?
Android es bastante bueno para matar servicios de larga duración. He encontrado que WakefulIntentService de CommonsWare es útil en mi aplicación: https://github.com/commonsguy/cwac-wakeful
Le permite especificar un intervalo de tiempo mientras intenta hacerlo durmiendo.
El principal problema es que no podemos decir
Los servicios no están destinados a ser asesinados. Están destinados a ejecutarse en segundo plano siempre que lo deseemos.
Básicamente, eso no es verdad. El sistema aún puede terminar el servicio en poca memoria y posiblemente en otras situaciones. Hay 2 formas de superar esto:
- Si está implementando el servicio, anule
onStartCommand()
y devuelvaSTART_STICKY
como resultado. Le indicará al sistema que incluso si desea cancelar su servicio debido a la poca memoria, debería volver a crearlo tan pronto como la memoria vuelva a ser normal. - Si no está seguro de que la primera aproximación funcionará, tendrá que utilizar AlarmManager http://developer.android.com/reference/android/app/AlarmManager.html . Ese es un servicio del sistema, que ejecutará acciones cuando usted lo indique, por ejemplo, periódicamente. Eso asegurará que si su servicio terminará, o incluso todo el proceso morirá (por ejemplo, con el cierre forzado), AlarmManager lo reiniciará al 100%.
Buena suerte
Los servicios son asesinados. Al igual que la aplicación se mata. Es filosofía de Android que puedes matarte en cualquier momento.
Deberías, como otros escribieron, no asumir que tu fondo funciona para siempre.
Pero puede utilizar un servicio de tierra para reducir drásticamente la posibilidad de que lo maten / reinicien. Tenga en cuenta que esto obliga a una notificación que siempre está visible. Por ejemplo, reproductores de música, aplicaciones vpn y sportstracker usan esta API.
Para el problema 1, de Java vainilla, Thread.Sleep()
está garantizado para reactivar el hilo después de que el temporizador haya expirado, pero no exactamente después de que haya expirado, puede ser más tarde dependiendo principalmente de los estados de otros hilos, prioridad, etc. ; así que si duermes tu hilo un segundo, dormirá al menos un segundo, pero puede ser 10 dependiendo de muchos factores, no soy muy versado en el desarrollo de Android, pero estoy bastante seguro de que es la misma situación .
Para el problema 2, los servicios pueden ser eliminados cuando la memoria baja o manualmente por el usuario, por lo que otros han señalado probablemente usando AlarmManager para reiniciar su servicio después de un cierto tiempo lo ayudará a tenerlo funcionando todo el tiempo.
Puede usar ScheduledExecutorService diseñado específicamente para tal fin.
No utilice Temporizadores, como se muestra en "Concurrencia de Java en la práctica" , pueden ser muy inexactos.
Una mejor solución sería hacer que un AlarmManager suene cada 60 segundos. Este AlarmManager luego inicia el servicio que sondea el servidor, luego el servicio inicia un nuevo AlarmManager, es una solución recursiva que funciona bastante bien.
Esta solución será más confiable ya que no tiene la amenaza de que el sistema operativo Android mate su servicio que se cierne sobre usted. Según la API: el Administrador de alarmas está diseñado para casos en los que desee que su código de aplicación se ejecute en un momento específico, incluso si su aplicación no se está ejecutando actualmente.
En su UI / actividad principal, etc., configure este temporizador para que se apague en 60 segundos:
long ct = System.currentTimeMillis(); //get current time
AlarmManager mgr=(AlarmManager)getApplicationContext().getSystemService(Context.ALARM_SERVICE);
Intent i= new Intent(getApplicationContext(), yourservice.class);
PendingIntent pi=PendingIntent.getService(getApplicationContext(), 0, i, 0);
mgr.set(AlarmManager.RTC_WAKEUP, ct + 60000 , pi); //60 seconds is 60000 milliseconds
En yourservice.class
puedes tener esto, comprueba el estado de la conexión, si es bueno establece el temporizador para que se apague en otros 60 segundos:
public class yourservice extends IntentService {
public yourservice() { //needs this constructor
super("server checker");
}
@Override
protected void onHandleIntent(Intent intent) {
WifiManager wificheck = (WifiManager) this.getSystemService(Context.WIFI_SERVICE);
if(check for a certain condition your app needs etc){
//could check connection state here and stop if needed etc
stopSelf(); //stop service
}
else{ //poll the server again in 60 seconds
long ct = System.currentTimeMillis();
AlarmManager mgr=(AlarmManager)getApplicationContext().getSystemService(Context.ALARM_SERVICE);
Intent i= new Intent(getApplicationContext(), yourservice.class);
PendingIntent pi=PendingIntent.getService(getApplicationContext(), 0, i, 0);
mgr.set(AlarmManager.RTC_WAKEUP, ct + 60000 , pi);
stopSelf(); //stop service since its no longer needed and new alarm is set
}
}
}
IntentService
no está diseñado para seguir ejecutándose en un bucle while. La idea es reaccionar ante una Intent
, hacer un procesamiento y detener el servicio una vez hecho.
Eso no significa que no esté funcionando y no puedo decirle por qué ve demoras tan largas, pero la solución más limpia es usar alguna fuente externa para pinchar el servicio periódicamente. Además de los métodos Java vainilla, también puedes echar un vistazo al http://developer.android.com/reference/android/app/AlarmManager.html o un Handler
como se menciona en la documentación de AlarmManager
.
El modo Handler
funcionaría así.
public class TriggerActivity extends Activity implements Handler.Callback {
// repeat task every 60 seconds
private static final long REPEAT_TIME = 60 * 1000;
// define a message id
private static final int MSG_REPEAT = 42;
private Handler mHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mHandler = new Handler(this);
}
@Override
protected void onStart() {
super.onStart();
// start cycle immediately
mHandler.sendEmptyMessage(MSG_REPEAT);
}
@Override
protected void onStop() {
super.onStop();
// stop cycle
mHandler.removeMessages(MSG_REPEAT);
}
@Override
protected void onDestroy() {
super.onDestroy();
mHandler = null;
}
@Override
public boolean handleMessage(Message msg) {
// enqueue next cycle
mHandler.sendEmptyMessageDelayed(MSG_REPEAT, REPEAT_TIME);
// then trigger something
triggerAction();
return true;
}
private void triggerAction() {
// trigger the service
Intent serviceIntent = new Intent(this, MyService.class);
serviceIntent.setAction("com.test.intent.OPTIONAL_ACTION");
startService(serviceIntent);
}
}
Una Activity
simple (que podría ampliarse para tener esa funcionalidad en todas sus actividades) que se envía un Message
todo el tiempo mientras se está ejecutando (aquí entre onStart
y onStop
)
IntentService
que debería usar un Service
lugar de un Service
de IntentService
pero si desea usar un servicio de IntentService
y hacer que se ejecute cada 60 segundos, debe usar el AlarmManager
lugar de simplemente decirle al Thread
que se IntentService
dormido. IntentService
quiere parar, dejarlo y AlarmManager
que AlarmManager
lo AlarmManager
cuando debería ejecutarse de nuevo.
It could be probably for two reasons..
- O bien el ciclo while crea un problema, hace que el controlador funcione hasta
temp==true
A esto se le agregan hilos, lo que crea
long delays
hasta6 seconds
. En el caso, el sistema está trabajando para una gran base de datos, creandolong delays
entre cada consulta se agregará a la memoria del sistema. Cuando el uso de la memoria para la aplicación se vuelve tan grande que lasystem memory gets low
delsystem memory gets low
, el sistema debeterminate the process
.Solución para el problema ...
Puede reemplazar arriba con
Alarm Manager
para revocar los servicios del sistema después de un intervalo de tiempo particular usando Alarm Manager.- Además, para recuperar la intención después de que el sistema recupere la aplicación de la finalización, debe usar
START_REDELIVER_INTENT
. Es para recuperar su último intento de trabajo después de que finalice la aplicación. para su uso, estudie https://developer.android.com/reference/android/app/Service.html#START_REDELIVER_INTENT