Android
Usługa
Szukaj…
Wprowadzenie
Usługa działa w tle, aby wykonywać długotrwałe operacje lub wykonywać prace dla procesów zdalnych. Usługa nie zapewnia interfejsu użytkownika, który działa tylko w tle z danymi wejściowymi użytkownika. Na przykład usługa może odtwarzać muzykę w tle, gdy użytkownik jest w innej aplikacji, lub może pobierać dane z Internetu bez blokowania interakcji użytkownika z urządzeniem z Androidem.
Uwagi
Jeśli nie zdefiniowałeś swojej usługi w pliku AndroidManifest.xml, otrzymasz ServiceNotFoundException
podczas próby jej uruchomienia.
Uwaga:
Aby uzyskać informacje na temat usługi IntentService
, zobacz tutaj: Przykład usługi IntentService
Uruchomienie usługi
Uruchomienie usługi jest bardzo proste, wystarczy wywołać startService
z zamiarem, z poziomu działania:
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.
Cykl życia usługi
Cykl życia usług ma następujące wywołania zwrotne
-
onCreate()
:
Wywoływany, gdy usługa jest tworzona po raz pierwszy w celu skonfigurowania potrzebnych konfiguracji początkowych. Ta metoda jest wykonywana tylko wtedy, gdy usługa nie jest jeszcze uruchomiona.
-
onStartCommand()
:
Wywoływany za każdym razem, gdy startService()
jest wywoływany przez inny komponent, taki jak Activity lub BroadcastReceiver. Podczas korzystania z tej metody usługa będzie działać do momentu wywołania stopSelf()
lub stopService()
. Zauważ, że niezależnie od tego, ile razy wywołujesz onStartCommand()
, metody stopSelf()
i stopService()
muszą być wywoływane tylko raz, aby zatrzymać usługę.
-
onBind()
:
Wywoływany, gdy komponent wywołuje bindService()
i zwraca instancję IBInder, zapewniając kanał komunikacji z usługą. Wywołanie bindService()
utrzyma działanie usługi, dopóki będą z nią związani klienci.
-
onDestroy()
:
Wywoływany, gdy usługa nie jest już używana i umożliwia zbywanie przydzielonych zasobów.
Należy zauważyć, że podczas cyklu życia usługi mogą być wywoływane inne wywołania zwrotne, takie jak onConfigurationChanged()
i onLowMemory()
https://developer.android.com/guide/components/services.html
Definiowanie procesu usługi
Pole android:process
określa nazwę procesu, w którym usługa ma być uruchomiona. Zwykle wszystkie składniki aplikacji działają w domyślnym procesie utworzonym dla aplikacji. Jednak komponent może zastąpić wartość domyślną za pomocą własnego atrybutu procesu, co pozwala rozłożyć aplikację na wiele procesów.
Jeśli nazwa przypisana do tego atrybutu zaczyna się od dwukropka („:”), usługa będzie działać w osobnym procesie.
<service
android:name="com.example.appName"
android:process=":externalProcess" />
Jeśli nazwa procesu zaczyna się od małej litery, usługa będzie działać w procesie globalnym o tej nazwie, pod warunkiem, że ma na to pozwolenie. Umożliwia to współdzielenie procesu przez komponenty w różnych aplikacjach, zmniejszając zużycie zasobów.
Tworzenie usługi powiązanej za pomocą Binder
Utwórz klasę, która rozszerza klasę Service
i metodą przesłoniętą onBind
zwraca lokalną instancję spoiwa:
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;
}
}
Następnie w swoim działaniu połącz się z usługą w wywołaniu zwrotnym onStart
, używając instancji ServiceConnection
i usuń powiązanie z nią w 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;
}
};
}
Tworzenie usługi zdalnej (przez AIDL)
Opisz interfejs dostępu do usługi za pomocą pliku .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();
}
Teraz po skompilowaniu aplikacji narzędzia SDK wygenerują odpowiedni plik .java
. Ten plik będzie zawierał klasę Stub
która implementuje nasz interfejs aidl i którą musimy rozszerzyć:
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;
}
}
Następnie w aktywności:
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;
}
}
Tworzenie usługi niezwiązanej
Pierwszą rzeczą do zrobienia jest dodanie usługi do AndroidManifest.xml
wewnątrz tagu <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>
Jeśli zamierzasz zarządzać klasą usługi w oddzielnym pakiecie (np .: .AllServices.RecordingService), musisz określić lokalizację usługi. W powyższym przypadku zmodyfikujemy:
android:name=".RecordingService"
do
android:name=".AllServices.RecordingService"
lub najłatwiejszym sposobem jest podanie pełnej nazwy pakietu.
Następnie tworzymy rzeczywistą klasę usług:
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();
}
}
Wszystko, co robi ta usługa, to wyświetla powiadomienie, gdy jest uruchomiona, i może wyświetlać tosty, gdy wywoływana jest jej metoda doSomething()
.
Jak zauważysz, jest on zaimplementowany jako singleton , śledząc własną instancję - ale bez zwykłej metody statycznej fabryki singletonów, ponieważ usługi są naturalnie singletonami i są tworzone przez intencje. Instancja jest przydatna na zewnątrz, aby uzyskać „uchwyt” do usługi, gdy jest uruchomiona.
Na koniec musimy uruchomić i zatrzymać usługę od działania:
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);
}
}
W tym przykładzie usługa jest uruchamiana i zatrzymywana tą samą metodą, w zależności od jej bieżącego stanu.
Możemy również wywoływać metodę doSomething()
z naszej działalności:
public void makeServiceDoSomething(){
if( RecordingService.isRunning )
RecordingService.instance.doSomething();
}