JavascriptProva

domenica 19 giugno 2016

Studio dei due Service che si rincorrono

Mi devo esercitare meglio per perfezionare il meccanismo.

Con Services semplici.

Riscriviamo Alfa e Beta.


Alfa:
public class Alfa extends Service{

 @Override
 public IBinder onBind(Intent intent) {
  // TODO Auto-generated method stub
  return null;
 }
 
}

Beta:
public class Beta extends Service{

 @Override
 public IBinder onBind(Intent intent) {
  // TODO Auto-generated method stub
  return null;
 }
 
}
Aggiungo ad ambedue onStartCommand e onDestroy:

ALfa:
public class Alfa extends Service{
 
 @Override
 public int onStartCommand(Intent intent, int flags, int startUI){
  return Service.START_NOT_STICKY;
 }
 
 @Override
 public void onDestroy(){
  super.onDestroy();
 }

 @Override
 public IBinder onBind(Intent intent) {
  // TODO Auto-generated method stub
  return null;
 }
 
}

Beta:
public class Beta extends Service{

 @Override
 public int onStartCommand(Intent intent, int flags, int startUI){
  return Service.START_NOT_STICKY; 
 }
 
 @Override
 public void onDestroy(){
  super.onDestroy();
 }
 
 @Override
 public IBinder onBind(Intent intent) {
  // TODO Auto-generated method stub
  return null;
 }
 
}


Ora faccio eseguire a Beta un compito, come scrivere qualcosa in LogCat:
 @Override
 public int onStartCommand(Intent intent, int flags, int startUI){
  Log.v("BETA","COMPITO ESEGUITO");
  stopSelf();
  return Service.START_NOT_STICKY; 
 }
Alfa può evocare Beta e poi distruggersi.
Anziché farlo in onStartCommand, per concentrare tutto, può farlo in onDestroy, se in onStartCommand inserisco stopSelf.
public class Alfa extends Service{
 
 @Override
 public int onStartCommand(Intent intent, int flags, int startUI){
  stopSelf()
  return Service.START_NOT_STICKY;
 }
 
 @Override
 public void onDestroy(){
  startService(new Intent(this,Beta.class));
  super.onDestroy();
 }

 @Override
 public IBinder onBind(Intent intent) {
  // TODO Auto-generated method stub
  return null;
 }
 
}
Mettiamo gli indici ai metodi e proviamo...
public class Alfa extends Service{
 
 @Override
 public int onStartCommand(Intent intent, int flags, int startUI){
  Log.v("ALFA","ONSTARTCOMMAND");
  stopSelf();
  return Service.START_NOT_STICKY;
 }
 
 @Override
 public void onDestroy(){
  Log.v("ALFA","ONDESTROY");
  startService(new Intent(this,Beta.class));
  super.onDestroy();
 }

 @Override
 public IBinder onBind(Intent intent) {
  // TODO Auto-generated method stub
  return null;
 }
 
}
Proviamo... Bene.
Mettiamo tutti i markers e riproviamo.
public class Alfa extends Service{
 
 @Override
 public int onStartCommand(Intent intent, int flags, int startUI){
  Log.v("ALFA","ONSTARTCOMMAND");
  stopSelf();
  Log.v("ALFA","/ ONSTARTCOMMAND");
  return Service.START_NOT_STICKY;
 }
 
 @Override
 public void onDestroy(){
  Log.v("ALFA","ONDESTROY");
  startService(new Intent(this,Beta.class));
  Log.v("ALFA","/ ONDESTROY");
  super.onDestroy();
 }

 @Override
 public IBinder onBind(Intent intent) {
  // TODO Auto-generated method stub
  return null;
 }
 
}
public class Beta extends Service{

 @Override
 public int onStartCommand(Intent intent, int flags, int startUI){
  Log.v("BETA","ONSTARTCOMMAND");
  Log.e("BETA","Esecuzione del compito stabilito");
  stopSelf();
  Log.v("BETA","/ ONSTARTCOMMAND");
  return Service.START_NOT_STICKY; 
 }
 
 @Override
 public void onDestroy(){
  Log.v("BETA","ONDESTROY");
  Log.v("BETA","/ ONDESTROY");
  super.onDestroy();
 }
 
 @Override
 public IBinder onBind(Intent intent) {
  // TODO Auto-generated method stub
  return null;
 }
 
}
Ed ecco la sequenza:
06-19 18:51:02.222: V/ALFA(29067): ONSTARTCOMMAND
06-19 18:51:02.222: V/ALFA(29067): / ONSTARTCOMMAND
06-19 18:51:02.242: V/ALFA(29067): ONDESTROY
06-19 18:51:02.252: V/ALFA(29067): / ONDESTROY
06-19 18:51:02.252: V/BETA(29067): ONSTARTCOMMAND
06-19 18:51:02.252: E/BETA(29067): Esecuzione del compito stabilito
06-19 18:51:02.262: V/BETA(29067): / ONSTARTCOMMAND
06-19 18:51:02.262: V/BETA(29067): ONDESTROY
06-19 18:51:02.262: V/BETA(29067): / ONDESTROY
Ora, se in onDestroy di Beta metto un richiamo ad Alfa creo un circolo vizioso:
 public void onDestroy(){
  Log.v("BETA","ONDESTROY");
  startService(new Intent(this,Alfa.class));
  Log.v("BETA","/ ONDESTROY");
  super.onDestroy();
 }
Dovrò riavviare il cellulare...


Qual è il modo migliore per interrompere il circolo vizioso infinito?

Promemoria codice dei due processi che si mordono la coda

Dunque lo scheletro dell'applicazione a due Services alternantisi è essenzialmente che in onStartCommand ogni Service apre l'altro e chiude se stesso.
Facile.
Provo ad applicarlo al Service che parla...

Qui conviene aspettare che si arrivi a onUtteranceCompletedListener per aprire l'altro e chiudere se stesso, in modo da dare il tempo al Service di parlare.
Ci provo.
Questo service (ne ho creato una copia denominata Voce) non overrida onStartCommand, ma onCreate, che chiama onInit del TextToSpeech, implementata dal service stesso.
Mi conviene piuttosto attendere che abbia finito di parlare per richiamare Alfa, quindi lo start con relativo stop di se stesso lo piazzo in onUtteranceCompleted.
 @Override
 public void onUtteranceCompleted(String utteranceId) {
  Intent intent=new Intent(this,Alfa.class);
  startService(intent);
  stopSelf();
  Log.v("SEGNALE","ONUTTERANCE");
  
 }
Alfa dovrebbe comportarsi come al solito, richiamando Voce con ritardo secondo AlarmManager.
Proviamo...

Dimenticavo la condizione relativa alla variabile globale:
 @Override
 public void onUtteranceCompleted(String utteranceId) {
  if(Globals.stop==false){
   Intent intent=new Intent(this,Alfa.class);
   startService(intent);
   stopSelf();
  }
  Log.v("SEGNALE","ONUTTERANCE");
  
 }
e andiamo dopo aver cambiato, in Alfa, Beta con Voce...

Sì, funziona!


Ora devo stabilire i tempi casuali.

L'ho fatto...
Non riesco a cancellare subito l'AlarmManager... Intanto mi annoto il codice:
public class MainActivity extends Activity {

 Button button;
 Button bttStart;
 Intent intent;
 AlarmManager alarmManager;
 PendingIntent pendingIntent;
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  
  

  
  button=(Button)findViewById(R.id.button1);
  bttStart=(Button)findViewById(R.id.button2);
  
  button.setOnClickListener(new View.OnClickListener() {
   
   @Override
   public void onClick(View v) {
    Globals.stop=true;
   }
  });
  
  bttStart.setOnClickListener(new View.OnClickListener() {
   
   @Override
   public void onClick(View v) {
    Globals.stop=false;
    intent=new Intent(getApplicationContext(),Alfa.class); 
    startService(intent);
    
   }
  });

  
 }
}


Alfa;
public class Alfa extends Service{

 AlarmManager alarmManager;
 PendingIntent pendingIntent;
 @Override
 public IBinder onBind(Intent intent) {
  // TODO Auto-generated method stub
  return null;
 }
 
 @Override
 public int onStartCommand(Intent intent,int flags,int startUI){
  Log.v("ALFA","ONSTARTCOMMAND");
  if (Globals.stop==false){
   Intent i=new Intent(this,Voce.class);
   pendingIntent=PendingIntent.getService(this,0,i,0);
   Log.d("MSECS",""+Globals.mSecs);
   alarmManager=(AlarmManager)this.getSystemService(Context.ALARM_SERVICE);
   alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime()+Globals.mSecs, pendingIntent);
   stopSelf();
  }
  else{
   alarmManager.cancel(pendingIntent);
   stopSelf();
   stopService(new Intent(getApplicationContext(),Voce.class));
  }
  return Service.START_NOT_STICKY;
  
 }
 @Override
 public void onDestroy(){
  Log.v("ALFA","ONDESTROY");
  super.onDestroy();
  
 }

}


Voce:
public class Voce extends Service implements TextToSpeech.OnInitListener,TextToSpeech.OnUtteranceCompletedListener{
 TextToSpeech tts;

 @Override
 public void onCreate(){
  tts=new TextToSpeech(this,this);
  Log.v("SEGNALE", "ONCREATE");
 }
 @Override
 public IBinder onBind(Intent intent) {
  // TODO Auto-generated method stub
  return null;
 }

 @Override
 public void onInit(int status) {
  if(status==TextToSpeech.SUCCESS){
   int result=tts.setLanguage(Locale.ITALIAN);
   tts.setOnUtteranceCompletedListener(this);
   HashMap hashMap=new HashMap();
   hashMap.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, "frase");
   tts.setPitch(.5f);
   int tempo=Globals.mSecs/1000; //tempo in secondi
   int minuti=tempo/60;
   int secondi=tempo%60;
   tts.speak("Sono passati "+minuti+" minuti e"+secondi+"secondi", TextToSpeech.QUEUE_FLUSH, hashMap);
  }
  Log.v("SEGNALE","ONINIT");
  
 }

 @Override
 public void onUtteranceCompleted(String utteranceId) {
  Log.v("VARIABILE GLOBALE",""+Globals.mSecs);
  if(Globals.stop==false){
   Globals.mSecs=(int) (Math.random()*3*60*1000);
   Intent intent=new Intent(this,Alfa.class);
   startService(intent);
   stopSelf();
  }
  Log.v("SEGNALE","ONUTTERANCE");
  
 }
 
 @Override
 public void onDestroy(){
  tts.stop();
  tts.shutdown();
  super.onDestroy();
  Log.v("SEGNALE","ONDESTROY");
 }
 
}


sabato 18 giugno 2016

Due Services che si mordono la coda (con il dovuto ritardo)

Sto facendo tentativi per ottenere ogni volta un intervallo casuale fra le ripetizioni di un Service.

Sto pensando, in proposito, a un sistema di due Services che si chiamino l'uno con l'altro.
Uno chiama l'altro che esegue il compito e si distrugge, quindi questo esegue il compito, si distrugge e in quel momento richiama il primo.
Proviamo con due Services nuovi...

Sì, ci sono riuscito.
Ecco i codici:

MainActivity:
public class MainActivity extends Activity {

 Button button;
 Button bttStart;
 Intent intent;
 AlarmManager alarmManager;
 PendingIntent pendingIntent;
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  
  

  
  button=(Button)findViewById(R.id.button1);
  bttStart=(Button)findViewById(R.id.button2);
  
  button.setOnClickListener(new View.OnClickListener() {
   
   @Override
   public void onClick(View v) {
    Globals.stop=true;
   }
  });
  
  bttStart.setOnClickListener(new View.OnClickListener() {
   
   @Override
   public void onClick(View v) {
    Globals.stop=false;
    intent=new Intent(getApplicationContext(),Alfa.class); 
    startService(intent);
    
   }
  });

  
 }
}


Alfa:
public class Alfa extends Service{

 @Override
 public IBinder onBind(Intent intent) {
  // TODO Auto-generated method stub
  return null;
 }
 
 @Override
 public int onStartCommand(Intent intent,int flags,int startUI){
  Log.v("ALFA","ONSTARTCOMMAND");
  if (Globals.stop==false){
   Intent i=new Intent(this,Beta.class);
   PendingIntent pendingIntent=PendingIntent.getService(this,0,i,0);
   AlarmManager alarmManager=(AlarmManager)this.getSystemService(Context.ALARM_SERVICE);
   alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime()+10*1000, pendingIntent);
   stopSelf();
  }
  return Service.START_NOT_STICKY;
  
 }
        @Override
 public void onDestroy(){
  Log.v("ALFA","ONDESTROY");
  super.onDestroy();
  
 }

}


Beta:
public class Beta extends Service{

 @Override
 public IBinder onBind(Intent intent) {
  // TODO Auto-generated method stub
  return null;
 }

 @Override
 public int onStartCommand(Intent intent, int flags,int startUI){
  Log.v("BETA","ONSTARTCOMMAND");
  if(Globals.stop==false){
   Intent i=new Intent(this,Alfa.class);
   startService(i);
   stopSelf();
  }
  return Service.START_NOT_STICKY;
  
 }
 @Override
 public void onDestroy(){
  Log.v("BETA","ONDESTROY");
  super.onDestroy();
  
 }

}
In più è dichiarata una variabile globale:
public class Globals {
 public static boolean stop=false;
}



Quali sono i princìpi:
MainActivity alla pressione di un bottone chiama Alfa:
  bttStart.setOnClickListener(new View.OnClickListener() {
   
   @Override
   public void onClick(View v) {
    Globals.stop=false;
    intent=new Intent(getApplicationContext(),Alfa.class); 
    startService(intent);
    
   }
  })


Alfa all'avvio, ossia in onStartCommand, chiama Beta e stoppa se stesso con un ritardo stabilito da AlarmManager:
 @Override
 public int onStartCommand(Intent intent,int flags,int startUI){
  Log.v("ALFA","ONSTARTCOMMAND");
  if (Globals.stop==false){
   Intent i=new Intent(this,Beta.class);
   PendingIntent pendingIntent=PendingIntent.getService(this,0,i,0);
   AlarmManager alarmManager=(AlarmManager)this.getSystemService(Context.ALARM_SERVICE);
   alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime()+10*1000, pendingIntent);
   stopSelf();
  }
  return Service.START_NOT_STICKY;
  
 }


Beta all'avvio, ossia in onStartCommand, chiama Alfa e stoppa se stesso:
@Override
 public int onStartCommand(Intent intent, int flags,int startUI){
  Log.v("BETA","ONSTARTCOMMAND");
  if(Globals.stop==false){
   Intent i=new Intent(this,Alfa.class);
   startService(i);
   stopSelf();
  }
  return Service.START_NOT_STICKY;
  
 }
Alfa, attivato, richiama Beta (sempre con il ritardo di AlarmManager) e stoppa se stesso, e così via in un loop senza fine.

Per stoppare il loop abbiamo una variabile globale booleana Globals.Variabile che inizialmente è false e appunto per andare avanti ad aprirsi a vicenda fra Alfa e Beta è impostata la condizione che questa variabile sia false: nel bottone 2 di MainActivity c'è il codice per impostare questa variabile a true:
  button.setOnClickListener(new View.OnClickListener() {
   
   @Override
   public void onClick(View v) {
    Globals.stop=true;
   }
  });
e questo, facendo venire meno la condizione per l'apertura reciproca dei due Services, interrompe efficacamente il processo.

L'ho idato io: ingegnoso!

AlarmManager gestito da un Service intermedio.

Creare un AlarmManager nel contesto di un Service in modo da chiamare un altro Service a intervalli.

Ecco fatto:

MainActivity:
public class MainActivity extends Activity {

 boolean flag=true;
 Button button;
 Button bttStart;
 Intent intent;
 AlarmManager alarmManager;
 PendingIntent pendingIntent;
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  
  

  
  button=(Button)findViewById(R.id.button1);
  bttStart=(Button)findViewById(R.id.button2);
  
  button.setOnClickListener(new View.OnClickListener() {
   
   @Override
   public void onClick(View v) {
    stopService(new Intent(getApplicationContext(),BaseAlarm.class));
   }
  });
  
  bttStart.setOnClickListener(new View.OnClickListener() {
   
   @Override
   public void onClick(View v) {
    intent=new Intent(getApplicationContext(),BaseAlarm.class); 
    startService(intent);
    
   }
  });

  
 }
}


BaseAlarm (service):
public class BaseAlarm extends Service{
 Intent intent;
 PendingIntent pendingIntent;
 AlarmManager alarmManager;
 int mSecs=5*1000;
 @Override
 public IBinder onBind(Intent intent) {
  // TODO Auto-generated method stub
  return null;
 }
 
 @Override
 public int onStartCommand(Intent intent, int flags, int startUI){
  Log.v("BASEALARM","ONSTARTCOMMAND");
  intent=new Intent(this,Servizio.class);
  pendingIntent=PendingIntent.getService(this, 0, intent, 0);
  alarmManager=(AlarmManager)this.getSystemService(Context.ALARM_SERVICE);
  alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, 
          0, 
          mSecs, 
          pendingIntent);
  return Service.START_NOT_STICKY;
  
 }
 public void onDestroy(){
  alarmManager.cancel(pendingIntent);
  
   stopService(new Intent(getApplicationContext(),Servizio.class));
  
  
  super.onDestroy();
  Log.v("BASEALARM","ONDESTROY");
  
 }

}


Servizio (service):
public class Servizio extends Service implements TextToSpeech.OnInitListener,TextToSpeech.OnUtteranceCompletedListener{
 TextToSpeech tts;

 @Override
 public void onCreate(){
  tts=new TextToSpeech(this,this);
  Log.v("SEGNALE", "ONCREATE");
 }
 @Override
 public IBinder onBind(Intent intent) {
  // TODO Auto-generated method stub
  return null;
 }

 @Override
 public void onInit(int status) {
  if(status==TextToSpeech.SUCCESS){
   int result=tts.setLanguage(Locale.ITALIAN);
   tts.setOnUtteranceCompletedListener(this);
   HashMap hashMap=new HashMap();
   hashMap.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, "frase");

   tts.speak("Ciao, birichino.", TextToSpeech.QUEUE_FLUSH, hashMap);
  }
  Log.v("SEGNALE","ONINIT");
  
 }

 @Override
 public void onUtteranceCompleted(String utteranceId) {
  try{
   tts.stop();
   tts.shutdown();
   stopSelf();
  }catch(Exception e){}
  Log.v("SEGNALE","ONUTTERANCE");
  
 }
 
 @Override
 public void onDestroy(){
  super.onDestroy();
  Log.v("SEGNALE","ONDESTROY");
 }
 
}
Ho dovuto aggiungere un try...catch all'evento onDestroy di Servizio perché certamente la chiusura del TTS impiega del tempo per cui lo stopSelf veniva eseguito probabilmente in anticipo.

Azione ripetitiva con Alarm.setInexactRepeating e cancellazione di un Alarm

Vediamo sulla documentazione setInexactRepeating di AlarmManager.
Il secondo parametro si chiama triggerAtMillis e il secondo intervalMillis.
Che significano?

Ecco: il primo dovrebbe essere il tempo intercorso prima della prima ripetizione, e l'altro il tempo fra le ripetizioni.
Facciamo pochi secondi...

alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, 1000*20, 1000*5, pendingIntent);
Vediamo... No... appare immediatamente per poi andare a intervalli.

Ma se la proprietà AlarmManager.INTERVAL_HALF_HOUR comprendesse SystemClock.elapsedRealtime sommata di mezz'ora?

Riproviamo con questa sintassi:
  alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, 
           SystemClock.elapsedRealtime()+1000*40, 
           1000*5, 
           pendingIntent);
Sì, adesso funziona.

Solo che adesso il mio Service va mettendo toast a tempo indeterminato.
Come lo stoppiamo?
Ci aggiungo questo nel codice di MainActivity:
 public void onDestroy(){
  super.onDestroy();
  stopService(new Intent(this,Servizio2.class));
 }
...che non funziona! Provo a metterlo in un onClickListener del button, ma non funziona lo stesso.
Dopo un po' di tribolazioni ho avuto l'intuizione che devo stoppare l'AlarmManager prima di stoppare il Service!

Proviamo:
  button.setOnClickListener(new View.OnClickListener() {
   
   @Override
   public void onClick(View v) {
    alarmManager.cancel(pendingIntent);
    
   }
  });
Vediamo se la smette di lampeggiare!

E' riuscito!
Per completezza devo chiudere anche il Service:
  button.setOnClickListener(new View.OnClickListener() {
   
   @Override
   public void onClick(View v) {
    alarmManager.cancel(pendingIntent);
    stopService(new Intent(getApplicationContext(),Servizio2.class));
   }
  });
e ne voglio una conferma mettendo un segnale nell'evento onDestroy del Service:
 @Override
 public void onDestroy(){
  super.onDestroy();
  Log.v("SEGNALE","ONDESTROY");
 }
Ecco:
06-18 18:07:02.445: V/SEGNALE(14864): ONDESTROY

venerdì 17 giugno 2016

Chiamare un Service dopo un intervallo prestabilito di tempo, una sola volta

Adesso mi esercito con AlarmManager.
Ovviamente, prima di farlo con il TTS, meglio usare dei semplici Toast o dei Log in LogCat.


Creiamo un'app che mostri un Toast ogni 30 secondi.

Provo a creare un AlarmManager secondo quel poco che ricordo, certamente sbagliando...
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		AlarmManager alarmManager=(AlarmManager)this.getSystemService(Context.ALARM_SERVICE);
		alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,)
...che è incompleto: il parametro successivo presente sul tutorial è SystemClock.elapsedRealtime() sommato a qualcosa...
Voglio vedere, criticamente, cosa è questo SystemClock.elapsedRealtime().

Dunque "commentizzo" il codice incompleto di cui sopra e me lo metto in LogCat.
		Log.v("SystemClock.elapsedRealtime",""+SystemClock.elapsedRealtime());
		
		/*AlarmManager alarmManager=(AlarmManager)this.getSystemService(Context.ALARM_SERVICE);
		alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,)*/
E vediamo...

Ho ottenuto questo valore:
06-18 01:08:21.163: V/SystemClock.elapsedRealtime(19020): 8482000
Sospetto che si tratti di un valore in millisecondi: lo converto in ore...
8482 secondi,
141 minuti e 22 secondi
2 ore, 21 minuti e 22 secondi.

Dovrebbe essere il tempo passato dall'accensione del cellulare...

Ora per fare una prova lo spengo e lo riaccendo, e vediamo...

06-18 01:34:22.074: V/SystemClock.elapsedRealtime(9256): 661888
conversione: 11 min, 1 sec, 888 msec.
Ci sta!

Ora, mentre si esegue l'App viene stabilito un tempo passato dall'accensione del cellulare e viene stabilito che dopo un tot di millisecondi si deve verificare un dato evento.
Riprendiamo il codice...

Eccolo, completato con intent e pendingIntent:
public class MainActivity extends Activity {

	Button button;
	Intent intent;
	PendingIntent pendingIntent;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
	
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		intent=new Intent(this,Servizio2.class);
		pendingIntent=PendingIntent.getService(this, 0, intent, 0);
		AlarmManager alarmManager=(AlarmManager)this.getSystemService(Context.ALARM_SERVICE);
		alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,SystemClock.elapsedRealtime()+1000*60,pendingIntent);
		
	}
}
Vediamo se funziona: dovrebbe scattare il Toast evocato dal Service dopo 60 secondi.

E non scatta!
Ovviamente, mi pareva strano che non ci fosse un errore...
non ho registrato il Service sul Manifest!.
Non funziona nemmeno con il semplice startService(intent).
Rimedio...
    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <service android:name="Servizio"
            android:exported="true" />
        <service android:name="Servizio2" />
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name" > 
Adesso vediamo...

Ora il Toast appare, ma appare subito, non dopo il tempo voluto.
Provo con il LogCat...

Appare subito e dopo un tempo congruente con i 60 secondi. Perché?

Perché sono cretino io!. Avevo lasciato startService(intent), e quindi appariva subito grazie a questo!

Ora rifaccio tutto (dopo soli 10 secondi perché sembra che il tempo non passi mai). Sì! Funziona!

Creazione di un Service con tts (definitiva)

Ho ancora bisogno di esercitarmi sul ciclo vitale dei Services. Costruisco un Service...

Dopo la prima costruzione guidata da Eclipse ho questo:
public class Servizio extends Service{

	@Override
	public IBinder onBind(Intent intent) {
		// TODO Auto-generated method stub
		return null;
	}
	
}
Gli eventi che voglio esplorare sono onCreate, onStartCommand e onDestroy.
Li inserisco.
public class Servizio extends Service{
	
	@Override
	public void onCreate(){
		super.onCreate();
		Log.v("SEGNALE", "ONCREATE");
	}
	
	@Override
	public int onStartCommand(Intent intent, int flags, int startUI){
		Log.v("SEGNALE", "ONSTARTCOMMAND");
		return startUI;
		
	}
	
	@Override
	public void onDestroy(){
		super.onDestroy();
		Log.v("SEGNALE", "ONDESTROY");
	}

	@Override
	public IBinder onBind(Intent intent) {
		// TODO Auto-generated method stub
		return null;
	}
	
}
E ora chiamo questo Service da MainActivity:
public class MainActivity extends Activity {

	Button button;
	Intent intent;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		button=(Button)findViewById(R.id.button1);
		button.setOnClickListener(new View.OnClickListener() {
			
			@Override
			public void onClick(View v) {
				intent=new Intent(getApplicationContext(),Servizio.class);
				startService(intent);
				
			}
		});

	
		}
}
... chiamo con la pressione di un pulsante.
E vado.

E schiaccio ripetutamente il Button.
Ecco:
06-16 19:26:01.314: V/SEGNALE(2305): ONCREATE
06-16 19:26:01.314: V/SEGNALE(2305): ONSTARTCOMMAND
06-16 19:26:06.289: V/SEGNALE(2305): ONSTARTCOMMAND
06-16 19:26:08.170: V/SEGNALE(2305): ONSTARTCOMMAND
06-16 19:26:09.662: V/SEGNALE(2305): ONSTARTCOMMAND
06-16 19:26:11.203: V/SEGNALE(2305): ONSTARTCOMMAND
ONCREATE viene chiamato soltanto alla prima pressione.
Ci riprovo ancora, reinstallando sul cellulare da Eclipse dopo aver chiuso l'App sul cellulare:
06-16 19:28:28.678: V/SEGNALE(4387): ONSTARTCOMMAND
06-16 19:28:30.099: V/SEGNALE(4387): ONSTARTCOMMAND
06-16 19:28:31.340: V/SEGNALE(4387): ONSTARTCOMMAND
06-16 19:28:32.401: V/SEGNALE(4387): ONSTARTCOMMAND
Ecco: ONCREATE non viene più evocato.
Dopo un po' ottengo un messaggio di errore "L'applicazione Esercizio6 non risponde. Chiudere?".
Digito "SI" e vedo in LogCat a vedere di capirci qualcosa...

Nessuna segnalazione.
Dunque, il Service viene creato una sola volta, mentre le volte successive è già creato e quindi viene soltanto eseguito startCommand.

Passato un po' di tempo, riapro l'App e compare di nuovo ONCREATE.
E mi segnala poi di nuovo l'errore di sopra, senza alcuna segnalazione in LogCat.


Provo a chiudere l'App e a riaprirla, e ridigito.
06-16 19:36:25.543: V/SEGNALE(5494): ONCREATE
06-16 19:36:25.543: V/SEGNALE(5494): ONSTARTCOMMAND
06-16 19:36:25.553: V/SEGNALE(5494): ONSTARTCOMMAND
06-16 19:36:26.844: V/SEGNALE(5494): ONSTARTCOMMAND
06-16 19:36:27.555: V/SEGNALE(5494): ONSTARTCOMMAND
06-16 19:36:28.145: V/SEGNALE(5494): ONSTARTCOMMAND
Ma se faccio tutto, chiusura e riapertura dell'App, in tempi brevi, ONCREATE non viene richiamata.
Se lo faccio in tempi più lunghi viene richiamata, e comunque una singola pressione sul bottone mi causa ripetute chiamate di ONSTARTCOMMAND.

Comunque, ONCREATE, trovo sulla documentazione, viene eseguito se è necessario.
Resta l'enigma di quel messaggio di errore...
Che non sia perché onStartCommand ha necessariamente bisogno di un return STICKY o qualche altra cosa?
Ora ci metto Service.START_STICKY e vediamo se lo fa di nuovo...

Ecco, sembra che con lo START_STICKY non venga fuori quel problema.
Dunque bisogna necessariamente dare un valore al return di onStartCommand.

Con lo START_STICKY sembra che sia veramente "colloso": dopo diversi minuti non si ricrea ONCREATE, segno che il Service è ancora "vivo" nel cellulare.
Ora provo con un diverso valore...
	@Override
	public int onStartCommand(Intent intent, int flags, int startUI){
		Log.v("SEGNALE", "ONSTARTCOMMAND");
		return Service.START_NOT_STICKY;
		
	}
06-16 20:31:21.959: V/SEGNALE(10437): ONCREATE
06-16 20:31:21.959: V/SEGNALE(10437): ONSTARTCOMMAND
06-16 20:31:23.891: V/SEGNALE(10437): ONSTARTCOMMAND
06-16 20:31:25.062: V/SEGNALE(10437): ONSTARTCOMMAND
06-16 20:31:25.662: V/SEGNALE(10437): ONSTARTCOMMAND
Chiudo l'App e la riapro, e ogni volta vedo questo:
06-16 20:31:21.959: V/SEGNALE(10437): ONCREATE
06-16 20:31:21.959: V/SEGNALE(10437): ONSTARTCOMMAND
06-16 20:31:23.891: V/SEGNALE(10437): ONSTARTCOMMAND
06-16 20:31:25.062: V/SEGNALE(10437): ONSTARTCOMMAND
06-16 20:31:25.662: V/SEGNALE(10437): ONSTARTCOMMAND
Attendo più tempo...
06-16 20:33:10.655: V/SEGNALE(11963): ONCREATE
06-16 20:33:10.665: V/SEGNALE(11963): ONSTARTCOMMAND
06-16 20:33:11.676: V/SEGNALE(11963): ONSTARTCOMMAND
06-16 20:33:12.306: V/SEGNALE(11963): ONSTARTCOMMAND


Controprova: ritorno con lo START_STICKY:
	@Override
	public int onStartCommand(Intent intent, int flags, int startUI){
		Log.v("SEGNALE", "ONSTARTCOMMAND");
		return Service.START_STICKY;
		
	}
06-16 20:34:35.368: V/SEGNALE(12274): ONCREATE
06-16 20:34:35.368: V/SEGNALE(12274): ONSTARTCOMMAND
06-16 20:34:36.389: V/SEGNALE(12274): ONSTARTCOMMAND
06-16 20:34:36.919: V/SEGNALE(12274): ONSTARTCOMMAND
06-16 20:34:37.490: V/SEGNALE(12274): ONSTARTCOMMAND
06-16 20:34:39.171: V/SEGNALE(12274): ONSTARTCOMMAND
Chiudo e riapro...
06-16 20:36:15.355: V/SEGNALE(14166): ONSTARTCOMMAND
06-16 20:36:16.376: V/SEGNALE(14166): ONSTARTCOMMAND
06-16 20:36:17.057: V/SEGNALE(14166): ONSTARTCOMMAND
06-16 20:36:17.667: V/SEGNALE(14166): ONSTARTCOMMAND
Ma vedo anche:
06-16 20:36:14.344: V/SEGNALE(14166): ONCREATE
06-16 20:36:14.344: V/SEGNALE(14166): ONSTARTCOMMAND
sia pure non ogni volta come con lo STICKY.

Ho riprovato con NOT_STICKY e ONCREATE mi appare ogni volta.
Sì, credo che la differenza sia questa: con STICKY il Service viene creato solo se necessario, mentre con NOT_STICKY viene regolarmente creato ogni volta.
Non vedo riferimenti di ONDESTROY, però.

Ecco, se io metto uno stopSelf() arriva il DESTROY, con la necessità di ricreare ogni volta il Service:
	@Override
	public int onStartCommand(Intent intent, int flags, int startUI){
		Log.v("SEGNALE", "ONSTARTCOMMAND");
		stopSelf();
		return Service.START_NOT_STICKY;
	}
06-16 20:52:43.849: V/SEGNALE(16417): ONCREATE
06-16 20:52:43.849: V/SEGNALE(16417): ONSTARTCOMMAND
06-16 20:52:43.849: V/SEGNALE(16417): ONDESTROY
06-16 20:52:45.151: V/SEGNALE(16417): ONCREATE
06-16 20:52:45.151: V/SEGNALE(16417): ONSTARTCOMMAND
06-16 20:52:45.151: V/SEGNALE(16417): ONDESTROY
06-16 20:52:48.004: V/SEGNALE(16417): ONCREATE
06-16 20:52:48.004: V/SEGNALE(16417): ONSTARTCOMMAND
06-16 20:52:48.014: V/SEGNALE(16417): ONDESTROY

Ecco perché nel mio Service parlante è assurdo mettere il codice con lo speak() nel contesto di onCreate: in tutti i casi in cui onCreate non viene creato, il Service non parlerà!
Provo ancora:
public class Servizio extends Service{
	TextToSpeech tts;
	@Override
	public void onCreate(){
		super.onCreate();
		Log.v("SEGNALE", "ONCREATE");
		tts=new TextToSpeech(this,new TextToSpeech.OnInitListener() {
			
			@Override
			public void onInit(int status) {
				// TODO Auto-generated method stub
				if(status==TextToSpeech.SUCCESS){
					tts.setLanguage(Locale.ITALIAN);
				}
			}
		});
		
	}
	
	@Override
	public int onStartCommand(Intent intent, int flags, int startUI){
		Log.v("SEGNALE", "ONSTARTCOMMAND");
		tts.speak("Vieni avanti, cretino", TextToSpeech.QUEUE_FLUSH, null);
		stopSelf();
		return Service.START_NOT_STICKY;
	}
	
	@Override
	public void onDestroy(){
		super.onDestroy();
		Log.v("SEGNALE", "ONDESTROY");
	}

	@Override
	public IBinder onBind(Intent intent) {
		// TODO Auto-generated method stub
		return null;
	}
	
}
Ecco, in questo caso ottengo un errore:
06-16 21:11:07.356: V/SEGNALE(19270): ONCREATE
06-16 21:11:07.536: W/ApplicationPackageManager(19270): getCSCPackageItemText()
06-16 21:11:07.596: I/TextToSpeech(19270): Sucessfully bound to com.google.android.tts
06-16 21:11:07.596: V/SEGNALE(19270): ONSTARTCOMMAND
06-16 21:11:07.616: W/TextToSpeech(19270): speak failed: not bound to TTS engine
06-16 21:11:07.696: V/SEGNALE(19270): ONDESTROY
06-16 21:11:07.726: E/ActivityThread(19270): Service com.example.esercizio6.Servizio has leaked ServiceConnection android.speech.tts.TextToSpeech$Connection@41e1a728 that was originally bound here
che è dovuto al fatto che non c'è connessione con il tts engine.
Troppo presto, probabilmente.
Provo a mettere lo speak() nel contesto di onCreate:
public class Servizio extends Service{
	TextToSpeech tts;
	@Override
	public void onCreate(){
		super.onCreate();
		Log.v("SEGNALE", "ONCREATE");
		tts=new TextToSpeech(this,new TextToSpeech.OnInitListener() {
			
			@Override
			public void onInit(int status) {
				// TODO Auto-generated method stub
				if(status==TextToSpeech.SUCCESS){
					tts.setLanguage(Locale.ITALIAN);
					tts.speak("Vieni avanti, cretino", TextToSpeech.QUEUE_FLUSH, null);
					stopSelf();
				}
			}
		});
		
	}
	
	@Override
	public int onStartCommand(Intent intent, int flags, int startUI){
		Log.v("SEGNALE", "ONSTARTCOMMAND");
		return Service.START_NOT_STICKY;
	}
	
	@Override
	public void onDestroy(){
		super.onDestroy();
		Log.v("SEGNALE", "ONDESTROY");
	}

	@Override
	public IBinder onBind(Intent intent) {
		// TODO Auto-generated method stub
		return null;
	}
	
}
Ottengo una risposta molto ritardata, incostante e incompleta.
Perché?
Forse è quello stopSelf() che dà fastidio... Lo levo.
	public void onCreate(){
		super.onCreate();
		Log.v("SEGNALE", "ONCREATE");
		tts=new TextToSpeech(this,new TextToSpeech.OnInitListener() {
			
			@Override
			public void onInit(int status) {
				// TODO Auto-generated method stub
				if(status==TextToSpeech.SUCCESS){
					tts.setLanguage(Locale.ITALIAN);
					tts.speak("Vieni avanti, cretino", TextToSpeech.QUEUE_FLUSH, null);
					
				}
			}
		});
		
	}
E vediamo:
Sì, adesso funziona, sia pure con notevole ritardo.
Alla seconda non funziona più, semplicemente perché ONCREATE non viene più eseguita!

E' un bel problema, come gestire il tts in un service.
L'inizializzazione del tts può anche essere messa in onCreate, ma lo speak() no, altrimenti non viene eseguito in tutte quelle situazioni in cui onCreate non viene eseguito.
Peraltro lo speak() rischia di non essere eseguito nemmeno in onStartCommand, se l'inizializzazione del tts in onCreate prende troppo tempo e nel frattempo viene eseguito onStartCommand senza che la connessione al tts sia completa.
Vediamo di trovare esempi in rete...

Ho trovato questo.
Ci provo.

public class Servizio extends Service implements TextToSpeech.OnInitListener,OnUtteranceCompletedListener{

	TextToSpeech tts;
	@Override
	public IBinder onBind(Intent intent) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public void onUtteranceCompleted(String utteranceId) {
		// TODO Auto-generated method stub
	}

	@Override
	public void onInit(int status) {
		// TODO Auto-generated method stub
		
	}
	
}
Ecco lo scheletro che ho ottenuto estendendo Service e implementando le interfacce di TextToSpeech.
Ora in onInit piazzo tutto il codice per la creazione di un motore tts, mentre l'istanziazione la piazzo in onCreate.
public class Servizio extends Service implements TextToSpeech.OnInitListener,OnUtteranceCompletedListener{

	TextToSpeech tts;
	@Override
	public void onCreate(){
		tts=new TextToSpeech(this,this);
	}
	@Override
	public IBinder onBind(Intent intent) {
		// TODO Auto-generated method stub
		return null;
	}
	@Override
	public void onUtteranceCompleted(String utteranceId) {
		
		
	}
	@Override
	public void onInit(int status) {
		if(status==TextToSpeech.SUCCESS){
			int result=tts.setLanguage(Locale.ITALIAN);
			tts.speak("Ciao, ciccio", TextToSpeech.QUEUE_FLUSH, null);
			
		}	
	}
	
}
Ho piazzato l'istanziazione in onCreate e il codice per l'inizializzazione e lo speak() in onInit, ovviamente.
Vediamo se funziona...

Funziona solo la prima volta. Successivamente si blocca.
Ci metto anche onStartCommand, per metterci lo STICKY o meno...

Ecco, adesso mi è più chiaro.
06-17 01:26:09.764: V/SEGNALE(19408): ONCREATE
06-17 01:26:09.764: V/SEGNALE(19408): ONSTARTCOMMAND
06-17 01:26:12.156: I/TextToSpeech(19408): Connected to ComponentInfo{com.google.android.tts/com.google.android.tts.service.GoogleTTSService}
06-17 01:26:12.196: I/TextToSpeech(19408): Set up connection to ComponentInfo{com.google.android.tts/com.google.android.tts.service.GoogleTTSService}
06-17 01:26:12.216: V/SEGNALE(19408): ONINIT
Viene chiamato ONCREATE, quindi ONSTARTCOMMAND e da ultimo ONINIT: tts viene istanziato in onCreate, da dove chiama onInit per l'inizializzazione e quindi lo speak.
onStartCommand viene chiamato direttamente dal richiamo del Service, credo, ma onInit viene chiamato da onCreate nel momento in cui in onCreate tts viene istanziato.

Alla successiva pressione del button, invece, si realizza solo onStartCommand, e per questo il programma non emette alcun suono.

Ora, dunque, bisognerebbe fare in modo da chiudere il Service ogni volta, in modo che si possa riaprire onCreate.
A questo proposito cerchiamo di usare onUtteranceCompletedListener.

E se mettessi tutto in onStartCommand?
Proviamo...

public class Servizio extends Service{

	TextToSpeech tts;
	@Override
	public void onCreate(){
	
		Log.v("SEGNALE", "ONCREATE");
	}
		
	@Override
	public int onStartCommand(Intent intent, int flags, int startUI){
		tts=new TextToSpeech(this,new TextToSpeech.OnInitListener() {
			
			@Override
			public void onInit(int status) {
				if(status==TextToSpeech.SUCCESS){
					int result=tts.setLanguage(Locale.ITALIAN);
					tts.speak("Vieni avanti, cretino", TextToSpeech.QUEUE_FLUSH, null);
				}
			}
		});
		Log.v("SEGNALE", "ONSTARTCOMMAND");
		return Service.START_NOT_STICKY;
	}
	
	@Override
	public void onDestroy(){
		super.onDestroy();
		Log.v("SEGNALE", "ONDESTROY");
	}
	
	@Override
	public IBinder onBind(Intent intent) {
		// TODO Auto-generated method stub
		return null;
	}

}
Sembra funzionare in modo molto più efficiente di prima.

Potrei anche stoppare il Service, comunque.
Questa è la sequenza su LogCat:
06-17 11:39:04.933: V/SEGNALE(12422): ONCREATE
06-17 11:39:04.933: W/ApplicationPackageManager(12422): getCSCPackageItemText()
06-17 11:39:04.943: I/TextToSpeech(12422): Sucessfully bound to com.google.android.tts
06-17 11:39:04.943: V/SEGNALE(12422): ONSTARTCOMMAND
06-17 11:39:05.053: I/TextToSpeech(12422): Connected to ComponentInfo{com.google.android.tts/com.google.android.tts.service.GoogleTTSService}
06-17 11:39:05.063: I/TextToSpeech(12422): Set up connection to ComponentInfo{com.google.android.tts/com.google.android.tts.service.GoogleTTSService}
06-17 11:39:20.208: W/ApplicationPackageManager(12422): getCSCPackageItemText()
06-17 11:39:20.218: I/TextToSpeech(12422): Sucessfully bound to com.google.android.tts
06-17 11:39:20.218: V/SEGNALE(12422): ONSTARTCOMMAND
06-17 11:39:20.218: I/TextToSpeech(12422): Connected to ComponentInfo{com.google.android.tts/com.google.android.tts.service.GoogleTTSService}
06-17 11:39:20.228: I/TextToSpeech(12422): Set up connection to ComponentInfo{com.google.android.tts/com.google.android.tts.service.GoogleTTSService}
La prima volta il sintetizzatore vocale impiega più tempo, per partire, mentre le volte successive parte immediatamente.
Forse non mi conviene che il Service si chiuda...

Il ritardo è dovuto al tempo necessario per la creazione ex novo del Service.

Però, così facendo, se il service rimane attivo, a ogni interruzione del service seguita da ri-creazione, mi "parte" la voce...

Dal momento che non mi interessa l'esecuzione immediata, posso permettermi qualche secondo perché venga creato il Service ex novo, e quindi è meglio interromperlo a ogni esecuzione.
Come fare?

Riscrivo...

public class Servizio extends Service implements TextToSpeech.OnInitListener,TextToSpeech.OnUtteranceCompletedListener{
	TextToSpeech tts;

	@Override
	public IBinder onBind(Intent intent) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public void onInit(int status) {
		if(status==TextToSpeech.SUCCESS){
			int result=tts.setLanguage(Locale.ITALIAN);
			tts.speak("Ciao, bello", TextToSpeech.QUEUE_FLUSH, null);
		}
		
	}

	@Override
	public void onUtteranceCompleted(String utteranceId) {
		// TODO Auto-generated method stub
		
	}
	
}
Questo dovrebbe essere sufficiente per parlare...
Vediamo...

No: ho dimenticato di istanziare tts nell'ambito di onCreate.
public class Servizio extends Service implements TextToSpeech.OnInitListener,TextToSpeech.OnUtteranceCompletedListener{
	TextToSpeech tts;

	@Override
	public void onCreate(){
		tts=new TextToSpeech(this,this);
	}
	@Override
	public IBinder onBind(Intent intent) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public void onInit(int status) {
		if(status==TextToSpeech.SUCCESS){
			int result=tts.setLanguage(Locale.ITALIAN);
			tts.speak("Ciao, bello", TextToSpeech.QUEUE_FLUSH, null);
		}
		
	}

	@Override
	public void onUtteranceCompleted(String utteranceId) {
		// TODO Auto-generated method stub
		
	}
	
}
E infatti parla. Con i soliti alcuni secondi di ritardo, ma parla.

Ora voglio che il Service si chiuda per riaprirsi all'occorrenza.
Per prima cosa, mi marco con il Log tutti i metodi
public class Servizio extends Service implements TextToSpeech.OnInitListener,TextToSpeech.OnUtteranceCompletedListener{
	TextToSpeech tts;

	@Override
	public void onCreate(){
		tts=new TextToSpeech(this,this);
		Log.v("SEGNALE", "ONCREATE");
	}
	@Override
	public IBinder onBind(Intent intent) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public void onInit(int status) {
		if(status==TextToSpeech.SUCCESS){
			int result=tts.setLanguage(Locale.ITALIAN);
			tts.speak("Ciao, bello", TextToSpeech.QUEUE_FLUSH, null);
		}
		Log.v("SEGNALE","ONINIT");
		
	}

	@Override
	public void onUtteranceCompleted(String utteranceId) {
		// TODO Auto-generated method stub
		
	}
	
}
Ecco:
06-17 13:20:56.402: I/TextToSpeech(27081): Sucessfully bound to com.google.android.tts
06-17 13:20:56.412: V/SEGNALE(27081): ONCREATE
06-17 13:20:58.454: I/TextToSpeech(27081): Connected to ComponentInfo{com.google.android.tts/com.google.android.tts.service.GoogleTTSService}
06-17 13:20:58.494: I/TextToSpeech(27081): Set up connection to ComponentInfo{com.google.android.tts/com.google.android.tts.service.GoogleTTSService}
06-17 13:20:58.564: D/SEGNALE(27081): ONINIT
E adesso andiamo a studiare onUtterance... eccetera...

Per prima cosa mi marco anche quello:
	@Override
	public void onUtteranceCompleted(String utteranceId) {
		Log.v("SEGNALE","ONUTTERANCE");
		
	}
E scopro che non viene eseguito.
Aggiungo anche onDestroy:
	@Override
	public void onDestroy(){
		super.onDestroy();
		Log.v("SEGNALE","ONDESTROY");
	}
E so già che non viene eseguito, comunque me lo riprovo ugualmente...

Infatti non viene eseguito.

Andiamo finalmente a studiare onUtteranceCompleted!
E' deprecato, consigliata la classe onUtteranceProgressListener, ma in entrembi i casi è necessario fornire un utteranceId
Che accidenti è un utteranceId?



Soluzione trovata. Ecco il Service:
public class Servizio extends Service implements TextToSpeech.OnInitListener,TextToSpeech.OnUtteranceCompletedListener{
	TextToSpeech tts;

	@Override
	public void onCreate(){
		tts=new TextToSpeech(this,this);
		Log.v("SEGNALE", "ONCREATE");
	}
	@Override
	public IBinder onBind(Intent intent) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public void onInit(int status) {
		if(status==TextToSpeech.SUCCESS){
			int result=tts.setLanguage(Locale.ITALIAN);
			tts.setOnUtteranceCompletedListener(this);
			HashMap hashMap=new HashMap();
			hashMap.put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, "frase");
			tts.setPitch(0.1f);
			tts.setSpeechRate(0.1f);
			tts.speak("Verrò a ucciderti stanotte", TextToSpeech.QUEUE_FLUSH, hashMap);
		}
		Log.v("SEGNALE","ONINIT");
		
	}

	@Override
	public void onUtteranceCompleted(String utteranceId) {
		tts.stop();
		tts.shutdown();
		stopSelf();
		Log.v("SEGNALE","ONUTTERANCE");
		
	}
	
	@Override
	public void onDestroy(){
		super.onDestroy();
		Log.v("SEGNALE","ONDESTROY");
	}
	
}
Funziona.
Dopo aver detto la frase, il Service viene stoppato e si distrugge (non senza aver chiuso il tts).