page.title=Привязанные службы parent.title=Службы parent.link=services.html @jd:body

    Содержание документа

    1. Основы
    2. Создание привязанной службы
      1. Расширение класса Binder
      2. Использование объекта Messenger
    3. Привязка к службе
    4. Управление жизненным циклом привязанной службы

    Ключевые классы

    1. {@link android.app.Service}
    2. {@link android.content.ServiceConnection}
    3. {@link android.os.IBinder}

    Примеры

    1. {@code RemoteService}
    2. {@code LocalService}

    См. также:

    1. Службы

Привязанная служба предоставляет интерфейс типа клиент-сервер. Привязанная служба позволяет компонентам приложения (например, операциям) взаимодействовать со службой, отправлять запросы, получать результаты и даже делать то же самое с другими процессами через IPC. Привязанная служба обычно работает, пока другой компонент приложения привязан к ней. Она не работает постоянно в фоновом режиме.

В этом документе рассказывается, как создать привязанную службу, включая привязку службы к другим компонентам приложения. Также рекомендуем обратиться к статье Службы, чтобы узнать подробнее о службах, например, об организации отправки уведомлений от службы, настройке службы на работу на переднем плане и т. д.

Основы

Привязанная служба представляет собой реализацию класса {@link android.app.Service}, которая позволяет другим приложениям привязываться к нему и взаимодействовать с ним. Чтобы обеспечить привязку службы , сначала необходимо реализовать метод обратного вызова {@link android.app.Service#onBind onBind()}. Этот метод возвращает объект {@link android.os.IBinder}. Он определяет программный интерфейс, с помощью которого клиенты могут взаимодействовать со службой.

Для привязки к службе клиент может вызвать метод {@link android.content.Context#bindService bindService()}. После привязки он должен предоставить реализацию метода {@link android.content.ServiceConnection}, который служит для отслеживания подключения к службе. Метод {@link android.content.Context#bindService bindService()} возвращается незамедлительно без значения, однако , когда система Android устанавливает подключение клиент-служба, она вызывает метод {@link android.content.ServiceConnection#onServiceConnected onServiceConnected()} для {@link android.content.ServiceConnection}, чтобы выдать объект {@link android.os.IBinder}, который клиент может использовать для взаимодействия со службой.

Одновременно к службе могут подключиться сразу несколько клиентов. Однако система вызывает метод {@link android.app.Service#onBind onBind()} вашей службы для получения объекта {@link android.os.IBinder} только при первой привязке клиента. После чего система выдает такой же объект {@link android.os.IBinder} для любых дополнительных клиентов, которые выполняют привязку, без повторного вызова метода {@link android.app.Service#onBind onBind()}.

Когда отменяется привязка последнего клиента от службы, система уничтожает службу (если только служба не была так же запущена методом {@link android.content.Context#startService startService()}).

Самую важную роль в реализации привязанной службы играет определение интерфейса, который возвращает ваш метод обратного вызова {@link android.app.Service#onBind onBind()}. Существует несколько различных способов определения интерфейса {@link android.os.IBinder} службы. Каждый из них рассматривается в следующем разделе.

Создание привязанной службы

При создании службы, обеспечивающей привязку, требуется объект {@link android.os.IBinder}, который обеспечивает программный интерфейс, с помощью которого клиенты могут взаимодействовать со службой. Существует три способа определения такого интерфейса:

Расширение класса Binder
Если служба является частной и предоставляется в рамках вашего собственного приложения, а также выполняется в том же процессе, что и клиент (общий процесс), создавать интерфейс следует путем расширения класса {@link android.os.Binder} и возврата его экземпляра из метода {@link android.app.Service#onBind onBind()}. Клиент получает объект {@link android.os.Binder}, после чего он может использовать его для получения прямого доступа к общедоступным методам, имеющимся либо в реализации {@link android.os.Binder}, либо даже в {@link android.app.Service}.

Этот способ является предпочтительным, когда служба просто выполняется в фоновом режиме для вашего приложения. Этот способ не подходит для создания интерфейса только тогда, когда ваша служба используется другими приложениями или в отдельных процессах.

Использование объекта Messenger
Если необходимо, чтобы интерфейс службы был доступен для разных процессов, его можно создать с помощью объекта {@link android.os.Messenger}. Таким образом, служба определяет объект {@link android.os.Handler}, соответствующий различным типам объектов {@link android.os.Message}. Этот объект {@link android.os.Handler} является основой для объекта {@link android.os.Messenger}, который, в свою очередь, предоставляет клиенту объект {@link android.os.IBinder}, благодаря чему последний может отправлять команды в службу с помощью объектов {@link android.os.Message}. Кроме того, клиент может определить объект {@link android.os.Messenger} для самого себя, что позволяет службе возвращать сообщения клиенту.

Это самый простой способ организовать взаимодействие процессов, поскольку {@link android.os.Messenger} организует очередь всех запросов в рамках одного потока, поэтому вам не нужно делать свою службу потокобезопасной.

Использование языка AIDL
AIDL (Android Interface Definition Language) выполняет всю работу по разделению объектов на примитивы, которые операционная система может распознать и распределить по процессам для организации взаимодействия между ними (IPC). Предыдущий способ с использованием объекта {@link android.os.Messenger} фактически основан на AIDL, поскольку это его базовая структура. Как уже упоминалось выше, объект {@link android.os.Messenger} создает очередь из всех запросов клиентов в рамках одного потока, поэтому служба одновременно получает только один запрос. Однако, если необходимо, чтобы служба обрабатывала одновременно сразу несколько запросов, можно использовать AIDL напрямую. В таком случае ваша служба должна поддерживать многопоточность и должна быть потокобезопасной.

Чтобы использовать AIDL напрямую, необходимо создать файл {@code .aidl}, который определяет программный интерфейс. Этот файл используется инструментами SDK Android для создания абстрактного класса, который реализует интерфейс и обеспечивает взаимодействие процессов, и который в дальнейшем можно расширить в службе.

Примечание. В большинстве приложений не следует использовать AIDL для создания и привязки службы, поскольку для этого может потребоваться поддержка многопоточности, что, в свою очередь, может привести к более сложной реализации. Поэтому AIDL не подходит для большинства приложений, и в этой статье мы не будем рассматривать использование этого способа для вашей службы. Если же вы точно уверены, что вам необходимо использовать AIDL напрямую, обратитесь к статье AIDL.

Расширение класса Binder

Если ваша служба используется только локальным приложением и не взаимодействует с разными процессами, можно реализовать собственный класс {@link android.os.Binder}, с помощью которого клиент получает прямой доступ к общедоступным методам в службе.

Примечание. Этот вариант подходит только в том случае, если клиент и служба выполняются внутри одного приложения и процесса, что является наиболее распространенной ситуацией. Например, расширение класса отлично подойдет для музыкального приложения, в котором необходимо привязать операцию к собственной службе приложения, которая воспроизводит музыку в фоновом режиме.

Вот как это сделать:

  1. Создайте в вашей службе экземпляр класса {@link android.os.Binder} со следующими характеристиками:
  2. Верните этот экземпляр класса {@link android.os.Binder} из метода обратного вызова {@link android.app.Service#onBind onBind()}.
  3. В клиенте получите класс {@link android.os.Binder} от метода обратного вызова {@link android.content.ServiceConnection#onServiceConnected onServiceConnected()} и выполните вызовы к привязанной службе с помощью предоставленных методов.

Примечание. Служба и клиент должны выполняться в одном и том же приложении , поскольку в этом случае клиент может транслировать возвращенный объект и надлежащим образом вызывать его API-интерфейсы. Кроме того, служба и клиент должны выполняться в рамках одного и того же процесса, поскольку этот способ не подразумевает какого-либо распределения по процессам.

Ниже представлен пример службы, которая предоставляет клиентам доступ к методам посредством реализации класса {@link android.os.Binder}:

public class LocalService extends Service {
    // Binder given to clients
    private final IBinder mBinder = 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 mBinder;
    }

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

Объект {@code LocalBinder} предоставляет для клиентов метод {@code getService()}, чтобы они могли получить текущий экземпляр класса {@code LocalService}. Благодаря этому клиенты могут вызывать общедоступные методы в службе. Например, клиенты могут вызвать метод {@code getRandomNumber()} из службы.

Ниже представлен пример операции, которая выполняет привязку к классу {@code LocalService} и вызывает метод {@code getRandomNumber()} при нажатии кнопки:

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

    /** 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 were something that might hang, then this request should
            // occur 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 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;
        }
    };
}

В примере выше показано, как клиент привязывается к службе с помощью реализации {@link android.content.ServiceConnection} и обратного вызова {@link android.content.ServiceConnection#onServiceConnected onServiceConnected()}. В следующем разделе представлена более подробная информация об этом процессе привязки к службе.

Примечание. В примере выше не выполняется явная отмена привязки к службе, однако всем клиентам следует отменять привязку в соответствующие сроки (например, когда операция приостанавливается).

Примеры кода представлены в статьях, посвященных классам {@code LocalService.java} и {@code LocalServiceActivities.java}, в ApiDemos.

Использование объекта Messenger

Если необходимо, чтобы служба взаимодействовала с удаленными процессами, для предоставления интерфейса службы можно воспользоваться объектом {@link android.os.Messenger}. Такой подход позволяет организовать взаимодействие между процессами (IPC) без необходимости использовать AIDL.

Вот краткий обзор того, как следует использовать объект {@link android.os.Messenger}:

Таким образом, для клиента отсутствуют «методы» для отправки вызова службе. Вместо этого клиент отправляет «сообщения» (объекты {@link android.os.Message}), которые служба получает в своем объекте {@link android.os.Handler}.

Ниже представлен пример службы, которая использует интерфейс {@link android.os.Messenger}:

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.
     */
    class IncomingHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_SAY_HELLO:
                    Toast.makeText(getApplicationContext(), "hello!", Toast.LENGTH_SHORT).show();
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }

    /**
     * Target we publish for clients to send messages to IncomingHandler.
     */
    final Messenger mMessenger = new Messenger(new IncomingHandler());

    /**
     * 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();
        return mMessenger.getBinder();
    }
}

Обратите внимание, что метод {@link android.os.Handler#handleMessage handleMessage()} в объекте {@link android.os.Handler} — это место, где служба получает входящие объекты {@link android.os.Message} и решает, что делать дальше, руководствуясь элементом {@link android.os.Message#what}.

Клиенту требуется лишь создать объект {@link android.os.Messenger} на основе объекта {@link android.os.IBinder}, возвращенного службой, и отправить сообщение с помощью метода {@link android.os.Messenger#send send()}. Ниже представлен пример того, как простая операция выполняет привязку к службе и отправляет ей сообщение {@code MSG_SAY_HELLO}:

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 mBound;

    /**
     * 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);
            mBound = 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;
            mBound = false;
        }
    };

    public void sayHello(View v) {
        if (!mBound) 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 (mBound) {
            unbindService(mConnection);
            mBound = false;
        }
    }
}

Обратите внимание, что в этом примере не показано, как служба отвечает клиенту. Если требуется, чтобы служба реагировала, необходимо создать объект {@link android.os.Messenger} и в клиенте. Затем, когда клиент получает обратный вызов {@link android.content.ServiceConnection#onServiceConnected onServiceConnected()}, она отправляет в службу объект {@link android.os.Message}, который включает объект {@link android.os.Messenger} клиента в качестве значения параметра {@link android.os.Message#replyTo} метода {@link android.os.Messenger#send send()}.

Пример организации двустороннего обмена сообщениями приведен в примерах кода {@code MessengerService.java} (служба) и {@code MessengerServiceActivities.java} (клиент).

Привязка к службе

Для привязки к службе компоненты приложения (клиенты) могут использовать метод {@link android.content.Context#bindService bindService()}. После этого система Android вызывает метод {@link android.app.Service#onBind onBind()} службы, который возвращает объект {@link android.os.IBinder} для взаимодействия со службой.

Привязка выполняется асинхронно. {@link android.content.Context#bindService bindService()} возвращается сразу же и не возвращает клиенту объект {@link android.os.IBinder}. Для получения объекта {@link android.os.IBinder} клиенту необходимо создать экземпляр {@link android.content.ServiceConnection} и передать его в метод {@link android.content.Context#bindService bindService()}. Интерфейс {@link android.content.ServiceConnection} включает метод обратного вызова, который система использует для того, чтобы выдать объект {@link android.os.IBinder}.

Примечание. Выполнить привязку к службе могут только операции, другие службы и поставщики контента — вы не можете самостоятельно выполнить привязку к службе из ресивера.

Поэтому для привязки к службе из клиента необходимо выполнить указанные ниже действия.

  1. Реализуйте интерфейс {@link android.content.ServiceConnection}.

    Ваша реализация должна переопределять два метода обратного вызова:

    {@link android.content.ServiceConnection#onServiceConnected onServiceConnected()}
    Система вызывает этот метод, чтобы выдать объект {@link android.os.IBinder}, возвращенный методом {@link android.app.Service#onBind onBind()}службы.
    {@link android.content.ServiceConnection#onServiceDisconnected onServiceDisconnected()}
    Система Android вызывает этот метод в случае непредвиденной потери подключения к службе, например при сбое в работе службы или в случае ее завершения. Этот метод не вызывается, когда клиент отменяет привязку.
  2. Вызовите метод {@link android.content.Context#bindService bindService()}, передав в него реализацию интерфейса {@link android.content.ServiceConnection}.
  3. Когда система вызывает ваш метод обратного вызова {@link android.content.ServiceConnection#onServiceConnected onServiceConnected()}, вы можете приступить к выполнению вызовов к службе с помощью методов, определенных интерфейсом.
  4. Чтобы отключиться от службы, вызовите метод {@link android.content.Context#unbindService unbindService()}.

    В случае уничтожения клиента выполняется отмена его привязки к службе, однако вам всегда следует отменять привязку по завершении взаимодействия со службой или в случае приостановки операции, чтобы служба могла завершить свою работу, когда она не используется. (Более подробно подходящее время для привязки и ее отмены рассматриваются далее в этом документе).

Ниже представлен пример фрагмента кода для подключения клиента к созданной выше службе путем расширения класса Binder — клиенту нужно лишь передать возвращенный объект {@link android.os.IBinder} в класс {@code LocalService} и запросить экземпляр {@code LocalService}:

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");
        mBound = false;
    }
};

С помощью этого интерфейса {@link android.content.ServiceConnection} клиент может выполнить привязку к службе, передав ее в метод {@link android.content.Context#bindService bindService()}. Например:

Intent intent = new Intent(this, LocalService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);

Дополнительные примечания

Ниже представлены некоторые важные замечания о привязке к службе.

Пример кода, в котором показан порядок привязки к службе, см. в статье, посвященной классу {@code RemoteService.java}, в ApiDemos.

Управление жизненным циклом привязанной службы

Когда выполняется отмена привязки службы ко всем клиентам, система Android уничтожает такую службу (если она не была запущена вместе с {@link android.app.Service#onStartCommand onStartCommand()}). В таком случае вам не нужно управлять жизненным циклом своей службы, если она исключительно привязанная служба — система Android управляет ей за вас на основании привязки службы к любым другим клиентам.

Однако, если вы решите реализовать метод обратного вызова {@link android.app.Service#onStartCommand onStartCommand()}, вам необходимо явным образом остановить службу, поскольку в этом случае она считается запущенной. В таком случае служба выполняется до тех пор, пока сама не остановит свою работу с помощью метода {@link android.app.Service#stopSelf()} или до тех пор, пока другой компонент не вызовет метод {@link android.content.Context#stopService stopService()}, независимо от привязки службы к каким-либо клиентам.

Кроме того, если ваша служба запущена и принимает привязку, то при вызове системой вашего метода {@link android.app.Service#onUnbind onUnbind()} вы также можете вернуть {@code true}, если желаете получить вызов к {@link android.app.Service#onRebind onRebind()} при следующей привязке к службе (вместо получения вызова к методу {@link android.app.Service#onBind onBind()}). Метод {@link android.app.Service#onRebind onRebind()} возвращает значение void, однако клиент по-прежнему получает объект {@link android.os.IBinder} в своем методе обратного вызова {@link android.content.ServiceConnection#onServiceConnected onServiceConnected()}. На рисунке 1 ниже иллюстрируется логика жизненного цикла такого рода.

Рисунок 1. Жизненный цикл запущенной службы, для которой выполняется привязка.

Дополнительные сведения о жизненном цикле уже запущенной службы представлены в статье Службы.