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.onServiceConnected
或 ServiceConnected.onServiceDisconnected
都没有被调用,尽管对 context.startService
和 context.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())) {
似乎可以解决问题。
我对此仍然不感兴趣,因为似乎应该有更好的检查方法。
我公司生产的 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.onServiceConnected
或 ServiceConnected.onServiceDisconnected
都没有被调用,尽管对 context.startService
和 context.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())) {
似乎可以解决问题。
我对此仍然不感兴趣,因为似乎应该有更好的检查方法。