В этом посте мы хотим поговорить об Android Bound Service . Связанный сервис — это сервис, который позволяет другим компонентам Android (таким как действия) связываться с ним и отправлять и получать данные. В предыдущем посте мы говорили о локальном сервисе и видели, как мы можем создать сервис, запустить и остановить его. Связанный сервис — это сервис, который может использоваться не только компонентами, работающими в том же процессе, что и локальный сервис, но и действия и сервисы, выполняющиеся в разных процессах, могут связываться с ним и отправлять и получать данные. Когда мы реализуем связанный сервис, мы всегда должны расширять класс Service, но мы также должны переопределять метод onBind
. Этот метод возвращает объект, который реализует IBinder
, который может использоваться для взаимодействия со службой.
Мы можем создать связанный сервис тремя способами:
- Расширение интерфейса IBinder
- Использование Messenger
- Использование AIDL
В этом посте мы хотим проанализировать, как создать сервис Android с Messenger . Используя этот метод, мы можем создать сервис, который может использоваться компонентами в разных процессах. В этом случае мы используем Обработчик и Сообщение для обмена данными между сервисом и другими компонентами.
Реализация связанного сервиса с Messenger
Сервис на основе Messenger может взаимодействовать с другими компонентами в различных процессах, известных как межпроцессное взаимодействие (IPC) , без использования AIDL. Для реализации такого сервиса нам нужно:
- Обработчик службы : этот компонент обрабатывает входящие запросы от клиентов, которые взаимодействуют с самой службой.
- Messenger : этот класс используется для создания объекта, реализующего интерфейс
IBinder
чтобы клиент мог взаимодействовать со службой.
Итак, давайте реализуем Сервис. В качестве примера мы можем предположить, что мы хотим создать сервис, который получает строку, преобразует ее в верхний регистр и возвращает результат клиенту.
Итак, во-первых, мы создаем класс, который реализует 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 для ответа, который будет использоваться службой, когда он должен будет отправить ответ.