笔记: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组件的进程间通信请求。

阶段1

ActivityThread创建每个Activity对象时,为它指定一个ContextImpl对象,作为Activity父类ContextWrapper.mBase字段。
ContextImpl.startService()获得一个AMS的本地Binder代理:

ActivityManagerNative.getDefault();

返回类型为ActivityManagerProxy,代理对象的startService()向AMS发起START_SERVICE_TRANSACTION通信请求。

阶段2:AMS创建新进程

AMS首先保存RemoteService的组件信息。发现运行RemoteService的进程不存在,就创建一个新的应用程序进程。

阶段2

AMS继承ActivityManagerNative,其onTransact()收到命令START_SERVICE_TRANSACTION时回调AMS.startService()。
AMS使用ServiceRecord记录要启动的Service组件的信息,根据接收到的intent得到RemoteService所要运行的进程,进程信息由ProcessRecord表示。
当对应的应用程序进程不存在时,调用AMS.startProcessLocked()启动新进程。

阶段3:新进程通知AMS创建完毕

新应用程序启动完成后,向AMS发送一个启动完成的进程间通信请求,这样AMS就可以继续执行启动RemoteService的操作。

阶段3

阶段4:AMS通知新进程启动RemoteService

AMS向新进程执行进程间通信,通知它启动RemoteService,将第2阶段保存的RemoteService组件的信息传递给新应用程序进程。

阶段4

阶段5:新进程启动RemoteService

新进程响应AMS的Service启动事件,执行RRemoteService的启动。

阶段5

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编写)

posted @   everhad  阅读(1000)  评论(0编辑  收藏  举报
编辑推荐:
· 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训练数据并当服务器共享给他人
点击右上角即可分享
微信分享提示