bindService никогда не вызывает onServiceConnected

Моя компания выпускает SDK, поставляемый в виде файла aar библиотеки Android. В рамках этого SDK мы определяем сервис:

    <service
        android:name=".session.internal.ChatSession"
        android:description="@string/description"
        android:enabled="true"
        android:exported="false"
        android:label="Network communication service"
    />

Затем эта служба запускается и связывается с дополнительным кодом в SDK:

public boolean bindService(final Runnable whenBound) {
    if (connection == null) {
        // Make sure the service is running
        boolean success = startService();
        if(BuildConfig.DEBUG && !success) {
            throw new AssertionError("startService failed");
        }

        connection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                chatSession = (IChatSession) service;
                if(whenBound != null) {
                    whenBound.run();
                }
            }

            @Override
            public void onServiceDisconnected(ComponentName name) {
                chatSession = null;
                connection = null;
            }
        };

        success = context.bindService(new Intent(context, ChatSession.class), connection, Context.BIND_IMPORTANT);
        if(BuildConfig.DEBUG && !success) {
            throw new AssertionError("bindService failed");
        }

        return success;
    }

    if(whenBound != null) {
        whenBound.run();
    }

    return true;
}

boolean startService() {
    boolean success = true;

    if(!isServiceRunning()) {
        success = context.startService(new Intent(context, ChatSession.class)) != null;
    }
    return success;
}

Все это прекрасно работает, пока на мобильном устройстве установлено только одно приложение, использующее SDK.

Поскольку служба явно не экспортируется (android:exported="false") и неявно не экспортируется (не определено <intent-filter>), мы ожидали, что это будет нормально работать с несколькими установленными приложениями, причем каждое приложение получит свой собственный частный экземпляр службы, когда bindService называется.

Что на самом деле происходит, так это то, что ни одно приложение больше не работает, поскольку ни ServiceConnection.onServiceConnected, ни ServiceConnected.onServiceDisconnected никогда не вызываются, хотя вызовы context.startService и context.bindService возвращают успех.

После установки обоих приложений единственный способ заставить любое из них работать — удалить оба, а затем переустановить только одно. Недостаточно удалить либо независимо.


person David Berry    schedule 29.07.2015    source источник
comment
Это странно. Если вы когда-нибудь создадите пару проектов, которые воспроизводят эту проблему, и опубликуете исходный код для нее, дайте мне знать, так как мне было бы интересно попробовать это.   -  person CommonsWare    schedule 29.07.2015
comment
Это действительно странно. Вы не используете службы помощи, поэтому нет возможности привязаться к службе в другом приложении.   -  person jakubbialkowski    schedule 29.07.2015
comment
@jakubbialkowski AIDL имеет мало общего с привязкой к локальной/удаленной службе   -  person pskink    schedule 29.07.2015
comment
@pskink да, я знаю это. Я указал на это, потому что не думаю, что многие приложения с одним и тем же SDK могут вызвать такую ​​​​проблему без службы помощи.   -  person jakubbialkowski    schedule 29.07.2015


Ответы (1)


Оказывается, проблема была на самом деле в этом фрагменте кода, который препятствовал запуску службы в первую очередь:

if(!isServiceRunning()) {
    success = context.startService(new Intent(context, ChatSession.class)) != null;
}

У меня был списанный код для isServiceRunning, который неправильно определял, что служба запущена, основываясь только на имени:

public boolean isServiceRunning() {
    ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
    List<ActivityManager.RunningServiceInfo> services = manager.getRunningServices(Integer.MAX_VALUE);
    if(services == null) {
        return false;
    }
    for (ActivityManager.RunningServiceInfo service : services) {
        if (ChatSession.class.getName().equals(service.service.getClassName())) {
            return true;
        }
    }
    return false;
}

Замена if на:

        if (Process.myPid() == service.pid && ChatSession.class.getName().equals(service.service.getClassName())) {

кажется, решить проблему.

Я все еще не в восторге от этого, так как кажется, что должен быть лучший способ проверки.

person David Berry    schedule 29.07.2015
comment
на самом деле вы можете удалить isServiceRunning, вам это вообще не нужно: просто вызовите startService безоговорочно - person pskink; 30.07.2015
comment
Спасибо @pskink. Я не был уверен в этом. Собирался попробовать и посмотреть. - person David Berry; 30.07.2015
comment
startService документы говорят: ... Если эта служба еще не запущена, она будет создана и запущена (при необходимости для нее будет создан процесс); если он работает, то он продолжает работать. ... Обратите внимание, что вызовы startService() не являются вложенными: независимо от того, сколько раз вы вызываете startService(), один вызов stopService(Intent) остановит его. ... - person pskink; 30.07.2015
comment
Кроме того, вы можете использовать флаг BIND_AUTO_CREATE, когда bindService (поэтому startService не требуется), если только вы действительно не хотите, чтобы служба выжила и продолжала работать после того, как клиент отключится от службы. - person pskink; 30.07.2015
comment
В этом случае я на самом деле хочу, чтобы служба сохранялась даже после того, как все клиенты будут отключены, поэтому кажется, что мне нужно явно запускать (и останавливать) службу. - person David Berry; 30.07.2015
comment
Вы можете использовать флаг START_STICKY в службе, чтобы служба работала без клиентов. - person jakubbialkowski; 30.07.2015
comment
@jakubbialkowski, боюсь, это так не работает - person pskink; 30.07.2015