Передача активных объектов между службами через AIDL

Я пытаюсь использовать общий объект для нескольких служб в разных пакетах. Каждая служба должна вызывать один и тот же объект.

Например, служба A (из APK A) создает экземпляр настраиваемого объекта, и я хочу, чтобы службы B и C (из APK B и C) извлекали ссылку на этот объект и вызывали какой-либо его метод.

Я нашел в справочнике Android, что это должно быть возможно с помощью Parcel:

Активные объекты

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

Объекты Binder являются основным средством общего кросс-процесса Android системы связи. Интерфейс IBinder описывает абстрактный протокол с объектом Binder. Любой такой интерфейс можно записать в Посылка, и после прочтения вы получите либо исходный объект реализации этого интерфейса или специальной реализации прокси, которая осуществляет обратную связь с исходным объектом. Способы использования writeStrongBinder (IBinder), writeStrongInterface (IInterface), readStrongBinder(), writeBinderArray (IBinder []), readBinderArray (IBinder []), createBinderArray(), writeBinderList (List), readBinderList (List), createBinderArrayList().

Я попытался сделать это, передав мой объект (который расширяет связующее) через AIDL, но ничего не работает, я всегда получаю исключение ClassCastException, когда пытаюсь получить ссылку из метода createFromParcel (Parcel in).

Пример моего кода:

public class CustomObject extends Binder implements Parcelable {
 public CustomObject() {
 super();
 }
 public static final Parcelable.Creator<customobject> CREATOR = new Parcelable.Creator<customobject>() {
 public CustomObject createFromParcel(Parcel in) {
 IBinder i = in.readStrongBinder();
 // HOW TO RETRIEVE THE REFERENCE ??
 return null;
 }
 @Override
 public CustomObject[] newArray(int size) {
 return null;
 }
 };
 @Override
 public int describeContents() {
 return 0;
 }
 @Override
 public void writeToParcel(Parcel dest, int flags) {
 dest.writeStrongBinder(this);
 }
}
</customobject></customobject>

Кто-нибудь уже это сделал?

Спасибо заранее!

3 ответа

Вот два подхода.

Простой: используйте helpl для самого объекта

  • Кажется, у вас есть существующий интерфейс AIDL, через который вы передаете этот "пользовательский объект" в качестве посылки. Не делай этого. Вместо этого:
  • Объект, который вы проходите, должен сам описываться AIDL. Скажем, например, вы называете это ICustomObject.aidl.
  • В этом случае вам не нужно делать объект Parcelable. Вам, вероятно, даже не нужно писать вышеуказанный код; просто используйте один описанный тип AIDL в другом. Например, добавьте такую ​​строку в основной AIDL для службы A:

    ICustomObject getCustomObject();
  • В сервисе A, в классе Stub, который у вас уже есть, вам нужно просто вернуть что-то наследующее от ICustomObject.

  • В службах B и C вы можете просто вызвать этот метод, чтобы получить ICustomObject. Просто! Нет посылок, нет readStrongBinder(), ничего.

Harder

Если вы делаете это выше, привязка к инструменту Android генерирует код Java, который сортирует и отменяет объект. Вместо этого вы можете написать код самостоятельно.

ICustomObject myObjectWhichActuallyLivesInAnotherProcess = ICustomObject.Stub.asInterface(parcel.readStrongBinder())

или даже

ICustomObject myObjectWhichActuallyLivesInAnotherProcess = (ICustomObject)parcel.readStrongBinder().queryLocalInterface("com.your.custom.object");

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

Заметка о совместном использовании классов

Возможно, вам захочется создать проект библиотеки "Android", в котором есть ICustomObject.aidl внутри него, чтобы вы могли делиться полученными классами между проектами, которые строят A, B и C.


Изучив это довольно подробно, я не думаю, что это действительно можно сделать. Полученное вами ClassCastException является результатом литья BinderProxy (который является частным классом, который расширяет IBinder) до вашего фактического класса (CustomObject). A BinderProxy всегда передается при обращении к Binders по другим процессам, и это объект, на который они ссылаются, когда они указывают "либо исходный объект, реализующий этот интерфейс, либо специальную реализацию прокси". BinderProxy позволяет вам вызвать метод onTransact() IBinder, но не более того.

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

licensed under cc by-sa 3.0 with attribution.