Как выбрать какой сервис для обработки намерения - PullRequest
2 голосов
/ 25 января 2012

У меня есть приложение, которое использует службу, которая включена в пакет приложения.

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

Если на устройстве установлено только одно приложение, это работает нормально.Однако, если на устройстве установлены оба приложения, они оба используют службу из того пакета, который был установлен не так давно, т. Е. Самого старого.Это проблематично по двум причинам: 1) старая служба не обрабатывает новые вызовы (очевидно) 2) при использовании службы, связанной с другим приложением, служба в конечном итоге использует сохраненные данные этого другого приложения вместо своих собственных.

В краткосрочной перспективе я просто хочу, чтобы каждое приложение взаимодействовало со своей версией сервиса.Можно ли сделать это, не заставляя каждую связанную услугу реагировать на уникальное намерение, и чтобы каждое приложение использовало это уникальное намерение?

В долгосрочной перспективе было бы неплохо, если бы оба приложения по желанию могли общаться с новейшей версиейслужбы, и пусть они обмениваются данными, и хорошо играют с любым удаленным приложением.Какой правильный способ сделать это?Данные в основном находятся в базе данных SQLite, которая хранится там, где указано Context.getDatabasePath(), что, конечно, зависит от приложения и стирается, если это приложение удаляется.

Вот что я пробовал до сих пор:

Я могу использовать PackageManager.queryIntentServices(), чтобы получить список ResolveInfo с обеими версиями сервиса.Глядя на ResolveInfo.serviceInfo.applicationInfo.packageName, я могу сказать, что есть что.Однако, по-видимому, нет никакого способа использовать это ResolveInfo, чтобы указать, какой из двух сервисов я хочу обработать намерение.

Похоже, что это должно быть правильно:

serviceIntent.setPackage(context.getApplicationContext().getApplicationInfo().packageName);

, поскольку этот packageName совпадает с тем, который я хочу найти.Действительно, когда я звоню queryIntentServices с этим измененным намерением, я получаю список обратно, содержащий только службу в моем пакете.Тем не менее, когда я пытаюсь связать службу с этим намерением, она выдает исключение безопасности.

java.lang.RuntimeException: Unable to bind to service my.service.package.name.MyServiceClass@40531558 with Intent { act=my.intent.string pkg=my.app.package.name }: java.lang.SecurityException: Not allowed to start service Intent { act=RuntimeException: Unable to bind to service my.service.package.name.MyServiceClass } without permission private to package

Основываясь на поиске, я также попытался создать намерение, выполнив:

Intent serviceIntent = new Intent().setClassName(
    "my.service.package.name",
    "my.service.package.name.ServiceClassName");

Это не разрешается никаким сервисам вообще.

Я также пытался изменить android:exported= в записи сервиса в AndroidManifest.xml на true, false или не указано, в различных комбинациях в обоих приложениях,но безрезультатно.(Я надеялся, что если установить для них обоих значение false, то каждое приложение сможет видеть только свою копию службы.)

Ответы [ 2 ]

3 голосов
/ 11 октября 2012

Вот решение, которое работает:

В манифесте добавить метаданные для Сервиса. Имя должно быть что-то вроде «запуска службы», а значение должно совпадать с именем действия в фильтре намерений.

<meta-data android:name="Service Launcher"
           android:value="your.service.action.string.here" />

<intent-filter>
    <action android:name="your.service.action.string.here" />
</intent-filter>

Затем в своем коде добавьте метод доступа для получения метаданных и используйте его для создания намерения.

public static String getServiceIntentAction(Context context) {
    String action = "";
    try {
        PackageManager pm = context.getPackageManager();
        android.content.ComponentName cn = new android.content.ComponentName(context.getPackageName(), "com.your.ServiceClass");
        android.content.pm.ServiceInfo si = pm.getServiceInfo(cn, PackageManager.GET_META_DATA);
        action = si.metaData.getString("Service Launcher");
    } catch(android.content.pm.PackageManager.NameNotFoundException nnfe) {}
    return action;
}

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

Чтобы оба приложения использовали один и тот же сервис, сервис, вероятно, должен быть установлен как отдельный объект.

0 голосов
/ 25 января 2012

Вы пробовали использовать категории? Для каждой категории в намерении потенциально принимающий компонент должен соответствовать каждой категории, чтобы считаться получателем.

Служба А имеет категорию "1" и намерена перейти в категорию "1". Служба B имеет категорию «1» и категорию «2» (при условии, что она все еще поддерживает все в версии «1». При этом старое приложение может общаться с более новой службой, но более новое приложение не может связываться со старой службой.

...