Android
Servizio
Ricerca…
introduzione
Un servizio viene eseguito in background per eseguire operazioni di lunga durata o per eseguire lavori per processi remoti. Un servizio non fornisce alcuna interfaccia utente che viene eseguita solo in background con l'input dell'utente. Ad esempio, un servizio può riprodurre musica in sottofondo mentre l'utente si trova in un'app diversa, oppure potrebbe scaricare dati da Internet senza bloccare l'interazione dell'utente con il dispositivo Android.
Osservazioni
Se non hai definito il tuo servizio nel tuo AndroidManifest.xml, riceverai una ServiceNotFoundException
quando tenti di avviarlo.
Nota:
Per informazioni su IntentService
, vedere qui: Esempio di IntentService
Avvio di un servizio
Avviare un servizio è molto semplice, basta chiamare startService
con un intento, all'interno di un'attività:
Intent intent = new Intent(this, MyService.class); //substitute MyService with the name of your service
intent.putExtra(Intent.EXTRA_TEXT, "Some text"); //add any extra data to pass to the service
startService(intent); //Call startService to start the service.
Ciclo di vita di un servizio
Il ciclo di vita dei servizi presenta le seguenti richiamate
-
onCreate()
:
Eseguito quando il servizio viene creato per configurare le configurazioni iniziali che potrebbero essere necessarie. Questo metodo viene eseguito solo se il servizio non è già in esecuzione.
-
onStartCommand()
:
Eseguito ogni volta che startService()
viene invocato da un altro componente, come un'attività o un BroadcastReceiver. Quando si utilizza questo metodo, il servizio verrà eseguito finché non si chiama stopSelf()
o stopService()
. Si noti che indipendentemente da quante volte si chiama onStartCommand()
, i metodi stopSelf()
e stopService()
devono essere richiamati una sola volta per arrestare il servizio.
-
onBind()
:
Eseguito quando un componente chiama bindService()
e restituisce un'istanza di IBInder, fornendo un canale di comunicazione al Servizio. Una chiamata a bindService()
manterrà il servizio in esecuzione finché ci sono client ad esso associati.
-
onDestroy()
:
Eseguito quando il servizio non è più in uso e consente lo smaltimento delle risorse assegnate.
È importante notare che durante il ciclo di vita di un servizio potrebbero essere richiamati altri callback come onConfigurationChanged()
e onLowMemory()
https://developer.android.com/guide/components/services.html
Definire il processo di un servizio
Il campo android:process
definisce il nome del processo in cui deve essere eseguito il servizio. Normalmente, tutti i componenti di un'applicazione vengono eseguiti nel processo predefinito creato per l'applicazione. Tuttavia, un componente può sovrascrivere il valore predefinito con il proprio attributo di processo, consentendo di distribuire l'applicazione su più processi.
Se il nome assegnato a questo attributo inizia con due punti (':'), il servizio verrà eseguito nel suo processo separato.
<service
android:name="com.example.appName"
android:process=":externalProcess" />
Se il nome del processo inizia con un carattere minuscolo, il servizio verrà eseguito in un processo globale con quel nome, a condizione che abbia il permesso di farlo. Ciò consente ai componenti di diverse applicazioni di condividere un processo, riducendo l'utilizzo delle risorse.
Creazione del servizio associato con l'aiuto di Binder
Crea una classe che estende la classe di Service
e in metodo sovrascritto onBind
restituisce l'istanza di binder locale:
public class LocalService extends Service {
// Binder given to clients
private final IBinder mBinder = new LocalBinder();
/**
* Class used for the client Binder. Because we know this service always
* runs in the same process as its clients, we don't need to deal with IPC.
*/
public class LocalBinder extends Binder {
LocalService getService() {
// Return this instance of LocalService so clients can call public methods
return LocalService.this;
}
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
}
Quindi, durante l'attività, onStart
binding al servizio in callback di onStart
, utilizzando l'istanza ServiceConnection
e separandoti da esso in onStop
:
public class BindingActivity extends Activity {
LocalService mService;
boolean mBound = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
@Override
protected void onStart() {
super.onStart();
// Bind to LocalService
Intent intent = new Intent(this, LocalService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onStop() {
super.onStop();
// Unbind from the service
if (mBound) {
unbindService(mConnection);
mBound = false;
}
}
/** Defines callbacks for service binding, passed to bindService() */
private ServiceConnection mConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName className,
IBinder service) {
// We've bound to LocalService, cast the IBinder and get LocalService instance
LocalBinder binder = (LocalBinder) service;
mService = binder.getService();
mBound = true;
}
@Override
public void onServiceDisconnected(ComponentName arg0) {
mBound = false;
}
};
}
Creazione di un servizio remoto (tramite AIDL)
Descrivi la tua interfaccia di accesso al servizio tramite il file .aidl
:
// IRemoteService.aidl
package com.example.android;
// Declare any non-default types here with import statements
/** Example service interface */
interface IRemoteService {
/** Request the process ID of this service, to do evil things with it. */
int getPid();
}
Ora dopo l'applicazione di build, gli strumenti sdk .java
file .java
appropriato. Questo file conterrà la classe Stub
che implementa la nostra interfaccia aidl e che dobbiamo estendere:
public class RemoteService extends Service {
private final IRemoteService.Stub binder = new IRemoteService.Stub() {
@Override
public int getPid() throws RemoteException {
return Process.myPid();
}
};
@Nullable
@Override
public IBinder onBind(Intent intent) {
return binder;
}
}
Quindi in attività:
public class MainActivity extends AppCompatActivity {
private final ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
IRemoteService service = IRemoteService.Stub.asInterface(iBinder);
Toast.makeText(this, "Activity process: " + Process.myPid + ", Service process: " + getRemotePid(service), LENGTH_SHORT).show();
}
@Override
public void onServiceDisconnected(ComponentName componentName) {}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
protected void onStart() {
super.onStart();
Intent intent = new Intent(this, RemoteService.class);
bindService(intent, connection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onStop() {
super.onStop();
unbindService(connection);
}
private int getRemotePid(IRemoteService service) {
int result = -1;
try {
result = service.getPid();
} catch (RemoteException e) {
e.printStackTrace();
}
return result;
}
}
Creazione di un servizio non associato
La prima cosa da fare è aggiungere il servizio a AndroidManifest.xml
, all'interno del tag <application>
:
<application ...>
...
<service
android:name=".RecordingService"
<!--"enabled" tag specifies Whether or not the service can be instantiated by the system — "true" -->
<!--if it can be, and "false" if not. The default value is "true".-->
android:enabled="true"
<!--exported tag specifies Whether or not components of other applications can invoke the -->
<!--service or interact with it — "true" if they can, and "false" if not. When the value-->
<!--is "false", only components of the same application or applications with the same user -->
<!--ID can start the service or bind to it.-->
android:exported="false" />
</application>
Se si intende gestire la classe di servizio in un pacchetto separato (ad esempio:. AllServices.RecordingService), sarà necessario specificare dove si trova il servizio. Quindi, nel caso di cui sopra modificheremo:
android:name=".RecordingService"
a
android:name=".AllServices.RecordingService"
o il modo più semplice per farlo è specificare il nome completo del pacchetto.
Quindi creiamo la classe di servizio effettiva:
public class RecordingService extends Service {
private int NOTIFICATION = 1; // Unique identifier for our notification
public static boolean isRunning = false;
public static RecordingService instance = null;
private NotificationManager notificationManager = null;
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate(){
instance = this;
isRunning = true;
notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId){
// The PendingIntent to launch our activity if the user selects this notification
PendingIntent contentIntent = PendingIntent.getActivity(this, 0, new Intent(this, MainActivity.class), 0);
// Set the info for the views that show in the notification panel.
Notification notification = new NotificationCompat.Builder(this)
.setSmallIcon(R.mipmap.ic_launcher) // the status icon
.setTicker("Service running...") // the status text
.setWhen(System.currentTimeMillis()) // the time stamp
.setContentTitle("My App") // the label of the entry
.setContentText("Service running...") // the content of the entry
.setContentIntent(contentIntent) // the intent to send when the entry is clicked
.setOngoing(true) // make persistent (disable swipe-away)
.build();
// Start service in foreground mode
startForeground(NOTIFICATION, notification);
return START_STICKY;
}
@Override
public void onDestroy(){
isRunning = false;
instance = null;
notificationManager.cancel(NOTIFICATION); // Remove notification
super.onDestroy();
}
public void doSomething(){
Toast.makeText(getApplicationContext(), "Doing stuff from service...", Toast.LENGTH_SHORT).show();
}
}
Tutto questo servizio viene visualizzato quando è in esecuzione e può visualizzare toasts quando viene chiamato il metodo doSomething()
.
Come si noterà, è implementato come un singleton , tenendo traccia della propria istanza, ma senza il solito metodo statico di fabbrica singleton perché i servizi sono naturalmente singleton e sono creati da intenti. L'istanza è utile all'esterno per ottenere un "handle" per il servizio quando è in esecuzione.
Infine, dobbiamo avviare e interrompere il servizio da un'attività:
public void startOrStopService(){
if( RecordingService.isRunning ){
// Stop service
Intent intent = new Intent(this, RecordingService.class);
stopService(intent);
}
else {
// Start service
Intent intent = new Intent(this, RecordingService.class);
startService(intent);
}
}
In questo esempio, il servizio viene avviato e interrotto con lo stesso metodo, a seconda del suo stato corrente.
Possiamo anche invocare il metodo doSomething()
dalla nostra attività:
public void makeServiceDoSomething(){
if( RecordingService.isRunning )
RecordingService.instance.doSomething();
}