Статьи

Связанная служба Android: IPC с Messenger

В этом посте мы хотим поговорить об Android Bound Service . Связанный сервис — это сервис, который позволяет другим компонентам Android (таким как действия) связываться с ним и отправлять и получать данные. В предыдущем посте мы говорили о локальном сервисе и видели, как мы можем создать сервис, запустить и остановить его. Связанный сервис — это сервис, который может использоваться не только компонентами, работающими в том же процессе, что и локальный сервис, но и действия и сервисы, выполняющиеся в разных процессах, могут связываться с ним и отправлять и получать данные. Когда мы реализуем связанный сервис, мы всегда должны расширять класс Service, но мы также должны переопределять метод onBind . Этот метод возвращает объект, который реализует IBinder , который может использоваться для взаимодействия со службой.

Мы можем создать связанный сервис тремя способами:

  • Расширение интерфейса IBinder
  • Использование Messenger
  • Использование AIDL

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

Реализация связанного сервиса с Messenger

Сервис на основе Messenger может взаимодействовать с другими компонентами в различных процессах, известных как межпроцессное взаимодействие (IPC) , без использования AIDL. Для реализации такого сервиса нам нужно:

  • Обработчик службы : этот компонент обрабатывает входящие запросы от клиентов, которые взаимодействуют с самой службой.
  • Messenger : этот класс используется для создания объекта, реализующего интерфейс IBinder чтобы клиент мог взаимодействовать со службой.

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

android_boud_service_messenger [8]

Итак, во-первых, мы создаем класс, который реализует Service :

1
2
3
public class ConvertService extends Service {
..
}

Как уже говорилось ранее, нам нужен обработчик для реализации входящих запросов от клиентов, поэтому мы можем создать внутренний класс, например:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class ConvertHanlder extends Handler {
 
@Override
public void handleMessage(Message msg) {
    // This is the action
    int msgType = msg.what;
 
    switch(msgType) {
    case TO_UPPER_CASE: {
        try {
            // Incoming data
            String data = msg.getData().getString("data");
            Message resp = Message.obtain(null, TO_UPPER_CASE_RESPONSE);
            Bundle bResp = new Bundle();
            bResp.putString("respData", data.toUpperCase());
            resp.setData(bResp);
 
            msg.replyTo.send(resp);
        } catch (RemoteException e) {
 
            e.printStackTrace();
        }
        break;
    }
    default:
        super.handleMessage(msg);
    }
}

В handleMessage мы начинаем обрабатывать входящие запросы. Первое, что мы должны сделать, это «декодировать» тип запроса, который мы обрабатываем. Для этого мы можем использовать атрибут what класса Message . В зависимости от его значения мы выполняем различные операции: в нашем случае мы просто конвертируем в верхний регистр строку. Мы передаем строковое значение Bundle, прикрепленное к сообщению, так что в строке 12 мы получаем значение. Мы должны отправить ответ клиенту, поэтому мы создаем еще одно Сообщение (строка 13), которое содержит ответ, и присоединяем новый Пакет, содержащий преобразованную строку (строка 14-15). В строке 16 мы отправляем сообщение обратно клиенту (мы увидим его позже).

Таким образом, мы создали наш обработчик запросов, но нам нужно создать экземпляр IBinder, чтобы клиент мог использовать наш сервис. Для этого нам нужен мессенджер:

1
2
3
4
5
6
7
8
9
public class ConvertService extends Service {
    ...
    private Messenger msg = new Messenger(new ConvertHanlder());;
    ...
    @Override
    public IBinder onBind(Intent arg0) {               
        return msg.getBinder();
    }
}

В строке 3 создается новый экземпляр класса Messenger, передавая обработчик, который мы обсуждали ранее. В строке 6 мы переопределяем метод onBind и возвращаем экземпляр интерфейса IBinder. Наш Сервис готов. В конце мы определим это в Manifest.xml:

1
<service android:name=".ConvertService" android:process=":convertprc"/>

Обратите внимание, что мы использовали android:process поэтому служба работает не так, как клиент.

Клиент службы Android

Теперь нам нужно реализовать клиент, который привязывается к сервису и отправляет ему данные. Мы можем предположить, что клиент является Activity, которая позволяет пользователю вставить строку, которая должна быть преобразована в верхний регистр. Операция вызывает метод bindService для привязки к службе, созданной ранее. Когда мы связываемся с «удаленным» сервисом, используя метод bindService, нам необходимо предоставить методы обратного вызова, чтобы мы получали уведомление о завершении процесса связывания и могли «использовать» сервис. Мы должны создать класс, который реализует ServiceConnection для получения этого уведомления:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
..
private ServiceConnection sConn;
private Messenger messenger;
..
@Override
protected void onCreate(Bundle savedInstanceState) {
        // Service Connection to handle system callbacks
        sConn = new ServiceConnection() {
 
            @Override
            public void onServiceDisconnected(ComponentName name) {
                messenger = null;
            }
 
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                // We are conntected to the service
                messenger = new Messenger(service);
 
            }
        };
...
        // We bind to the service
        bindService(new Intent(this, ConvertService.class), sConn,
                Context.BIND_AUTO_CREATE);
..
}

В строке 8 мы создаем экземпляр ServiceConnection, переопределяющего его методы. В строке 18 мы создаем Messanger, который позже используем для получения экземпляра IBinder, чтобы мы могли отправлять сообщения нашему сервису. Наконец, в строке 24 мы связываемся с сервисом, определяя класс сервиса и интерфейс обратного вызова

Теперь нам нужен обработчик «получения» для управления ответом сервиса:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
// This class handles the Service response
class ResponseHandler extends Handler {
 
    @Override
    public void handleMessage(Message msg) {
        int respCode = msg.what;
 
        switch (respCode) {
        case ConvertService.TO_UPPER_CASE_RESPONSE: {
            result = msg.getData().getString("respData");
            txtResult.setText(result);
        }
        }
    }
 
}

Этот обработчик ведет себя так же, как и в реализации службы, он извлекает ответ из пакета, прикрепленного к сообщению, и показывает результат пользователю в строке 11.

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

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
btn.setOnClickListener(new View.OnClickListener() {
 
    @Override
    public void onClick(View v) {
 
        String val = edt.getText().toString();
        Message msg = Message
                .obtain(null, ConvertService.TO_UPPER_CASE);
 
        msg.replyTo = new Messenger(new ResponseHandler());
        // We pass the value
        Bundle b = new Bundle();
        b.putString("data", val);
 
        msg.setData(b);
 
        try {
            messenger.send(msg);
        } catch (RemoteException e) {                   
            e.printStackTrace();
        }
 
    }
 
});

Обратите внимание, что в строке 9 мы устанавливаем Messenger для ответа, который будет использоваться службой, когда он должен будет отправить ответ.

Ссылка: Android Bound Service: IPC с Messenger от нашего партнера JCG Франческо Аццолы в блоге Surviving с Android .