#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:
Возможно, здесь может помочь linkToDeath().
Комментарии:
1. Я забыл упомянуть linkToDeath() — см. Обновление. Спасибо за напоминание.
2. Глядя на этот RemoteCallbackList , я бы сказал, что вы правы: смерть объявляется, если / когда поставщик IInterface умирает. Это может обеспечить решение — увы, не очень приятное — если вы требуете, чтобы ваши клиенты регистрировали некоторый (фиктивный) обратный вызов в вашей службе. Как только клиент умирает, его (фиктивный) интерфейс обратного вызова тоже умирает, уведомляя вашу службу.
3. А. Это звучит правдоподобно; Я проверю это. (Мой альтернативный план состоит в том, чтобы заставить каждого клиента работать в своем собственном процессе, уведомить службу о своем pid, а затем использовать Service monitor / proc, чтобы увидеть, исчезают ли процессы; так что у меня довольно высокая планка количества уродства, которое я могу терпеть, чтобы избежать этого!)