Sınır hizmetlerine genel bakış

Bağlama hizmeti, istemci-sunucu arayüzündeki sunucudur. Etkinlikler gibi bileşenlerin hizmete bağlanmasına, istek göndermesine, yanıt almasına ve işlemler arası iletişim (IPC) gerçekleştirmesine olanak tanır. Bağlama hizmeti genellikle yalnızca başka bir uygulama bileşenine hizmet verirken etkin olur ve arka planda süresiz olarak çalışmaz.

Bu belgede, diğer uygulama bileşenlerinden hizmete nasıl bağlanılacağı da dahil olmak üzere bağlı hizmet oluşturma işlemi açıklanmaktadır. Genel olarak hizmetler hakkında daha fazla bilgi edinmek için (ör. bir hizmetten bildirim gönderme ve hizmeti ön planda çalışacak şekilde ayarlama) Hizmetlere genel bakış başlıklı makaleyi inceleyin.

Temel özellikler

Bağlama hizmeti, diğer uygulamaların bağlanmasına ve etkileşim kurmasına olanak tanıyan Service sınıfının bir uygulamasıdır. Bir hizmet için bağlama sağlamak üzere onBind() geri çağırma yöntemini uygulamanız gerekir. Bu yöntem, istemcilerin hizmetle etkileşim kurmak için kullanabileceği programlama arayüzünü tanımlayan bir IBinder nesnesi döndürür.

Başlatılan bir hizmete bağlanma

Hizmetlere genel bakış bölümünde açıklandığı gibi, hem başlatılan hem de bağlı bir hizmet oluşturabilirsiniz. Yani startService() yöntemini çağırarak bir hizmet başlatabilirsiniz. Bu yöntem, hizmetin süresiz olarak çalışmasına olanak tanır. Ayrıca, bindService() yöntemini çağırarak bir istemcinin hizmete bağlanmasına izin verebilirsiniz.

Hizmetinizin başlatılmasına ve bağlanmasına izin verirseniz hizmet başladığında sistem, tüm istemciler bağlantıyı kaldırdığında hizmeti yok etmez. Bunun yerine, stopSelf() veya stopService() yöntemini çağırarak hizmeti açıkça durdurmanız gerekir.

Genellikle onBind() veya onStartCommand() yöntemlerinden birini uygulasanız da bazen her ikisini de uygulamanız gerekebilir. Örneğin, bir müzik çalar, hizmetinin süresiz olarak çalışmasına izin vermeyi ve bağlama sağlamayı yararlı bulabilir. Bu sayede bir etkinlik, müzik çalmak için hizmeti başlatabilir ve kullanıcı uygulamadan ayrılsa bile müzik çalmaya devam eder. Ardından, kullanıcı uygulamaya döndüğünde etkinlik, oynatma kontrolünü yeniden kazanmak için hizmete bağlanabilir.

Başlatılan bir hizmete bağlama eklerken hizmet yaşam döngüsü hakkında daha fazla bilgi için Bağlı hizmetin yaşam döngüsünü yönetme bölümüne bakın.

İstemci, bindService() yöntemini çağırarak bir hizmete bağlanır. Bu durumda, hizmetle bağlantıyı izleyen ServiceConnection uygulamasını sağlaması gerekir. bindService() işlevinin döndürdüğü değer, istenen hizmetin mevcut olup olmadığını ve istemcinin bu hizmete erişim izni olup olmadığını gösterir.

Android sistemi, istemci ile hizmet arasında bağlantı oluşturduğunda ServiceConnection üzerinde onServiceConnected() yöntemini çağırır. onServiceConnected() yöntemi, istemcinin bağlı hizmetle iletişim kurmak için kullandığı bir IBinder bağımsız değişkeni içerir.

Bir hizmete aynı anda birden fazla istemci bağlayabilirsiniz. Ancak sistem, IBinder hizmet iletişim kanalını önbelleğe alır. Diğer bir deyişle sistem, yalnızca ilk istemci bağlandığında IBinder oluşturmak için hizmetin onBind() yöntemini çağırır. Sistem daha sonra aynı IBinder öğesini, onBind() yöntemini tekrar çağırmadan aynı hizmete bağlanan tüm ek istemcilere gönderir.

Son istemci hizmetle olan bağını kopardığında, hizmet startService() kullanılarak başlatılmadığı sürece sistem hizmeti yok eder.

Bağlama hizmeti uygulamanızın en önemli kısmı, onBind() geri çağırma yönteminizin döndürdüğü arayüzü tanımlamaktır. Aşağıdaki bölümde, hizmetinizin IBinder arayüzünü tanımlayabileceğiniz çeşitli yöntemler ele alınmaktadır.

Bağlama hizmeti oluşturma

Bağlama sağlayan bir hizmet oluştururken istemcilerin hizmetle etkileşim kurmak için kullanabileceği programlama arayüzünü sağlayan bir IBinder sağlamanız gerekir. Arayüzü üç şekilde tanımlayabilirsiniz:

Binder sınıfını genişletme
Hizmetiniz kendi uygulamanıza özelse ve istemciyle aynı işlemde çalışıyorsa (bu yaygın bir durumdur) Binder sınıfını genişleterek ve onBind() yönteminden bu sınıfın bir örneğini döndürerek arayüzünüzü oluşturun. İstemci, Binder değerini alır ve bu değeri kullanarak Binder uygulamasında veya Service içinde bulunan herkese açık yöntemlere doğrudan erişebilir.

Hizmetiniz yalnızca kendi uygulamanız için bir arka plan çalışanıysa bu teknik tercih edilir. Arayüzünüzü oluşturmak için bu yöntemin tercih edilmediği tek kullanım alanı, hizmetinizin diğer uygulamalar tarafından veya ayrı işlemler arasında kullanılmasıdır.

Messenger kullanma
Arayüzünüzün farklı işlemler arasında çalışması gerekiyorsa Messenger ile hizmet için bir arayüz oluşturabilirsiniz. Bu şekilde hizmet, farklı türdeki Message nesnelerine yanıt veren bir Handler tanımlar.

Bu Handler, istemciyle IBinder paylaşabilen bir Messenger için temel oluşturur. Böylece istemci, Message nesnelerini kullanarak hizmete komut gönderebilir. Ayrıca istemci, hizmetin mesajları geri gönderebilmesi için kendi Messenger değerini tanımlayabilir.

Bu, işlemler arası iletişim (IPC) gerçekleştirmenin en basit yoludur. Messenger, tüm istekleri tek bir iş parçacığında sıraya koyduğundan hizmetinizi iş parçacığı açısından güvenli olacak şekilde tasarlamanız gerekmez.

AIDL kullanma
Android Arayüz Tanımlama Dili (AIDL), nesneleri işletim sisteminin anlayabileceği temel öğelere ayırır ve IPC gerçekleştirmek için bunları işlemler arasında sıralar. Messenger kullanan önceki teknik, temel yapısı olarak AIDL'yi kullanır.

Önceki bölümde belirtildiği gibi, Messenger tüm istemci isteklerini tek bir iş parçacığında sıraya koyar. Böylece hizmet, istekleri tek tek alır. Ancak hizmetinizin aynı anda birden fazla isteği işlemesini istiyorsanız AIDL'yi doğrudan kullanabilirsiniz. Bu durumda, hizmetiniz iş parçacığı açısından güvenli ve çoklu iş parçacığı oluşturma özelliğine sahip olmalıdır.

AIDL'yi doğrudan kullanmak için programlama arayüzünü tanımlayan bir .aidl dosyası oluşturun. Android SDK araçları, arayüzü uygulayan ve IPC'yi işleyen soyut bir sınıf oluşturmak için bu dosyayı kullanır. Bu sınıfı daha sonra hizmetinizde genişletebilirsiniz.

Not: Çoğu uygulamada, çoklu iş parçacığı özellikleri gerektirebileceği ve daha karmaşık bir uygulamaya neden olabileceği için AIDL, bağlı hizmet oluşturmak için en iyi seçenek değildir. Bu nedenle, bu belgede, hizmetiniz için nasıl kullanılacağı ele alınmamaktadır. AIDL'yi doğrudan kullanmanız gerektiğinden eminseniz AIDL belgesini inceleyin.

Binder sınıfını genişletme

Hizmetinizi yalnızca yerel uygulama kullanıyorsa ve hizmetin işlemler arasında çalışması gerekmiyorsa istemcinizin hizmetteki herkese açık yöntemlere doğrudan erişmesini sağlayan kendi Binder sınıfınızı uygulayabilirsiniz.

Not: Bu yöntem yalnızca istemci ve hizmet aynı uygulama ve işlemde olduğunda çalışır. Bu, en yaygın durumdur. Örneğin, bir etkinliği arka planda müzik çalan kendi hizmetine bağlaması gereken bir müzik uygulaması için bu yöntem idealdir.

Bunu nasıl kuracağınız aşağıda açıklanmıştır:

  1. Hizmetinizde, aşağıdakilerden birini yapan bir Binder örneği oluşturun:
    • İstemcinin çağırabileceği herkese açık yöntemleri içerir.
    • İstemcinin çağırabileceği herkese açık yöntemleri olan mevcut Service örneğini döndürür.
    • Hizmet tarafından barındırılan başka bir sınıfın örneğini, istemcinin çağırabileceği herkese açık yöntemlerle döndürür.
  2. onBind() geri çağırma yönteminden bu Binder örneğini döndürün.
  3. İstemcide, onServiceConnected() geri çağırma yönteminden Binder değerini alın ve sağlanan yöntemleri kullanarak bağlama hizmetine çağrı yapın.

Not: İstemcinin, döndürülen nesneyi yayınlayabilmesi ve API'lerini doğru şekilde çağırabilmesi için hizmet ve istemci aynı uygulamada olmalıdır. Bu teknik, işlemler arasında sıralama yapmadığından hizmet ve istemci de aynı işlemde olmalıdır.

Örneğin, aşağıdaki hizmet, müşterilere Binder uygulaması aracılığıyla hizmetteki yöntemlere erişim sağlar:

Kotlin

class LocalService : Service() {
    // Binder given to clients.
    private val binder = LocalBinder()

    // Random number generator.
    private val mGenerator = Random()

    /** Method for clients.  */
    val randomNumber: Int
        get() = mGenerator.nextInt(100)

    /**
     * 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.
     */
    inner class LocalBinder : Binder() {
        // Return this instance of LocalService so clients can call public methods.
        fun getService(): LocalService = this@LocalService
    }

    override fun onBind(intent: Intent): IBinder {
        return binder
    }
}

Java

public class LocalService extends Service {
    // Binder given to clients.
    private final IBinder binder = new LocalBinder();
    // Random number generator.
    private final Random mGenerator = new Random();

    /**
     * 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 binder;
    }

    /** Method for clients. */
    public int getRandomNumber() {
      return mGenerator.nextInt(100);
    }
}

LocalBinder, istemcilerin geçerli LocalService örneğini alması için getService() yöntemini sağlar. Bu sayede istemciler, hizmetteki herkese açık yöntemleri çağırabilir. Örneğin, istemciler hizmetten getRandomNumber() yöntemini çağırabilir.

Aşağıda, bir düğme tıklandığında LocalService hizmetine bağlanan ve getRandomNumber() yöntemini çağıran bir etkinlik örneği verilmiştir:

Kotlin

class BindingActivity : Activity() {
    private var mService: LocalService? = null
    private var mBound: Boolean = false

    /** Defines callbacks for service binding, passed to bindService().  */
    private val connection = object : ServiceConnection {

        override fun onServiceConnected(className: ComponentName, service: IBinder) {
            // We've bound to LocalService, cast the IBinder and get LocalService instance.
            val binder = service as LocalService.LocalBinder
            mService = binder.getService()
            mBound = true
        }

        override fun onServiceDisconnected(arg0: ComponentName) {
            mService = null
            mBound = false
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.main)
    }

    override fun onStart() {
        super.onStart()
        // Bind to LocalService.
        Intent(this, LocalService::class.java).also { intent ->
            bindService(intent, connection, Context.BIND_AUTO_CREATE)
        }
    }

    override fun onStop() {
        super.onStop()
        unbindService(connection)
        // Release the reference so the service can be garbage-collected once it's
        // destroyed. onServiceDisconnected() isn't called when the client unbinds.
        mService = null
        mBound = false
    }

    /** Called when a button is clicked (the button in the layout file attaches to
     * this method with the android:onClick attribute).  */
    fun onButtonClick(v: View) {
        if (mBound) {
            // Call a method from the LocalService.
            // However, if this call is something that might hang, then put this request
            // in a separate thread to avoid slowing down the activity performance.
            val num: Int = mService?.randomNumber ?: return
            Toast.makeText(this, "number: $num", Toast.LENGTH_SHORT).show()
        }
    }
}

Java

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, connection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onStop() {
        super.onStop();
        unbindService(connection);
        // Release the reference so the service can be garbage-collected once it's
        // destroyed. onServiceDisconnected() isn't called when the client unbinds.
        mService = null;
        mBound = false;
    }

    /** Called when a button is clicked (the button in the layout file attaches to
      * this method with the android:onClick attribute). */
    public void onButtonClick(View v) {
        if (mBound) {
            // Call a method from the LocalService.
            // However, if this call is something that might hang, then put this request
            // in a separate thread to avoid slowing down the activity performance.
            int num = mService.getRandomNumber();
            Toast.makeText(this, "number: " + num, Toast.LENGTH_SHORT).show();
        }
    }

    /** Defines callbacks for service binding, passed to bindService(). */
    private ServiceConnection connection = 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) {
            mService = null;
            mBound = false;
        }
    };
}

Yukarıdaki örnekte, istemcinin ServiceConnection ve onServiceConnected() geri çağırma işlevinin bir uygulamasını kullanarak hizmete nasıl bağlandığı gösterilmektedir. Bir sonraki bölümde, hizmete bağlanma süreci hakkında daha fazla bilgi verilmektedir.

Not: Önceki örnekte onStop() yöntemi, istemcinin hizmetle olan bağını kaldırır ve istemcinin hizmete olan referansını serbest bırakır. Ek notlar bölümünde açıklandığı gibi, istemcilerin hizmetlerle olan bağını uygun zamanlarda kaldırın.

Daha fazla örnek kod için ApiDemos'taki LocalService.java sınıfına ve LocalServiceActivities.java sınıfına bakın.

Kurye kullanma

Hizmetinizin uzak işlemlerle iletişim kurması gerekiyorsa hizmetiniz için arayüz sağlamak üzere Messenger kullanabilirsiniz. Bu teknik, AIDL kullanmanıza gerek kalmadan işlemler arası iletişim (IPC) gerçekleştirmenize olanak tanır.

Arayüzünüz için Messenger kullanmak, AIDL kullanmaktan daha basittir çünkü Messenger, hizmete yapılan tüm çağrıları sıraya alır. Saf bir AIDL arayüzü, hizmete eşzamanlı istekler gönderir. Hizmetin daha sonra çoklu iş parçacığı işlemesi gerekir.

Çoğu uygulamada hizmetin çoklu iş parçacığı oluşturması gerekmez. Bu nedenle, Messenger kullanıldığında hizmet her seferinde bir çağrıyı işler. Hizmetinizin çok iş parçacıklı olması önemliyse arayüzünüzü tanımlamak için AIDL kullanın.

Messenger kullanma hakkında bir özet:

  1. Hizmet, istemciden gelen her çağrı için geri çağırma alan bir Handler uygular.
  2. Hizmet, Handler kullanarak bir Messenger nesnesi (Handler referansı) oluşturur.
  3. Messenger, hizmetin onBind() üzerinden istemcilere döndürdüğü bir IBinder oluşturur.
  4. İstemciler, Messenger örneklendirmek için IBinder kullanır (hizmetin Handler referans verir). İstemci, hizmete Message nesneleri göndermek için bunu kullanır.
  5. Hizmet, her Message öğesini Handler içinde, özellikle de handleMessage() yönteminde alır.

Bu şekilde, istemcinin hizmeti çağırması için yöntem yoktur. Bunun yerine istemci, hizmetin Handler içinde aldığı mesajları (Message nesneleri) teslim eder.

Messenger arayüzünü kullanan örnek bir hizmeti aşağıda bulabilirsiniz:

Kotlin

/** Command to the service to display a message.  */
private const val MSG_SAY_HELLO = 1

class MessengerService : Service() {

    /**
     * Target we publish for clients to send messages to IncomingHandler.
     */
    private lateinit var mMessenger: Messenger

    /**
     * Handler of incoming messages from clients.
     */
    internal class IncomingHandler(
            context: Context,
            private val applicationContext: Context = context.applicationContext
    ) : Handler() {
        override fun handleMessage(msg: Message) {
            when (msg.what) {
                MSG_SAY_HELLO ->
                    Toast.makeText(applicationContext, "hello!", Toast.LENGTH_SHORT).show()
                else -> super.handleMessage(msg)
            }
        }
    }

    /**
     * When binding to the service, we return an interface to our messenger
     * for sending messages to the service.
     */
    override fun onBind(intent: Intent): IBinder? {
        Toast.makeText(applicationContext, "binding", Toast.LENGTH_SHORT).show()
        mMessenger = Messenger(IncomingHandler(this))
        return mMessenger.binder
    }
}

Java

public class MessengerService extends Service {
    /**
     * Command to the service to display a message.
     */
    static final int MSG_SAY_HELLO = 1;

    /**
     * Handler of incoming messages from clients.
     */
    static class IncomingHandler extends Handler {
        private Context applicationContext;

        IncomingHandler(Context context) {
            applicationContext = context.getApplicationContext();
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_SAY_HELLO:
                    Toast.makeText(applicationContext, "hello!", Toast.LENGTH_SHORT).show();
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }

    /**
     * Target we publish for clients to send messages to IncomingHandler.
     */
    Messenger mMessenger;

    /**
     * When binding to the service, we return an interface to our messenger
     * for sending messages to the service.
     */
    @Override
    public IBinder onBind(Intent intent) {
        Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show();
        mMessenger = new Messenger(new IncomingHandler(this));
        return mMessenger.getBinder();
    }
}

Handler içindeki handleMessage() yöntemi, hizmetin gelen Message öğesini aldığı ve what üyesine göre ne yapacağına karar verdiği yerdir.

İstemcinin tek yapması gereken, hizmet tarafından döndürülen IBinder değerine göre bir Messenger oluşturmak ve send() kullanarak mesaj göndermektir. Örneğin, hizmete bağlanan ve hizmete MSG_SAY_HELLO mesajını gönderen bir etkinlik aşağıda verilmiştir:

Kotlin

class ActivityMessenger : Activity() {
    /** Messenger for communicating with the service.  */
    private var mService: Messenger? = null

    /** Flag indicating whether we have called bind on the service.  */
    private var bound: Boolean = false

    /**
     * Class for interacting with the main interface of the service.
     */
    private val mConnection = object : ServiceConnection {

        override fun onServiceConnected(className: ComponentName, service: IBinder) {
            // This is called when the connection with the service has been
            // established, giving us the object we can use to
            // interact with the service.  We are communicating with the
            // service using a Messenger, so here we get a client-side
            // representation of that from the raw IBinder object.
            mService = Messenger(service)
            bound = true
        }

        override fun onServiceDisconnected(className: ComponentName) {
            // This is called when the connection with the service has been
            // unexpectedly disconnected—that is, its process crashed.
            mService = null
            bound = false
        }
    }

    fun sayHello(v: View) {
        if (!bound) return
        // Create and send a message to the service, using a supported 'what' value.
        val msg: Message = Message.obtain(null, MSG_SAY_HELLO, 0, 0)
        try {
            mService?.send(msg)
        } catch (e: RemoteException) {
            e.printStackTrace()
        }

    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.main)
    }

    override fun onStart() {
        super.onStart()
        // Bind to the service.
        Intent(this, MessengerService::class.java).also { intent ->
            bindService(intent, mConnection, Context.BIND_AUTO_CREATE)
        }
    }

    override fun onStop() {
        super.onStop()
        // Unbind from the service.
        if (bound) {
            unbindService(mConnection)
            mService = null
            bound = false
        }
    }
}

Java

public class ActivityMessenger extends Activity {
    /** Messenger for communicating with the service. */
    Messenger mService = null;

    /** Flag indicating whether we have called bind on the service. */
    boolean bound;

    /**
     * Class for interacting with the main interface of the service.
     */
    private ServiceConnection mConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className, IBinder service) {
            // This is called when the connection with the service has been
            // established, giving us the object we can use to
            // interact with the service.  We are communicating with the
            // service using a Messenger, so here we get a client-side
            // representation of that from the raw IBinder object.
            mService = new Messenger(service);
            bound = true;
        }

        public void onServiceDisconnected(ComponentName className) {
            // This is called when the connection with the service has been
            // unexpectedly disconnected—that is, its process crashed.
            mService = null;
            bound = false;
        }
    };

    public void sayHello(View v) {
        if (!bound) return;
        // Create and send a message to the service, using a supported 'what' value.
        Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0);
        try {
            mService.send(msg);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

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

    @Override
    protected void onStart() {
        super.onStart();
        // Bind to the service.
        bindService(new Intent(this, MessengerService.class), mConnection,
            Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onStop() {
        super.onStop();
        // Unbind from the service.
        if (bound) {
            unbindService(mConnection);
            mService = null;
            bound = false;
        }
    }
}

Bu örnekte, hizmetin istemciye nasıl yanıt verebileceği gösterilmemektedir. Hizmetin yanıt vermesini istiyorsanız istemcide bir Messenger de oluşturmanız gerekir. İstemci, onServiceConnected() geri çağırmasını aldığında hizmete bir Message gönderir. Bu istek, send() yönteminin replyTo parametresinde istemcinin Messenger değerini içerir.

İki yönlü mesajlaşmanın nasıl sağlanacağına dair bir örneği MessengerService.java (hizmet) ve MessengerServiceActivities.java (istemci) örneklerinde görebilirsiniz.

Bir hizmete bağlanma

Uygulama bileşenleri (istemciler), bindService() yöntemini çağırarak bir hizmete bağlanabilir. Ardından Android sistemi, hizmetin onBind() yöntemini çağırır. Bu yöntem, hizmetle etkileşim kurmak için bir IBinder döndürür.

Bağlama işlemi eşzamansızdır ve bindService(), IBinder değerini istemciye döndürmeden hemen döndürür. IBinder almak için istemcinin bir ServiceConnection örneği oluşturması ve bunu bindService() öğesine iletmesi gerekir. ServiceConnection, sistemin IBinder değerini iletmek için çağırdığı bir geri çağırma yöntemi içerir.

Not: Bir hizmete yalnızca etkinlikler, hizmetler ve içerik sağlayıcılar bağlanabilir. Yayın alıcıdan bir hizmete bağlanamazsınız.

İstemcinizden bir hizmete bağlanmak için aşağıdaki adımları uygulayın:

  1. ServiceConnection uygulayın.

    Uygulamanızın iki geri çağırma yöntemini geçersiz kılması gerekir:

    onServiceConnected()
    Sistem, hizmetin onBind() yöntemi tarafından döndürülen IBinder değerini iletmek için bu yöntemi çağırır.
    onServiceDisconnected()
    Android sistemi, hizmetle bağlantı beklenmedik bir şekilde kesildiğinde (ör. hizmet kilitlendiğinde veya sonlandırıldığında) bu yöntemi çağırır. İstemci bağlantıyı kaldırdığında bu yöntem çağrılmaz.
  2. ServiceConnection uygulamasını ileterek bindService() yöntemini çağırın.

    Not: Yöntem false değerini döndürürse istemcinizin hizmetle geçerli bir bağlantısı yoktur. An ancak istemcinizde unbindService() yöntemini çağırın. Aksi takdirde istemciniz, hizmetin boştayken kapanmasını engeller.

  3. Sistem, onServiceConnected() geri çağırma yönteminizi çağırdığında arayüz tarafından tanımlanan yöntemleri kullanarak hizmete çağrı yapmaya başlayabilirsiniz.
  4. Hizmetle bağlantıyı kesmek için unbindService() numaralı telefonu arayın.

    Uygulamanız istemciyi yok ettiğinde istemci hâlâ bir hizmete bağlıysa yok etme işlemi istemcinin bağlantısının kaldırılmasına neden olur. İstemcinin hizmetle etkileşimi biter bitmez bağlantısını kaldırmak daha iyi bir uygulamadır. Böylece boşta olan hizmet kapatılır. Bağlama ve bağlantıyı kaldırma için uygun zamanlar hakkında daha fazla bilgi edinmek istiyorsanız Ek notlar bölümünü inceleyin.

Aşağıdaki örnekte, istemci daha önce Binder sınıfı genişletilerek oluşturulan hizmete bağlanır. Bu nedenle, döndürülen IBinder değerini LocalBinder sınıfına dönüştürmesi ve LocalService örneğini istemesi yeterlidir:

Kotlin

var mService: LocalService? = null

val mConnection = object : ServiceConnection {
    // Called when the connection with the service is established.
    override fun onServiceConnected(className: ComponentName, service: IBinder) {
        // Because we have bound to an explicit
        // service that is running in our own process, we can
        // cast its IBinder to a concrete class and directly access it.
        val binder = service as LocalService.LocalBinder
        mService = binder.getService()
        mBound = true
    }

    // Called when the connection with the service disconnects unexpectedly.
    override fun onServiceDisconnected(className: ComponentName) {
        Log.e(TAG, "onServiceDisconnected")
        mService = null
        mBound = false
    }
}

Java

LocalService mService;
private ServiceConnection mConnection = new ServiceConnection() {
    // Called when the connection with the service is established.
    public void onServiceConnected(ComponentName className, IBinder service) {
        // Because we have bound to an explicit
        // service that is running in our own process, we can
        // cast its IBinder to a concrete class and directly access it.
        LocalBinder binder = (LocalBinder) service;
        mService = binder.getService();
        mBound = true;
    }

    // Called when the connection with the service disconnects unexpectedly.
    public void onServiceDisconnected(ComponentName className) {
        Log.e(TAG, "onServiceDisconnected");
        mService = null;
        mBound = false;
    }
};

Bu ServiceConnection ile istemci, aşağıdaki örnekte gösterildiği gibi bindService() öğesine ileterek bir hizmete bağlanabilir:

Kotlin

Intent(this, LocalService::class.java).also { intent ->
    bindService(intent, connection, Context.BIND_AUTO_CREATE)
}

Java

Intent intent = new Intent(this, LocalService.class);
bindService(intent, connection, Context.BIND_AUTO_CREATE);
  • bindService() işlevinin ilk parametresi, bağlanacak hizmeti açıkça adlandıran bir Intent nesnesidir.

    Dikkat: Bir Service öğesine bağlanmak için intent kullanıyorsanız açık intent kullanarak uygulamanızın güvenli olduğundan emin olun. Bir hizmeti başlatmak için örtük intent kullanmak güvenlik açısından tehlikelidir. Çünkü intent'e hangi hizmetin yanıt verdiğinden emin olamazsınız ve kullanıcı hangi hizmetin başladığını göremez. Android 5.0'dan (API düzeyi 21) itibaren sistem, örtük bir intent ile bindService() çağrısı yaparsanız istisna oluşturur.

  • İkinci parametre ServiceConnection nesnesidir.
  • Üçüncü parametre, bağlama seçeneklerini gösteren bir işarettir. Bu işaret, genellikle hizmet henüz etkin değilse hizmeti oluşturmak için BIND_AUTO_CREATE olur. Diğer olası değerler BIND_DEBUG_UNBIND, BIND_NOT_FOREGROUND veya hiçbiri için 0'dir.

Ek notlar

Bir hizmete bağlanmayla ilgili bazı önemli noktalar şunlardır:

  • Bağlantı kesildiğinde ortaya çıkan DeadObjectException istisnalarını her zaman yakalayın. Bu, uzak yöntemler tarafından oluşturulan tek istisnadır.
  • Nesneler, işlemler arasında referans sayılır.
  • Bağlama ve bağlamayı kaldırma işlemlerini genellikle istemcinin yaşam döngüsünün eşleşen başlatma ve kaldırma anlarında eşlersiniz. Bu durum aşağıdaki örneklerde açıklanmıştır:
    • Hizmetle yalnızca etkinliğiniz görünür durumdayken etkileşim kurmanız gerekiyorsa onStart() sırasında bağlayın ve onStop() sırasında bağlantıyı kaldırın.
    • Etkinliğinizin arka planda durdurulmuşken bile yanıt almasını istiyorsanız onCreate() sırasında bağlayın ve onDestroy() sırasında bağlantıyı kaldırın. Bu durum, etkinliğinizin çalıştığı süre boyunca (arka planda bile) hizmeti kullanması gerektiği anlamına gelir. Dolayısıyla, hizmet başka bir işlemdeyken işlemin ağırlığını artırırsınız ve sistem tarafından sonlandırılma olasılığı daha yüksek olur.

    Not: Bu geri çağırmalar her yaşam döngüsü geçişinde gerçekleştiğinden, etkinliğinizin onResume() ve onPause() geri çağırmaları sırasında genellikle bağlama ve bağlamayı kaldırma işlemleri yapılmaz. Bu geçişlerde gerçekleşen işlemleri minimumda tutun.

    Ayrıca, uygulamanızdaki birden fazla etkinlik aynı hizmete bağlanırsa ve bu etkinliklerden ikisi arasında geçiş olursa mevcut etkinlik bağlantıyı kaldırdığında (duraklatma sırasında) hizmet yok edilip yeniden oluşturulabilir ve bir sonraki etkinlik bağlanır (devam ettirme sırasında). Etkinliklerin yaşam döngülerini nasıl koordine ettiğine dair bu etkinlik geçişi, Etkinlik yaşam döngüsü bölümünde açıklanmıştır.

Bir hizmete nasıl bağlanılacağını gösteren daha fazla örnek kod için ApiDemos'taki RemoteService.java sınıfına bakın.

Bağlama hizmetinin yaşam döngüsünü yönetme

Bir hizmetin tüm istemcilerle olan bağı kaldırıldığında Android sistemi bu hizmeti yok eder (startService() kullanılarak başlatılmadığı sürece). Bu nedenle, tamamen bağlı bir hizmetse hizmetinizin yaşam döngüsünü yönetmeniz gerekmez. Android sistemi, herhangi bir istemciye bağlı olup olmadığına göre hizmeti sizin için yönetir.

Ancak onStartCommand() geri çağırma yöntemini uygulamayı seçerseniz hizmet artık başlatıldı olarak kabul edildiğinden hizmeti açıkça durdurmanız gerekir. Bu durumda hizmet, herhangi bir istemciye bağlı olup olmadığına bakılmaksızın stopSelf() ile kendi kendini durdurana veya başka bir bileşen stopService() yöntemini çağırana kadar çalışır.

Ayrıca, hizmetiniz başlatılır ve bağlamayı kabul ederse sistem onUnbind() yönteminizi çağırdığında, bir istemci hizmete bir sonraki bağlandığında onRebind() çağrısı almak istiyorsanız isteğe bağlı olarak true değerini döndürebilirsiniz. onRebind() void döndürür ancak istemci, onServiceConnected() geri çağırmasında IBinder değerini almaya devam eder. Aşağıdaki şekilde bu tür bir yaşam döngüsünün mantığı gösterilmektedir.

Başlatılan ve bağlanan bir hizmetin yaşam döngüsü: onStartCommand() ve onBind() çağrılır; tüm istemcilerin bağlantısı kaldırıldığında onUnbind() çağrılır; bir sonraki bağlamada, onUnbind() true değerini döndürdüyse onRebind() çağrılır, aksi takdirde onBind() çağrılır; hizmet durdurulduktan sonra onDestroy() çağrılır.
Şekil 1. Başlatılan ve bağlamaya da izin veren bir hizmetin yaşam döngüsü.

Başlatılan bir hizmetin yaşam döngüsü hakkında daha fazla bilgi için Hizmetlere genel bakış başlıklı makaleyi inceleyin.