bindService 从不调用 onServiceConnected

bindService never calls onServiceConnected

我公司生产的 SDK 作为 Android 库 aar 文件提供。作为该 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.onServiceConnectedServiceConnected.onServiceDisconnected 都没有被调用,尽管对 context.startServicecontext.bindService 的调用都 return成功。

安装完这两个应用程序后,让其中一个运行的唯一方法是卸载两个应用程序,然后只重新安装一个。单独卸载是不够的。

事实证明,问题实际上是这段代码首先阻止了服务启动:

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())) {

似乎可以解决问题。

我对此仍然不感兴趣,因为似乎应该有更好的检查方法。