JavascriptProva

giovedì 16 giugno 2016

Chiusura di AlarmManager, onsiderazioni sul ciclo di vita di un Service, chiusura corretta di TTS.

Sarebbe il caso di vedere come si può terminare un AlarmManager.
Forse anche qui c'è un problema analogo a quallo del binding...

Ecco, ho trovato, ma ho problemi di TTS.
Ottengo un messaggio di errore in cui viene segnalato un problema relativo al TTS.
Il TextToSpeech andrebbe chiuso. Ma coms?
Ecco. Scopro che TextToSpeech ha due metodi: stop() e shutdown()
Il problema è: se io ho avviato un service da MainActivity, il codice del TTS è sul Service, non sulla main. Come faccio a chiudere TTS?
La cosa migliore è chiudere non appena TTS ha cessato di parlare.
Solo voglio vedere che differenza c'è fra stop e shutdown.

Ma che scemo! Faccio eseguire un metodo del Service che chiami i metodi del TTS prima di stoppare il Service.
Il metodo stop() interrompe la frase corrente, mentre shutdown() rilascia proprio le risorse per il tts.

Se voglio che il Service si stoppi proprio una volta che la frase è stata detta, mi conviene dunque prima stoppare il tts e poi stoppare il Service.

Vorrei verificare il messaggio di errore che ho ottenuto stoppando il Service mediante la pressione del pulsante.
Il codidce è questo:
  button.setOnClickListener(new View.OnClickListener() {
   
   @Override
   public void onClick(View v) {
    if(alarmManager!=null) {
     alarmManager.cancel(pendingIntent);
     stopService(new Intent(getApplicationContext(),Servizio.class));
     
    }
    
   }
  });
Il codice del bottone stoppa alarmManager e poi stoppa il Service.
Faccio partire il programma, che dopo 3 minuti dovrebbe dirmi una @Override public void onDestroy(){ super.onDestroy(); Log.e("Servizio","distrutto"); }.
Se schiaccio il pulsante prima che venga detta la frase, ottengo il messaggio di errore relativo al tts.
Probabilmente perché devo prima chiudere il tts, prima che sia chiuso il Service.

Vediamo se stoppare il service equivale a Destroy.
Ho iniziato aggiungendo al Service questo metodo:
 @Override
 public void onDestroy(){
  super.onDestroy();
  Log.e("Servizio","distrutto");
 }
Quando si stoppa il Service da esterno o lo si fa stoppare dall'interno, dunque, il service si autodistrugge.
Mi discosto un po' dal discorso iniziale del tts e sperimento un po' sul ciclo di vita di un service.
Pongo degli indicatori in onCreate e onDestroy.
 @Override
 public void onCreate(){
  super.onCreate();
  Log.d("onCreate","Creazione");
 @Override
 public int onStartCommand(Intent intent, int flags, int startUI){
  Log.e("onStartCommand","start");
  tts.speak("Buongiorno, bello", TextToSpeech.QUEUE_FLUSH,null);
  
  return Service.START_STICKY;
 @Override
 public void onDestroy(){
  super.onDestroy();
  Log.e("onDestroy","distruzione");
 }
Vediamo un po' i "marker" come si comportano quando chiamo un service e poi lo distruggo. Sempre con quel codice nel bottone di "distruzione", quindi non so se sia affidabile... Proviamo, comunque!
06-16 13:59:08.230: E/onCreate(15611): Creazione
06-16 13:59:08.240: W/ApplicationPackageManager(15611): getCSCPackageItemText()
06-16 13:59:08.321: I/TextToSpeech(15611): Sucessfully bound to com.google.android.tts
06-16 13:59:08.321: E/onStartCommand(15611): start
06-16 13:59:08.321: W/TextToSpeech(15611): speak failed: not bound to TTS engine
06-16 13:59:08.411: D/OpenGLRenderer(15611): Enabling debug mode 0
06-16 13:59:08.491: I/Timeline(15611): Timeline: Activity_idle id: android.os.BinderProxy@41eaa110 time:36195801
06-16 13:59:11.123: I/TextToSpeech(15611): Connected to ComponentInfo{com.google.android.tts/com.google.android.tts.service.GoogleTTSService}
06-16 13:59:11.133: I/TextToSpeech(15611): Set up connection to ComponentInfo{com.google.android.tts/com.google.android.tts.service.GoogleTTSService}
06-16 13:59:13.105: E/onStartCommand(15611): start
06-16 13:59:33.425: E/onDestroy(15611): distruzione
06-16 13:59:33.525: E/ActivityThread(15611): Service com.example.esercizio6.Servizio has leaked ServiceConnection android.speech.tts.TextToSpeech$Connection@41ed2638 that was originally bound here
Provato! Quando il Service viene avviato, entrano onCreate e onStartCommand. Quindi alla pressione del pulsante entra in gioco onDestroy.
Però dopo quest'ultimo ottengo un errore di TTS.

Il codice cancella AlarmManager e distrugge il Service, e penso che probabilmente l'errore sia dovuto al fatto che non viene debitamente chiuso TTS.
Vi aggiungo il comando tts.stop():
 @Override
 public void onDestroy(){
  super.onDestroy();
  Log.e("onDestroy","distruzione");
  tts.stop();
 }
Ecco:
06-16 14:04:33.708: E/onStartCommand(27773): start
06-16 14:04:44.428: E/onDestroy(27773): distruzione
06-16 14:04:44.498: E/ActivityThread(27773): Service com.example.esercizio6.Servizio has leaked ServiceConnection android.speech.tts.TextToSpeech$Connection@41ecf3c8 that was originally bound here
06-16 14:04:44.498: E/ActivityThread(27773): android.app.ServiceConnectionLeaked: Service com.example.esercizio6.Servizio has leaked ServiceConnection android.speech.tts.TextToSpeech$Connection@41ecf3c8 that was originally bound here


VI aggiungo il comando tts.shutdown():
 @Override
 public void onDestroy(){
  super.onDestroy();
  Log.e("onDestroy","distruzione");
  tts.stop();
  tts.shutdown();
 }
06-16 14:07:20.230: E/onStartCommand(16713): start
06-16 14:07:26.907: E/onDestroy(16713): distruzione
Okay!
Quindi, nel momento in cui si chiude un Service (ma anche un'Activity, meglio ricordarlo) si deve stoppare e poi fare shutdown sul tts.

mercoledì 15 giugno 2016

Service con TTS innescato da un AlarmManager: voce che ripete a intervalli predeterminati una frase.

Adesso metto un TTS nel Service.
Così ripasso anche la sintassi.

L'ho messo nel Service in questo modo:
public class Servizio extends Service{

 TextToSpeech tts;
 @Override
 public void onCreate(){
  super.onCreate();
   tts=new TextToSpeech(this, new TextToSpeech.OnInitListener() {
    
   @Override
   public void onInit(int status) {
    if(status==TextToSpeech.SUCCESS){
     int result=tts.setLanguage(Locale.ITALIAN);
    }
    
   }
  });
 }

 @Override
 public int onStartCommand(Intent intent, int flags, int startUI){
  tts.speak("Antonello è il più grandioso programmatore dilettante del mondo", TextToSpeech.QUEUE_FLUSH, null);
  return Service.START_STICKY;
  
 }
 @Override
 public IBinder onBind(Intent intent) {
  return null;
 } 
}


e nel MainActivity:
public class MainActivity extends Activity {

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

   
  
  
  
  AlarmManager alarmManager=(AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
  Intent intent=new Intent(this,Servizio.class);
  pendingIntent=PendingIntent.getService(this,0,intent,0);
  
  alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
          5*1000,
          5*1000, 
          pendingIntent);
      
  
 }
}
E funziona!

AlarmManager ELAPSED_TIME_WAKEUP

Ho acquisito sufficiente dimestichezza con il binding di un Service nell'ambito della stessa App.
Ora, giusto per non perdere di vista l'obiettivo, voglio creare nuovamente l'AlarmManager.

Non mi servirà che l'AlarmManager faccia il binding a un service, l'importante è che lo attivi e faccia il task che deve fare, quindi lo stoppi (o si stoppi da solo) in attesa della prossima chiamata.

Andiamo a ripassare AlarmManager...

Partiamo subito. Cancello il tutto...
Mi ricordo che ci sono due tipi di AlarmManager...

AlarmManager non si istanzia da una classe, ma la sintassi è quella di un getSystemService:
 alarmManager=(AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
Ora che abbiamo un alarmManager di tipo AlarmManager, che ci facciamo?
Ci vuole un Intent che venga inglobato da un PendingIntent: si tratta di un Intent esplicito che sembra far riferimento a un Service...

Ora ci metto allora il mio Service, con il solo metodo onStartCommand, e vediamo che succede...

Devo dichiarare la variabile PendingIntent...

 AlarmManager alarmManager;
 PendingIntent pendingIntent;


Ora si deve creare l'Intent esplicito e "inglobarlo" nel PendingIntent.
Devo vedere la sintassi di questo "inglobamento", tanto più che mentre nel tutorial che sto seguendo viene usato un broadcast receiver, io sto usando un service.
  Intent intent =new Intent(this,Servizio.class);
  pendingIntent.getService(this, 1, intent, 0);
A questo punto si "impacchetta" tutto in AlarmManager, dando le impostazioni. Facciamo che ogni 30 secondi viene richiamato il Service... si usa il metodo set di AlarmManager.
alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime()+60*1000, pendingIntent);
Ci provo: ogni 60 secondi dovrebbe apparire un Toast con scritto "VIENE ESEGUITO IL METODO DEL SERVICE". Mi sviscererò successivamente i parametri del metodo set...

Non funziona!

Trovato: un banale errore di sintassi che, corretto, lo fa funzionare egregiamente.
Ecco i codici:

MainActivity:
public class MainActivity extends Activity {

 
 PendingIntent pendingIntent;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);

  AlarmManager alarmManager=(AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
  Intent intent=new Intent(this,Servizio.class);
  pendingIntent=PendingIntent.getService(this,0,intent,0);
  
  alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
          5*1000,
          5 * 1000, pendingIntent);
      
  
 }
}


Servizio:
public class Servizio extends Service{

 @Override
 public IBinder onBind(Intent intent) {
  return null;
 }
 
 @Override
 public int onStartCommand(Intent intent, int flags, int startUI){
  Log.e("N'GUL", "A MAMMETE");
  
  return startUI;
  
 }
  
}

martedì 14 giugno 2016

Binding e unbinding di un Service dalla stessa App.

Ecco, ho creato un Service e l'ho avviato e bindato sia dalla stessa App sia da un'altra App.
Ho sul cellulare ambedue, e ha un comportamento di questo genere: Avvio la App1 che crea il Service, e mi appare il toast di inizio, quello che viene mostrato nel metodo onStartCommand.
Quindi bindo la stessa App1, che nel corso del binding rileva i metodi del Service e fa partire il metodo esegui() del Servizio, che mostra il toast "Questo è il service".
Chiudo App1 e lancio App2, che binda ugualmente il Service dandomi un segnale proveniente dalla stessa App2, perché non so eseguire metodi del Service quando questo viene bindato da esterno.
Il Service continua a stare attivo nel cellulare, perché anche a distanza di ore, quando eseguo App2, esso viene bindato.
Però sia che venga bindato con App1, sia che lo venga con App2, il binding funziona una volta sola. O meglio, essendo l'App già bindata non si binda un'altra volta, e dal momento che i segnali che ho posto nel binding sono ambedue dipendenti dal momento in cui il binding si crea, non appaiono più una volta che il binding già esista.

Mi esercito nel fare il binding e cerco di imparare a fare l'unbinding per i Services chiamati dalla stessa App, quindi proverò a imparare la sintassi del Message per il binding di Services chiamati da un'altra App.


Ho trovato il codice.
Esercitiamoci di nuovo con il binding e ci aggiungiamo unbinding.

Ma prima è il caso di salvarsi il codice per il tentativo che mi è già riuscito...

MainActivity:
public class MainActivity extends Activity {

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

   Servizio servizio;
   @Override
   public void onServiceConnected(ComponentName name, IBinder service) {
    Servizio.MyBinder binder=(Servizio.MyBinder)service;
    servizio=binder.getService();
    servizio.esegui();

    
   }

   @Override
   public void onServiceDisconnected(ComponentName name) {
    // TODO Auto-generated method stub
    
   }
   
  };

  

 }

}


Servizio:
public class Servizio extends Service{

 
 IBinder mioServizio=new MyBinder();
 @Override
 public IBinder onBind(Intent intent) {
  // TODO Auto-generated method stub
  return mioServizio;
 }
 
 @Override
 public int onStartCommand(Intent intent, int flags, int startUI){
  Toast.makeText(this, "Servizio attivato", Toast.LENGTH_LONG).show();
  return Service.START_STICKY;
  
 }
 
 public class MyBinder extends Binder{
  Servizio getService(){
   return Servizio.this;
  }
 }   
 

 
 public void esegui(){
  Toast.makeText(this, "Questo è il Service", Toast.LENGTH_LONG).show();
 }
}


Adesso, come al solito, rompiamo il Mandala...

E ricostruiamo, prima la semplice creazione di un Service e poi il codice col binding.

Servizio:
public class Servizio extends Service{

 @Override
 public IBinder onBind(Intent intent) {
  // TODO Auto-generated method stub
  return null;
 }
 
 @Override
 public int onStartCommand(Intent intent, int flage, int startUI){
  Toast.makeText(this, "onStartCommand", Toast.LENGTH_LONG).show();
  return Service.START_STICKY;
  
 }
 
}


MainActivity:
public class MainActivity extends Activity {

 Button button;
 @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 intent=new Intent(getApplicationContext(),Servizio.class);
    startService(intent);
    
   }
  });

 }

}


Facile...
Adesso invece avrò bisogno certamente di sbirciare...

Servizio:
public class Servizio extends Service{

 IBinder myBinder=new MyBinder();
 @Override
 public IBinder onBind(Intent intent) {
  // TODO Auto-generated method stub
  return myBinder;
 }
 
 public class MyBinder extends Binder{
  Servizio getReference(){
   return Servizio.this;
  }
 }
 
 @Override
 public int onStartCommand(Intent intent, int flage, int startUI){
  Toast.makeText(this, "onStartCommand", Toast.LENGTH_LONG).show();
  return Service.START_STICKY;
  
 }
 public void esegui(){
  Toast.makeText(this, "metodo ESEGUI del Service", Toast.LENGTH_LONG).show();
 }
 
}


MainActivity:
public class MainActivity extends Activity {

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

   Servizio servizio;
   @Override
   public void onServiceConnected(ComponentName name, IBinder service) {
    Servizio.MyBinder binder=(Servizio.MyBinder)service;
    servizio=binder.getReference();
    servizio.esegui();
    Intent intent=new Intent(getApplicationContext(),Servizio.class);
    stopService(intent);
    unbindService(serviceConnection);

    
   }

   @Override
   public void onServiceDisconnected(ComponentName name) {
    // TODO Auto-generated method stub
    
   }
   
  };
 }
}
E adesso il binding si fa ogni volta, e ogni volta appare il toast che testimonia l'avvenuto binding, ossia l'esecuzione del metodo del Service.

lunedì 13 giugno 2016

Esercizio di scrittura di un Service chiamato dalla stessa App

Nuovo esercizio in cui viene creato un Service e chiamato dalla stessa App.

Parto da questi:

MainActivity:
package com.example.esercizio6;

import android.app.Activity;
import android.os.Bundle;



public class MainActivity extends Activity {

	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
	}


}


Servizio:
package com.example.esercizio6;


Andiamo:
package com.example.esercizio6;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;



public class MainActivity extends Activity {

	Button button;
	@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 intent=new Intent(getApplicationContext(),Servizio.class);
				startService(intent);
				
			}
		});
	}

}
package com.example.esercizio6;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.widget.Toast;

public class Servizio 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){
		Toast.makeText(this, "Servizio attivato", Toast.LENGTH_LONG).show();
		return startUI;
		
	}
}


Fatto!

Creare un Service, registrarlo e chiamarlo dalla stessa App

Riprendiamo l'AlarmManager, sempre nell'intenzione di costruire un "ricordatore" per i farmaci.

Credo che la cosa migliore sia chiamare un service direttamente. Creare un Broadcast Receiver potrebbe avere senso se si volessero attivare più azioni contemporaneamente, o se si volesse intercettare un broadcast dal sistema.

Teniamo fermo questo aspetto.
E le acquisizioni che abbiamo fatto finora.

Dunque, mi devo esercitare nel binding di un service.
Focalizziamoci solo sopre ai services.

Ho creato un Service, in una classe a parte: la costruzione è piuttosto facile, implementando la classe Service: infatti si viene guidati fino a questo punto semplicemente implementando:
public class Servizio extends Service{

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

}
Ora ricordiamo che il Service si deve registrare.
Come si registra nel Manifest?
Io ricordo questo:
......
    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <service android:name="Servizio" />
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application> 
......
Ma vediamo se ricordo bene e soprattutto se è sufficiente...

S', va bene ma c'erano altre opzioni, che forse servono sia a permettere che il service sia chiamato da altre applicazioni sia a evitare che venga chiamato a sproposito...
Esploriamole. Non mi ricordo com'è che si chiamano...

Va bene, ma c'è anche l'opzione android:enabled che devo verificare.
Un'altra che non capisco è android:icon.
Il service, infatti, essendo un'operazione in background, che bisogno avrebbe di un'icona?
Credo sia relativo alla status bar... ma al momento lasciamo perdere...

Provo a chiamare un service così.
Sempre con il Toast, altimenti con il tts è troppo difficile. Mi eserciterò sempre con i toast, per il momento.
Ecco, ho aggiunto al Service l'evento onStartCommand:
public class Servizio extends Service{

	@Override
	public IBinder onBind(Intent intent) {
		// TODO Auto-generated method stub
		return null;
	}
	@Override
	public int onStartCommand(Intent intent, int flage, int startUI){
		Toast.makeText(this, "SERVIZIO CHIAMATO", Toast.LENGTH_LONG).show();
		return startUI;
	}

}
E lo chiamo, dallo stesso package, così:
public class MainActivity extends Activity {

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

	}


}
Non ha funzionato perché ho dimenticato di istanziare la variabile button.
...Ci ho riprovato e ho scoperto che non avevo avviato quella app ma un'altra!!!!!!!!
Riproviamo con tutti i codici a posto:

MainActivity:
public class MainActivity extends Activity {

	Button button;
	@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 intent=new Intent(getApplicationContext(),Servizio.class);
				startService(intent);
				
			}
		});

	}


}


Servizio:
public class Servizio extends Service{

	@Override
	public IBinder onBind(Intent intent) {
		// TODO Auto-generated method stub
		return null;
	}
	@Override
	public int onStartCommand(Intent intent, int flage, int startUI){
		Toast.makeText(this, "SERVIZIO CHIAMATO", Toast.LENGTH_LONG).show();
		return startUI;
	}

}
FUNZIONA!
Bene, ho creato un service, l'ho registrato e l'ho chiamato dalla stessa app. Funziona!

sabato 11 giugno 2016

Service con tts, chiamato dalla stessa App o da un'App diversa.

Ho trovato modi alternativi di inizializzare un oggetto TextToSpeech.
La sintassi che utilizzo io è:
  tts=new TextToSpeech(context,new TextToSpeech.OnInitListener() {
   
   @Override
   public void onInit(int status) {
    if(status==TextToSpeech.SUCCESS){
     int result=tts.setLanguage(Locale.ITALIAN);
    }
    
   }
  });
ma ora ne trovo di diverse.
Lascio un attimo i Broadcast Receiver per tornare sul TextToSpeech.
Un'altra sintassi che ho trovato usa un diverso costruttore di TextToSpeech:
tts = new TextToSpeech(this, this);
Vado a vedere sulla documentazione se ci sono diversi costruttori.

No.
Riceve come parametri il contesto (this) e il Listener, che è un'interfaccia.
Potrebbe ricevere come parametro anche il contesto se questo implementa un'interfaccia, dunque...

Proviamo:
public class MainActivity extends Activity implements OnInitListener {


 TextToSpeech tts=new TextToSpeech(this,this);
Questa va bene: dal momento che il secondo parametro del costruttore di TextToSpeech deve essere un oggetto che implementa l'interfaccia OnInitListener, anche questa sintassi va bene.
Ovviamente bisogna implementare il metodo onInit.

Torniamo al tts nel contesto del BroadcastReceiver...

Trovo una soluzione che istanzia tts in un Service.
Proviamo...

Ricreiamo un Service... Ecco, il metodo onStartCommand ha tre parametri che ho dimenticato completamente...

 @Override
 public int onStartCommand(Intent intent, int flags, int startId){
  return startId;
  
 }
ecco, una volta implementato, come appare onStartCommand...

Questo esprime le operazioni che il Service compie quando viene iniziato
Nel Service si può partire dal metodo onCreate.
Me lo accetta:
 @Override
 public void onCreate(){
  
 }
Ecco come creo un nuovo oggetto di tipo TextToSpeech nel Service, in onCreate:
public class Servizio extends Service{
 
 TextToSpeech tts;
 @Override
 public void onCreate(){
  tts=new TextToSpeech(this, new TextToSpeech.OnInitListener(){

   @Override
   public void onInit(int status) {
    if(status==TextToSpeech.SUCCESS){
     int result=tts.setLanguage(Locale.ITALIAN);
    }
    
   }
   
  });
 }
Bene.
Ora trovo un metodo onUtteranceCompleted Che significa?
Credo che faccia parte del Service...
Vediamo...

No, fa parte di TextToSpeech.
Dovrebbe essere chiamato alla fine dell'opera di TextToSpeech.
Ecco: ho risolto: faccio implementare alla classe sia l'interfaccia onInitListener sia l'interfaccia onUtteranceCompletedListener.
public class Servizio extends Service implements OnInitListener,OnUtteranceCompletedListener{
 
 TextToSpeech tts;
 @Override
 public void onCreate(){
  tts=new TextToSpeech(this,this);
 }
 
    @Override
    public void onUtteranceCompleted(String uttId) {
        stopSelf();
    }

 @Override
 public IBinder onBind(Intent intent) {
  // TODO Auto-generated method stub
  return null;
 }
 
 @Override
 public int onStartCommand(Intent intent, int flags, int startId){
  return startId;
  
 }
 @Override
 public void onInit(int status) {
  // TODO Auto-generated method stub
  
 }


}
Distruggo il Mandala e riscrivo...
public class Servizio extends Service implements TextToSpeech.OnInitListener, TextToSpeech.OnUtteranceCompletedListener{

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

 @Override
 public IBinder onBind(Intent intent) {
  // TODO Auto-generated method stub
  return null;
 }
 
}
Bene: facendo implementare le interfacce, i metodi vengono da soli.

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

 private TextToSpeech tts;
 
 @Override
 public void onCreate(){
  tts=new TextToSpeech(this,this);
  
 }
 @Override
 public void onInit(int status) {
  if(status==TextToSpeech.SUCCESS){
   int result=tts.setLanguage(Locale.ITALIAN);
   tts.speak("Ciao, mondo crudele", TextToSpeech.QUEUE_FLUSH, null);
  }
  
 }
 
 @Override
 public void onUtteranceCompleted(String utteranceId) {
  stopSelf();
  
 }

 @Override
 public IBinder onBind(Intent intent) {
  // TODO Auto-generated method stub
  return null;
 }
 
 @Override
 public void onDestroy(){
  if(tts!=null){
   tts.stop();
   tts.shutdown();
  }
  super.onDestroy();
 }
 
}
Eccolo completo, spero.

Ora come si chiama il servizio? L'ho dimenticato.

Ecco, si chiama con un semplice Intent, che può essere esplicito nella chiamata di un Service creato nella stessa App ma deve necessariamente essere implicito se il Service appartiene a una App diversa.

Ecco il codice dell'App che ha creato il Service:
public class MainActivity extends Activity {


 

 Button button;
 @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 intent=new Intent(getApplicationContext(),Servizio.class);
    startService(intent);
    
   }
  });
  
  

  
 }
}
Con questo Manifest:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.esercizio5"
    android:versionCode="1"
    android:versionName="1.0" >

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

    <uses-sdk
        android:minSdkVersion="18"
        android:targetSdkVersion="23" />

    <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"/>
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <activity
            android:name=".Seconda"
            android:label="@string/title_activity_seconda" >
        </activity>

    </application>

</manifest> 


...mentre il codice dell'App che chiama questo Service dall'esterno è:
public class MainActivity extends Activity {

 Button button;
 @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 intent=new Intent();
    intent.setComponent(new ComponentName("com.example.esercizio5","com.example.esercizio5.Servizio"));
    startService(intent);
    
   }
  });
  
    
 }


}