Android
Onderhoud
Zoeken…
Invoering
Een service wordt op de achtergrond uitgevoerd om langlopende bewerkingen uit te voeren of om werkzaamheden voor externe processen uit te voeren. Een service biedt geen gebruikersinterface die alleen op de achtergrond met gebruikersinvoer wordt uitgevoerd. Een service kan bijvoorbeeld muziek op de achtergrond afspelen terwijl de gebruiker zich in een andere app bevindt, of het kan gegevens van internet downloaden zonder de interactie van de gebruiker met het Android-apparaat te blokkeren.
Opmerkingen
Als u uw service niet hebt gedefinieerd in uw AndroidManifest.xml, ontvangt u een ServiceNotFoundException
wanneer u deze probeert te starten.
Notitie:
Voor informatie over IntentService
, zie hier: IntentService-voorbeeld
Een service starten
Een service starten is heel eenvoudig, bel startService
met een intentie vanuit een activiteit:
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.
Levenscyclus van een service
De serviceslevenscyclus heeft de volgende callbacks
-
onCreate()
:
Wordt uitgevoerd wanneer de service voor het eerst wordt gemaakt om de eerste configuraties in te stellen die u mogelijk nodig hebt. Deze methode wordt alleen uitgevoerd als de service nog niet actief is.
-
onStartCommand()
:
Elke keer uitgevoerd startService()
wordt aangeroepen door een ander onderdeel, zoals een activiteit of een BroadcastReceiver. Wanneer u deze methode gebruikt, wordt de Service uitgevoerd totdat u stopSelf()
of stopService()
. Merk op dat, ongeacht hoe vaak onStartCommand()
, de methoden stopSelf()
en stopService()
slechts eenmaal moeten worden aangeroepen om de service te stoppen.
-
onBind()
:
Uitgevoerd wanneer een component bindService()
en een exemplaar van IBInder retourneert, dat een communicatiekanaal naar de Service levert. Een aanroep van bindService()
zorgt ervoor dat de service actief blijft zolang er clients aan zijn gebonden.
-
onDestroy()
:
Uitgevoerd wanneer de service niet meer in gebruik is en de toegewezen middelen kunnen worden verwijderd.
Het is belangrijk op te merken dat tijdens de levenscyclus van een service andere callbacks kunnen worden aangeroepen, zoals onConfigurationChanged()
en onLowMemory()
https://developer.android.com/guide/components/services.html
Het proces van een service definiëren
Het veld android:process
definieert de naam van het proces waar de service moet worden uitgevoerd. Normaal gesproken worden alle componenten van een toepassing uitgevoerd in het standaardproces dat voor de toepassing is gemaakt. Een component kan de standaardinstelling echter overschrijven met een eigen proceskenmerk, zodat u uw toepassing over meerdere processen kunt spreiden.
Als de naam die is toegewezen aan dit kenmerk begint met een dubbele punt (':'), wordt de service uitgevoerd in een afzonderlijk proces.
<service
android:name="com.example.appName"
android:process=":externalProcess" />
Als de procesnaam met een kleine letter begint, wordt de service uitgevoerd in een globaal proces met die naam, op voorwaarde dat het toestemming heeft. Hierdoor kunnen componenten in verschillende applicaties een proces delen, waardoor het gebruik van bronnen wordt beperkt.
Bound Service creëren met behulp van Binder
Maak een klasse die Service
uitbreidt en in overschreven methode onBind
retourneer uw lokale onBind
:
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;
}
}
Vervolgens bindt u in uw activiteit aan service in onStart
callback, met behulp van ServiceConnection
instantie en onStop
deze 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;
}
};
}
Service op afstand maken (via AIDL)
Beschrijf uw servicetoegangsinterface via .aidl
bestand:
// 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();
}
Na het bouwen van de applicatie zullen de SDK-tools het juiste .java
bestand genereren. Dit bestand bevat de Stub
klasse die onze aidl-interface implementeert en die we moeten uitbreiden:
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;
}
}
Vervolgens in activiteit:
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;
}
}
Een ongebonden service maken
Het eerste wat u moet doen, is de service toevoegen aan AndroidManifest.xml
, binnen de 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>
Als u uw serviceklasse in een apart pakket wilt beheren (bijvoorbeeld: .AllServices.RecordingService), moet u opgeven waar uw service zich bevindt. Dus in het bovenstaande geval zullen we wijzigen:
android:name=".RecordingService"
naar
android:name=".AllServices.RecordingService"
of de eenvoudigste manier om dit te doen is om de volledige pakketnaam op te geven.
Vervolgens maken we de daadwerkelijke serviceklasse:
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();
}
}
Het enige dat deze service doet, is een melding weergeven wanneer deze wordt uitgevoerd en toasts weergeven wanneer de methode doSomething()
wordt aangeroepen.
Zoals u zult merken, is het geïmplementeerd als een singleton , waarbij het zijn eigen instantie bijhoudt - maar zonder de gebruikelijke statische singleton-fabrieksmethode omdat services van nature singletons zijn en door intenties zijn gecreëerd. Het exemplaar is handig voor de buitenkant om een "handle" voor de service te krijgen wanneer deze wordt uitgevoerd.
Ten slotte moeten we de service starten en stoppen met een activiteit:
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 dit voorbeeld wordt de service op dezelfde manier gestart en gestopt, afhankelijk van de huidige status.
We kunnen ook de methode doSomething()
gebruiken voor onze activiteit:
public void makeServiceDoSomething(){
if( RecordingService.isRunning )
RecordingService.instance.doSomething();
}