第3章 Service端实现与注册:onBind返回IBinder、清单注册、多线程处理
3.1 本章核心作用
Service端是Binder通信的“被调用方”,负责定义接口、实现业务逻辑并通过onBind()返回IBinder对象供客户端跨进程访问。本章讲解Service端三大关键环节:IBinder生成、清单注册和多线程调度。
要点速览
- onBind()返回的IBinder对象决定了客户端能调用的方法集。
- 清单注册使系统能够按Intent启动Service。
- Binder主线程池处理IPC请求,但耗时任务必须手动切至工作线程。
- onBind()返回的IBinder对象决定了客户端能调用的方法集。
- 清单注册使系统能够按Intent启动Service。
- Binder主线程池处理IPC请求,但耗时任务必须手动切至工作线程。
3.2 原理:Binder Service端的角色
Service端本质是一个运行在独立进程(或同一进程)的Android Service组件。它与客户端的交互模型如下:
- IBinder接口:通过AIDL或直接继承Binder类定义跨进程方法。
- onBind()生命周期:系统在客户端bindService()时调用,返回值是后续IPC的句柄。
- 多线程模型:Binder驱动内部维护一个线程池(默认最多16个),客户端请求可能并发到达,Service端需要处理好同步。
3.3 实现流程
- 定义AIDL接口:编写 .aidl 文件声明客户端可调用的方法(若用Binder原生接口则跳过)。
- 实现Service类:继承Service,创建一个内部Stub实现类,在onBind()中返回其对象。
- 清单注册:在AndroidManifest.xml的<application>内添加<service>元素,指定name和exported属性。
- 处理多线程:在Service的onCreate()中初始化线程池或HandlerThread,确保耗时操作不阻塞Binder线程。
- 构建与部署:编译APK,安装到设备/模拟器。
3.4 核心代码示例
3.4.1 AIDL接口定义(IMyService.aidl)
// IMyService.aidl
package com.example.myservice;
interface IMyService {
int add(int a, int b);
String getData();
}
3.4.2 Service实现类
// MyService.java
package com.example.myservice;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class MyService extends Service {
private HandlerThread mHandlerThread;
private Handler mWorkHandler;
private ExecutorService mExecutor = Executors.newFixedThreadPool(4);
// 通过AIDL生成的Stub类
private final IMyService.Stub mBinder = new IMyService.Stub() {
@Override
public int add(int a, int b) {
// 耗时加法(模拟),直接运行在Binder线程
return a + b;
}
@Override
public String getData() {
// 模拟耗时操作,需要切到工作线程
// 实际应使用回调或Future,简化代码直接让Binder线程等待
// 此处仅演示:使用后台线程并返回结果(需阻塞等待,实际不推荐)
// 更好的做法:在Service内缓存结果,或使用FutureTask
// 示例中返回简单字符串
return "Data from service";
}
};
@Override
public void onCreate() {
super.onCreate();
// 创建独立工作线程
mHandlerThread = new HandlerThread("ServiceWorkThread");
mHandlerThread.start();
mWorkHandler = new Handler(mHandlerThread.getLooper());
}
@Override
public IBinder onBind(Intent intent) {
// 返回Stub实例
return mBinder;
}
@Override
public void onDestroy() {
super.onDestroy();
mHandlerThread.quitSafely();
mExecutor.shutdown();
}
}
3.4.3 清单注册
<application ...>
<service
android:name=".MyService"
android:exported="true"
android:process=":remote">
<intent-filter>
<action android:name="com.example.myservice.BIND" />
</intent-filter>
</service>
</application>
关键属性说明:
-
-
-
-
android:process=":remote":让Service运行在独立进程,真正实现跨进程通信。-
android:exported="true":允许其他进程的客户端绑定。-
<intent-filter>:客户端通过该Action隐式绑定Service。
3.5 调试与验证
- 检查Service是否运行:
adb shell dumpsys activity services | grep MyService - 查看Binder线程池:
(可看到binder线程)adb shell cat /proc/<pid>/tasks - 日志监控:在Service中打印Log,观察onBind()和业务方法的调用归属线程(Log.d(TAG, Thread.currentThread().getName()))。
3.6 常见错误与解决方案
| 错误现象 | 原因 | 解决方法 |
|---|---|---|
| 客户端bindService返回false | 清单未注册Service,或Intent无法匹配 | 检查<service>和intent-filter配置 |
| onBind()被多次调用但返回null | 未正确返回Binder对象 | 确保onBind()返回有效的IBinder实例 |
| 远程调用抛出TransactionTooLargeException | 传输数据超过1MB | 拆分数据或使用FileDescriptor等 |
| ANR(应用无响应) | Binder线程执行耗时操作 | 将耗时任务移入工作线程,Binder线程只做调度 |
3.7 性能与稳定性优化
- 线程池配额:Binder默认线程池上限为16,不要在此池内执行任何阻塞操作(文件读写、网络请求)。
- 异步回调:若客户端需要结果,应采用
oneway接口定义非阻塞调用,或者使用回调接口RemoteCallbackList。 - 避免频繁创建Binder对象:每个客户端绑定只调用一次onBind(),复用同一个Stub对象可减少内存开销。
- 控制导出的Service:非必要不设置
android:exported="true",防止恶意第三方绑定。 - 使用线程安全的容器:Service端接收并发请求时,对共享数据使用
synchronized或CopyOnWriteArrayList等。
重要提醒:Service端如果使用HandlerThread或ExecutorService,需在Service销毁时正确清理,否则会导致线程泄漏。建议在onDestroy()中调用quitSafely()和shutdown()。
3.8 本章小结
Service端作为Binder通信的提供方,核心工作可以概括为“三个一”:
1. 一个onBind():返回IBinder对象(通常是AIDL生成的Stub)。
2. 一个清单条目:在AndroidManifest.xml中注册Service,指明进程和导出性。
3. 一个线程策略:Binder线程池只做调度,实际逻辑切至开发者管理的线程池或Handler中执行。