Получение уведомлений, когда клиенты подключаются и отсоединяются к службе Android

#android #android-service #android-binder

#Android #android-сервис #android-binder

Вопрос:

У меня есть служба Android, которая экспортирует интерфейс RPC через AIDL. Интерфейс ориентирован на подключение, где клиент подключается к нему, выполняет с ним транзакции, а затем отключается при выходе.

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

Есть ли какой-либо способ для службы получать автоматическое уведомление, когда новый клиент подключается и отсоединяется от интерфейса? Я нашел onBind() и onUnbind() , но это не одно и то же; они сообщают мне, используется ли интерфейс или нет. onUnbind() вызывается только при отсоединении последнего клиента.

Есть идеи? (И дизайн службы контролируется внешними требованиями и не может быть изменен …)

Обновление: я совершенно забыл упомянуть, что я рассмотрел linkToDeath() , что почти делает именно то, что я хочу, но, похоже, работает только наоборот — это позволяет клиенту получать уведомления, когда служба умирает. Когда я попробовал, казалось, ничего не произошло. Документация немного неоднородна; кто-нибудь точно знает, работает ли это так, как я хочу? И если да, то как заставить его сообщить мне, какой клиент умер?

Обновление обновление: я решил эту проблему, но только путем обмана. Я переопределил это. Мое приложение на самом деле в основном написано на C с использованием NDK, отсюда и странный дизайн сервиса; поэтому я перенес проблему в мир C и создал крошечный вспомогательный процесс, который напрямую взаимодействует с собственными частями моего приложения, используя доменные сокеты Unix. Это быстро, очень мало и почти пуленепробиваемо, но все равно обманывает. Итак, хотя моя проблема теперь решена, я все же хотел бы знать, каков фактический ответ.

Комментарии:

1. Когда вы говорите, что это невозможно изменить, допустимо ли добавлять методы в экспортируемый интерфейс, если вы не меняете существующие методы? Или, может быть, служба отправляет широковещательные передачи, намерения которых могут быть получены извне с помощью broadcastreceivers?

2. Я могу менять интерфейс сколько угодно, но общая архитектура, ориентированная на подключение, должна оставаться. Я мог бы заставить службу постоянно отправлять широковещательные сообщения, на которые клиенты должны отвечать, и предполагается, что клиенты, которые не отвечают, умерли, но … фу. Это то, что вы имели в виду? (Эй, если это сработает, я попробую.)

Ответ №1:

Используйте linkToDeath() (но делайте это наоборот), инициализируя новый Binder объект на стороне клиента и отправляя его через AIDL в службу.
Затем вы можете зарегистрироваться в DeathRecipient вашего клиента внутри вашей службы и получать уведомления от фреймворка при аварийном завершении процесса клиента.

Пример кода —

файл .AIDL внутри вашего — Создайте метод для передачи Binder объекта от клиента к службе

 void registerProcessDeath(in IBinder clientDeathListener);
  

На стороне клиента — инициализируйте новый объект и передайте его своей службе через интерфейс AIDL.

 public void onServiceConnected(ComponentName className, IBinder service) {
    mIMyAidlInterface = IMyAidlInterface.Stub.asInterface(service);
    //Call the registerProcessDeath method from the AIDL file and pass 
    //the new Binder object
    mIMyAidlInterface.registerProcessDeath(new Binder());
}
  

На стороне службы — получите клиента Binder и зарегистрируйтесь у него linkToDeath() .

 private IBinder mBinder; //Instance variable

private final IMyAidlInterface.Stub mStub = new IMyAidlInterface.Stub() {

    @Override
    public void registerProcessDeath(IBinder clientDeathListener) {
        mBinder = clientDeathListener;
        //Create new anonymous class of IBinder.DeathRecipient()
        clientDeathListener.linkToDeath(new IBinder.DeathRecipient() {
               @Override
               public void binderDied() {
               //Do your stuff when process exits abnormally here.
               //Unregister from client death recipient
               mBinder.unlinkToDeath(this,0);
               }
        },0);
    }
};
  

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

Ответ №2:

Комментарии:

1. Я забыл упомянуть linkToDeath() — см. Обновление. Спасибо за напоминание.

2. Глядя на этот RemoteCallbackList , я бы сказал, что вы правы: смерть объявляется, если / когда поставщик IInterface умирает. Это может обеспечить решение — увы, не очень приятное — если вы требуете, чтобы ваши клиенты регистрировали некоторый (фиктивный) обратный вызов в вашей службе. Как только клиент умирает, его (фиктивный) интерфейс обратного вызова тоже умирает, уведомляя вашу службу.

3. А. Это звучит правдоподобно; Я проверю это. (Мой альтернативный план состоит в том, чтобы заставить каждого клиента работать в своем собственном процессе, уведомить службу о своем pid, а затем использовать Service monitor / proc, чтобы увидеть, исчезают ли процессы; так что у меня довольно высокая планка количества уродства, которое я могу терпеть, чтобы избежать этого!)