笔记:Service的启动和绑定过程
NOTE 超简化版
本文内容是对老罗《Android源码情景分析》Service启动章节的笔记。
Service的创建特点
作为四大组件之一,Service的创建不是普通对象那样new出来的,而是用startService()、bindService()这样的方式启动。和Activity一样,它需要在AndroidManifest.xml中注册,由系统创建,而自定义的Service子类只需重写相关的回调方法即可。
Service可以运行在启动/绑定它的程序进程内,或另外的进程/程序内。
案例:启动其它进程中的Service
接下来分析启动将运行在另一个进程中的Service的过程,相比启动同进程中的Service更能完整
体现Service启动的完整过程。
需要启动的RemoteService接收的隐式意图为"com.hxw.bot.service.RemoteService",对它指定android:process
设置为"com.hxw.bot.server"这样它就运行在此进程名的新进程中。
可以让启动RemoteService的代码位于同程序或另一个程序中,只要保证startService()、bindService()的调用代码和RemoteService运行在不同进程即可。
假设启动RemoteService的代码位于同程序的ClientActivity组件中,简称Client。
Service组件的启动由ActivityManagerService管理,简称AMS,它是运行在系统进程System中的关键服务。现在Client、RemoteService和AMS都不在同进程中,所以接下来的启动过程涉及到三个进程之间的Binder通信。
阶段1:Client通知AMS启动Service
Client向AMS发送启动Service组件的进程间通信请求。
ActivityThread创建每个Activity对象时,为它指定一个ContextImpl对象,作为Activity父类ContextWrapper.mBase字段。
ContextImpl.startService()获得一个AMS的本地Binder代理:
ActivityManagerNative.getDefault();
返回类型为ActivityManagerProxy,代理对象的startService()向AMS发起START_SERVICE_TRANSACTION通信请求。
阶段2:AMS创建新进程
AMS首先保存RemoteService的组件信息。发现运行RemoteService的进程不存在,就创建一个新的应用程序进程。
AMS继承ActivityManagerNative,其onTransact()收到命令START_SERVICE_TRANSACTION时回调AMS.startService()。
AMS使用ServiceRecord记录要启动的Service组件的信息,根据接收到的intent得到RemoteService所要运行的进程,进程信息由ProcessRecord表示。
当对应的应用程序进程不存在时,调用AMS.startProcessLocked()启动新进程。
阶段3:新进程通知AMS创建完毕
新应用程序启动完成后,向AMS发送一个启动完成的进程间通信请求,这样AMS就可以继续执行启动RemoteService的操作。
阶段4:AMS通知新进程启动RemoteService
AMS向新进程执行进程间通信,通知它启动RemoteService,将第2阶段保存的RemoteService组件的信息传递给新应用程序进程。
阶段5:新进程启动RemoteService
新进程响应AMS的Service启动事件,执行RRemoteService的启动。
ApplicationThread是响应AMS的Binder对象。
ActivityThread中使用Hanler对象mH将操作封装为Message转到主线程去执行。
ActivityThread.handleCreateService()中根据Component信息得到RemoteService的类型,然后使用ClassLoader实例化RemoteService对象,最终执行其onCreate()方法。
案例:Client绑定MyService
Client表示ClientActivity组件,MyService是要绑定的Service组件。
如果Client和MyService不在一个进程,那么获得的是MyService.onBind()返回的Binder对象的代理对象。
如果在一个进程,Client得到的就是MyService.onBind()返回的Binder对象本身。
假设MyService和Client会运行在同一个应用程序进程,绑定过程如下:
绑定阶段1:Client通知AMS绑定
Client向AMS发送一个绑定MyService组件的进程间通信请求。
Client传递其ServiceConnection给AMS是使用Binder向其它进程注册接口回调的过程。
实际上就是Client端使用一个实现了IServiceConnection.aidl的Binder对象,然后传递给AMS对应的BinderProxy。
见LoadedApk.getServiceDispatcher(ServiceConnection c, Context context, Handler handler, ing flags)
绑定阶段2:AMS处理绑定请求
AMS发现运行MyService的组件的应用程序进程即Client组件所在进程,通知该进程启动MyService组件。
一个Service是可以被多个进程绑定的,ServiceRecord.bindings记录了绑定它的所有进程的信息,数据项为ProcessRecord。
得到Service要运行的进程信息,若进程不存在启动新应用程序进程。
通知应用程序进程启动MyService。
绑定阶段3:AMS通知MyService进程请求绑定Service
MyService启动后,AMS请求它返回其onBind()返回的Binder本地代理对象,以便Client使用此Binder代理和MyService进行通信。
AMS发出绑定请求:ApplicationThreadProxy.scheduleBindService(),请求是发送给MyService所在进程的。
ActivityThread对AMS.scheduleBindService()的处理在ActivityThread.handleBindService(BindServiceData data):
final HashMap<IBinder, Service> mServices
= new HashMap<IBinder, Service>();
private final void handleBindService(BindServiceData data) {
Service s = mServices.get(data.token);
if (s != null) {
try {
data.intent.setExtrasClassLoader(s.getClassLoader());
try {
if (!data.rebind) {
IBinder binder = s.onBind(data.intent);
ActivityManagerNative.getDefault().publishService(
data.token, data.intent, binder);
} else {
...
}
...
}
ActivityThread.mServices中保存所有当前进程创建了的Service组件。
参数data的字段token是AMS传递的ServiceRecord对象(它是AMS中对Service组件的描述)的Binder代理。
mServices的key就是此token,见ActivityThread.handleCreateService(CreateServiceData data)方法。所以MyService进程中像下面这样获得它已启动的Service组件对象: Service s = mServices.get(data.token);
紧接着调用Service.onBind()返回Service管理的Binder对象。
然后通知AMS:publishService()。
绑定阶段4:AMS将获得的Binder代理发送给Client组件
AMS响应publishService,获得MyService返回的Binder代理,跨进程执行connected()通信。传递给Client的正是MyService关联的Binder对象的代理。
绑定阶段5:Client响应connected
Client进程,LoadedApk.InnerConnection.connected()响应AMS,接收AMS发送给它的Binder代理对象后,将它转换为通信接口类型的实例,像普通接口对象那样使用它。
注意Binder通信时IBinder对的传递是通过Parcel.writeStrongBinder()/readStrongBinder(),跨进程通信写入的是Binder代理,而读取时如果发现是同进程内的Binder对象的代理,那么读取到的就是代理对应的Binder对象本身。
这就是为什么Service.onBind()返回的Binder对象,同进程的ServiceConnection.onServiceConnected()返回的是同一个Binder对象,而不同进程返回的是Binder代理对象。
无论那种情况,bindService获得MyService关联的IBinder的过程总是要通过AMS拿到。
补充
- 服务端的Binder的代理BinderProxy用来供其它进程访问此Binder对象,其BinderProxy作为其它
进程引用它的token。
(本文使用Atom编写)
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人