4. Client 端绑定 Service:bindService 流程、ServiceConnection 回调、获取代理
Binder 通信的起点通常是 Client 通过 bindService() 与远端 Service 建立绑定关系。本章聚焦 Client 侧的绑定流程:从 Context.bindService() 调用,到系统服务调度,再到 ServiceConnection 回调触发,最终获取可跨进程调用的代理对象。理解这一串异步回调是掌握 Binder 通信的基础。
AndroidManifest.xml 中显式声明,且 Client 与 Service 的进程必须不同(或通过 android:process 隔离)。未声明将直接导致绑定失败,且 onServiceConnected 永远不会被调用。
4.1 bindService 调用的入口与参数
Client 端通过 Context.bindService(Intent service, ServiceConnection conn, int flags) 发起绑定。核心参数说明如下:
| 参数 | 类型 | 作用 |
|---|---|---|
| service | Intent | 显式或隐式指定目标 Service(推荐显式)。 |
| conn | ServiceConnection | 回调接口,接收绑定成功/断开事件。 |
| flags | int | 绑定标志,常用 BIND_AUTO_CREATE(Service 未启动则自动创建)。 |
典型调用代码:
Intent intent = new Intent(this, RemoteService.class);
bindService(intent, connection, Context.BIND_AUTO_CREATE);
4.2 ServiceConnection 回调的时机与生命周期
ServiceConnection 是一个包含两个回调的接口:
- onServiceConnected(ComponentName name, IBinder service) — 当系统成功获取到 Service 返回的 Binder 对象后,在主线程中回调。参数
IBinder service即为 Binder 代理(或本地 Binder 对象)。 - onServiceDisconnected(ComponentName name) — 当 Service 进程意外死亡或连接异常断开时回调。注意:手动 unbind 不会触发此回调。
这两个回调均由系统主线程调度,因此不能在其中执行耗时操作。如果需要在断开后自动重连,可在 onServiceDisconnected 中重新发起 bindService。
onServiceConnected 不会被调用,且不会抛出任何异常。必须检查 bindService() 的返回值(boolean),false 表示绑定未成功发起(如 Service 未定义、Intent 不匹配等)。
4.3 从 IBinder 获取代理对象
跨进程场景下,onServiceConnected 回调提供的 IBinder service 实际上是 Binder 代理(Proxy 端)。我们需要通过 AIDL 接口的 Stub.asInterface(IBinder) 将其转换为具体代理:
private IRemoteService remoteService;
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
remoteService = IRemoteService.Stub.asInterface(service);
// 现在可以通过 remoteService 调用远程方法
}
@Override
public void onServiceDisconnected(ComponentName name) {
remoteService = null; // 销毁引用
}
};
Stub.asInterface() 内部会判断 IBinder 是否处于同一进程:若同进程则返回 Stub 自身(本地 Binder),否则返回 Proxy 对象。这一判断对开发者透明,保证了跨进程和本地调用的代码一致性。
4.4 绑定过程完整时序与系统介入
从 Client 调用 bindService 到最终获得代理,系统服务(AMS)负责查找/创建 Service 实例、传递 Binder 对象,流程如下:
- Client 进程调用
bindService,IPC 到 System Server 的 ActivityManagerService。 - AMS 查找目标进程(若未启动则先创建),发送消息让目标进程创建 Service 实例。
- Service 的
onBind()被调用,返回一个 IBinder 对象。 - AMS 拿到该 IBinder 后,通过 Binder 驱动将其引用传递回 Client 进程。
- Client 进程的主线程 Handler 收到回调消息,触发
onServiceConnected。
4.5 绑定标志与生命周期注意事项
| 标志 | 说明 |
|---|---|
BIND_AUTO_CREATE | 若 Service 未运行,自动创建并启动;解绑时 Service 将停止(若没有其他绑定)。 |
BIND_DEBUG_UNBIND | 在调试时记录解绑情况,生产环境不建议使用。 |
BIND_NOT_FOREGROUND | 禁止将 Service 提升为前台进程,可能增加被杀概率。 |
性能/稳定性建议:
- Activity 的绑定与解绑应在
onStart/onStop或onResume/onPause中成对执行,避免内存泄漏。 - 如果 Service 可能被多个 Client 绑定,确保
onUnbind返回false(默认)并使用BIND_AUTO_CREATE,由系统管理生命周期。 - 在
onServiceDisconnected中释放所有对代理的引用,防止 Binder 死亡后产生DeadObjectException。
4.6 调试与常见问题排查
绑定失败的表现通常为 bindService 返回 false,或 onServiceConnected 从未触发。常用调试手段:
- 检查 Logcat:搜索
ActivityManager或ServiceManager标签,查看Unable to start service等错误。 - 确认 Service 的
exported属性:如果从一个应用绑定另一个应用的服务,必须设置android:exported="true"。 - 确认 intent 的
package和class完全匹配,使用隐式 intent 可能被安全策略拒绝。 - 使用
adb shell dumpsys activity services查看当前已绑定的服务列表,验证服务是否存活。
onServiceConnected 中打印 IBinder 的 getInterfaceDescriptor(),可验证接收到的代理是否属于正确的 AIDL 接口。示例:
Log.d("Binder", "Descriptor: " + service.getInterfaceDescriptor());