bindService из другого приложения, но тот же идентификатор пользователя / процесс
Речь идет о IPC на основе памяти (как в примере с LocalService), но для двух приложений, работающих в одном процессе:
У меня есть два приложения (App1
, App2
) и общий проект (Shared
), который определяет некоторые интерфейсы и абстрактные классы для обоих приложений:
Shared (regular Java project, references android.jar)
- abstract myAbstractService
- Binder myBinder
App1 (Android Project, references Shared)
- MainActivity
App2 (Android Project, references Shared)
- myService extends myAbstractService
Оба приложения работают в одном и том же процессе (my.process
, определенный в <application>
), App2
издает com.www.app2.myService
:
<-- Both Apps run in the same process -->
<manifest <!-- *snip* --> android:sharedUserId="my.shareduser">
<!-- ... -->
<application <!-- *snip* --> android:process="my.process">
<-- App2 exports the service -->
<service android:name="com.www.app2.myService" android:exported="true">
<intent-filter>
<action android:name="com.www.app2.myService" />
</intent-filter>
</service>
Это абстрактный myAbstractService
(myService
пока не добавляю ничего нового):
abstract public class GameClient extends Service
{
private static final String LOGTAG = "GameClient";
private myBinder binder = new myBinder();
public IBinder onBind(Intent intent)
{
Log.d(LOGTAG, "onBind()");
return this.binder;
}
public class myBinder extends Binder
{
public void sendMessage()
{
Log.d(LOGTAG, "sendMessage()");
}
}
}
Когда я пытаюсь связать с myService
(App2) из моего MainActivity
(App1):
public void onServiceConnected(ComponentName name, IBinder service)
{
Log.d("MS", service.getClass().toString());
main.this.t = (myBinder) service; // Exception in this line of course
}
Я получаю исключение:
DEBUG / MS (5464): класс com.www.shared.myBinder
ОШИБКА /AndroidRuntime(5464): java.lang.ClassCastException: com.www.shared.myBinder
Поскольку оба приложения работают в одном и том же процессе, связь, связанная с памятью, должна работать (по крайней мере, я так думал). Я действительно не хочу использовать общение на основе сообщений или широковещательной рассылки, поскольку я отправлю довольно много сообщений.
Я подозреваю, что это исключение происходит из-за двух разных загрузчиков классов, используемых для одного и того же класса? Этот подход просто невозможен / неправильный, или я что-то упустил?
Обновить:
Моя цель - написать очень модульное приложение, в котором App1 используется как приложение для делегирования и запуска других модулей (приложений). Как я не хочу грузить App1
с каждым приложением, в зависимости от него, я сделал это своим собственным приложением.
Допустим, у меня есть третье приложение (App3, проект Android). App2 и App3 оба запускаются App1 (отвечает за настройку соединений, в то время как App2 и App3 предоставляют разную логику приложения (но имеют одинаковый интерфейс).
Во-вторых, я думаю, что это можно решить и с помощью библиотеки Android (App1 & Shared объединены в библиотеку с App2
а также App3
запуск Activity из этой библиотеки и ожидание результата)? Однако данные не могут быть отправлены (сетевые подключения), и я понятия не имею, как эта библиотека может распространяться независимо на рынке Android (например, App2
а также App3
там публикуются, но просят установить библиотеку тоже). Это решило бы эту проблему вообще?
3 ответа
Вы правы, вы получаете это исключение, потому что задействованы два загрузчика классов.
Я добавил следующие две строки кода:
@Override
public void onServiceConnected(ComponentName name, IBinder service)
{
...
Log.e(TAG, "Expected class loader: "+myBinder.class.getClass().getClassLoader());
Log.e(TAG, "Class loader: "+service.getClass().getClassLoader());
...
}
И получил эти журналы:
Expected class loader: java.lang.BootClassLoader@4001bdb0
Class loader: dalvik.system.PathClassLoader[/data/app/com.inazaruk.shared.service-2.apk]
Из журналов ясно, что был использован другой загрузчик классов. Это на самом деле имеет смысл, как вы могли бы установить App1
приложение через неделю с другой версией myBinder
класс (или любой другой класс, который передается через myBinder
интерфейс прямо или косвенно).
Обновлено:
Вы должны придерживаться библиотек Android в вашем сценарии. Обратите внимание, что библиотеки Android напрямую встроены в приложение, которое на них ссылается. Они не распространяются отдельно. Вот мой пост, объясняющий, как библиотеки Android отличаются от простых jar-файлов, и другие важные нюансы.
Вы по-прежнему имеете высокую степень модульности в библиотеке Android, поскольку окончательное приложение Android включает только модули, которые оно использует. Но это модульность во время компиляции, а не модульность во время выполнения.
Однако есть некоторые проблемы с библиотеками Android:
- Вам необходимо скопировать объявления всех компонентов в библиотеки
AndroidManifest.xml
в приложения для AndroidAndroidManifest.xml
, - В настоящее время библиотеки не поддерживают пользовательские атрибуты XML (см. Мои сообщения здесь и здесь).
- В настоящее время библиотеки не поддерживают ресурсы (см. Мой пост здесь).
#1 планируется исправить в ближайшее время (в соответствии с дорожной картой поддержки сборки). #2 и #3 могут быть исправлены в следующем выпуске инструментов платформы SDK.
Попробовал это сам некоторое время назад: подобные вопросы ранее задавались в ClassCastException при привязке к локальному сервису из другого Activity и https://stackru.com/questions/3162538/2-apks-running-in-1-process-sharing-code-and-data. Конечный результат в значительной степени тот же: процесс совместного использования НЕ похож на обмен кодом из-за природы иерархии загрузчиков классов (аналогично тому, как разделены загрузчики классов J2EE).
Классы происходят из разных файлов DEX, так что с технической точки зрения классы разные, даже если интерфейсы одинаковы. Я не верю, что то, что вы хотите сделать, возможно: я вижу AIDL в вашем будущем.
У вас есть класс myBinder в пространстве имен com.www.shared? Потому что это то, что он ищет и говорит, что там нет. Я вижу, у вас есть класс, но в каком пространстве имен он находится?