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

wprowadź opis zdjęcia tutaj

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();
}


Modified text is an extract of the original Stack Overflow Documentation
Licencjonowany na podstawie CC BY-SA 3.0
Nie związany z Stack Overflow